5 minute read

Azure CLI is a good alternative to using Azure PowerShell. It is idempotent, and is a great fit for Continuos Integration scenarios. But in the process of making Azure Cli compatible with bash pwsh cmd etc, az does not fit PowerShell when automated. Most problems can be solved with some helper functions, and this post is here to show you how.

In this article I will show you my approach to a helper method to ensure and monitor Azure Cli results, in Power Shell.

Skip to the end if you just want some code to copy. 🖨

This article was written with az cli v2.13.0 and PowerShell 5.0+ in mind.

Non-working example

Lets start off with a non-working example to show what is the problem with az commands in PowerShell

# Configure Error Action Preference Stop to stop on any error
$ErrorActionPreference = 'Stop'

# Create and setup app-service
az webapp create --resource-group $resourceGroup --name $appName

az webapp config set `
    --resource-group $resourceGroup `
    --name $appName `
    --always-on $true `
    --ftps-state FtpsOnly

Why does this not work?

If the first az webapp create fails, execution will continue. The az webapp config will also fail as a result. But the script will happily continue. If this was on your CI pipeline no error would be reported, and the CI build would be Green.

How can I avoid continuing on errors?

Check $LASTEXITCODE after every az command. If az has an error, it will print the error to console and exit with a non-zero ExitCode.

az webapp create --resource-group $resourceGroup --name $appName
if ($LASTEXITCODE -ne 0) {
  Write-Error "az failed with exit code $LASTEXITCODE" -ErrorAction 'Stop'
}

This seems like a lot of code to write on every az command?

Helper methods to the rescue. We can create a simple “Test” to ensure the exit code is non-zero.

Function Test-LastExitCode {
  if($LastExitCode -ne 0 ) {
    Write-Error "Operation failed with exit code $LastExitCode" -ErrorAction 'Stop'
  }
}
# We now have a single line error handling
az webapp create --resource-group $resourceGroup --name $appName
Test-LastExitCode

Using az output in PowerShell

az by default returns a JSON result if the command is successful. This can be converted to PowerShell objects for easy consumption. If it errors, the result is always empty or null.

We pipe the result into ConvertFrom-Json to parse this into a powershell native format.

$appInfo = az webapp create --resource-group $resourceGroup --name $appName | ConvertFrom-Json
Test-LastExitCode

# Use the returned data as a Powershell Object, nice!
Write-Output $appInfo.defaultHostName

Combining Error Handling and Output

What if we use the powershell pipe to both parse the exit code and the Json result?

The full code of the PowerShell az cli converter I use is found below. Include this in a module, or at the top of your file for reliable az scripting. 🚀

Function ConvertFrom-AzureCli {
  [CmdletBinding()]
  param (
    [Parameter(ValueFromPipeline)] [string] $line
  )
  begin {
    # Collect all lines in the input
    $lines = @()
  }

  process {
    # 'process' is run once for each line in the input pipeline.
    $lines += $line
  }

  end {
    # Azure Cli errors and warnings change output colors permanently.
    # Reset the shell colors after each operation to keep consistent.
    [Console]::ResetColor()

    # If the 'az' process exited with a non-zero exit code we have an error.
    # The 'az' error message is already printed to console, and is not a part of the input.
    if ($LASTEXITCODE) {
        Write-Error "az exited with exit code $LASTEXITCODE" -ErrorAction 'Stop'
    }

    $inputJson = $([string]::Join("`n", $lines));
    # We expect a Json result from az cli if we have no error. The json result CAN be $null.
    $result = ConvertFrom-Json $inputJson
    return $result
  }
}

Best-practice usage:

# I recommend using StrictMode to catch issues such as missing fields and unused variables early
Set-StrictMode -Version 'latest'
$ErrorActionPreference = 'Stop'

# Ensure app-service exists
$appInfo = (az webapp create `
  --resource-group $resourceGroup `
  --name $appName `
  | ConvertFrom-AzureCli
  )

# Ensure app-service configuration
az webapp config set `
    --ids $appInfo.id `
    --always-on $true `
    --ftps-state FtpsOnly `
 | ConvertFrom-AzureCli

Write-Output "Success!"

That’s all! I hope this could help setup a better PowerShell and Azure CLI pipeline.


Code Snippets are licensed under MIT No Attribution