Considerations for running the Azure CLI in a PowerShell environment

Azure CLI is a tool to manage Azure resources through Azure CLI reference commands that run in both a Bash and PowerShell environment. However, there are slight syntax differences in parameter formatting between environments that can result in unexpected results. The purpose of this article is to help you resolve Azure CLI syntax errors when working in a PowerShell environment.

This article compares syntax differences of Azure CLI commands executed in the following environments:

  • Bash running in a Linux operating system using Azure Cloud Shell.
  • PowerShell running in a Linux operating system using Azure Cloud Shell.
  • Windows PowerShell running in Windows 11 using the PowerShell 5 terminal.
  • PowerShell running in a Windows 11 using the PowerShell 7 terminal.

If you're new to CLI, differentiating between a tool and an environment might be confusing. How-to choose the right command-line tool provides a good comparison.

Prerequisites

This article is intended for you to read and learn. However, if you want to run the examples, select the Prepare your environments tab to install the environments used in this article.

Important

When you have an Azure CLI script that is producing an error, consider how the environment you are working in is parsing the Azure CLI command syntax.

Pass spaces in Azure CLI parameters

In Azure CLI, when you need to pass a parameter value containing a space, there are quoting differences between operating systems and environments. In this example, use az storage account list and rename output columns with a word containing a space.

In this example, notice the single quote ('...') wrapper with embedded double quotes ("..."). This example also works in PowerShell in Linux.

az storage account list --query '[].{"SA Name":name, "Primary endpoint":primaryEndpoints.blob}' --output table

If you want to add a filter, the syntax changes. Notice how this example wraps the --query parameter value in double quotes ("...") and uses a backslash (\) escape character. This script doesn't run in PowerShell.

 az storage account list --query "[?creationTime >='2024-02-01'].{\"SA Name\":name,\"Primary endpoint\":primaryEndpoints.blob}" --output table

If you just tried to run the filter syntax in a PowerShell environment, you received error message argument --query: invalid jmespath_type value: "[?creationTime >=...". However, in Bash within a Linux environment, your output is similar to this:

SA Name           Primary Endpoint
-----------       -----------------
msdocssa00000000  https://msdocssa000000000.blob.core.windows.net/

Pass parameters in a URL containing a query string

Question marks in URLs indicate the end of the URL and the beginning of a query string. Here's an example that opens step 3 in Learn to use the Azure CLI:

https://learn.microsoft.com/en-us/cli/azure/account?view=azure-cli-2020-09-01-hybrid.

The ?view=azure-cli-2020-09-01-hybrid results in the desired version of the Azure CLI reference content.

When you execute Azure CLI commands in a PowerShell environment, PowerShell allows question marks to be part of a variable name. This might create confusion in Azure CLI parameter values.

Here's an example from the Use the Azure REST API article:

Notice how $containerRegistryName?api-version concatenates together without error in Bash.

# Script for a Bash environment

# Variable block
let "randomIdentifier=$RANDOM*$RANDOM"
subscriptionId="00000000-0000-0000-0000-000000000000"
resourceGroup="msdocs-app-rg$randomIdentifier"
containerRegistryName="msdocscr$randomIdentifier"

# prior to this GET example, the resource group and container registry were created in the article.

az rest --method get --url https://management.azure.com/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.ContainerRegistry/registries/$containerRegistryName?api-version=2023-01-01-preview

Pass parameters containing a PowerShell special character

There are special characters of PowerShell, such as the At (@) symbol. To run Azure CLI in PowerShell, add a backtick ` before the special character to escape it. You can also enclose the value in single (') or double (") quotes.

The following three examples will work in PowerShell:

  • parameterName `@parameters.json
  • parameterName '@parameters.json'
  • parameterName "@parameters.json"

This example will not work in PowerShell:

  • parameterName @parameters.json

Pass parameters containing JSON

For complex arguments like a JSON string, the best practice is to use Azure CLI's @<file> convention to load from a file to bypass the shell's interpretation. Note that the At (@) symbol is a splatting operator in PowerShell, so it should be quoted.

There are good examples in az ad app create that contain both JSON file content and command examples. Here's a code snippet:

# Script for a Bash environment

az ad app create --display-name myTestAppName \
    --is-fallback-public-client \
    --required-resource-accesses @manifest.json

Pass parameters containing key:value pairs

Some Azure CLI parameter values, such as Azure resource tags, require key:value pairs. If your key or value contains a space or special character, the Bash and PowerShell syntax aren't always the same.

See Create tags to practice quoting differences in the Learn to use the Azure CLI tutorial. This tutorial step gives examples for Bash, PowerShell, and Cmd for the following key:value pair scenarios:

  • spaces
  • empty values
  • special characters
  • variables

Error handling for Azure CLI in PowerShell

You can run Azure CLI commands in PowerShell, as described in Choose the right Azure command-line tool. If you do, be sure you understand Azure CLI error handling in PowerShell. In particular, Azure CLI doesn't create exceptions for PowerShell to catch.

An alternative is to use the $? automatic variable. This variable contains the status of the most recent command. If the previous command fails, $? has the value of $False. For more information, see about_Automatic_Variables.

The following example shows how this automatic variable can work for error handling:

# Script for a PowerShell environment

az group create --name MyResourceGroup
if ($? -eq $false) {
    Write-Error "Error creating resource group."
}

The az command fails because it's missing the required --location parameter. The conditional statement finds that $? is false and writes an error.

If you want to use the try and catch keywords, you can use throw to create an exception for the try block to catch:

# Script for a PowerShell environment

$ErrorActionPreference = "Stop"
try {
    az group create --name MyResourceGroup
    if ($? -eq $false) {
        throw 'Group create failed.'
    }
}
catch {
    Write-Error "Error creating the resource group."
}
$ErrorActionPreference = "Continue"

By default, PowerShell catches only terminating errors. This example sets the $ErrorActionPreference global variable to Stop so PowerShell can handle the error.

The conditional statement tests the $? variable to see if the previous command failed. If so, the throw keyword creates an exception to catch. The catch block can be used to write an error message or handle the error.

The example restores $ErrorActionPreference to its default value.

For more information about PowerShell error handling, see Everything you wanted to know about exceptions.

Enable Tab Completion in PowerShell

Tab completion, also known as "Azure CLI completers", provides completion on inputs to provide hints, enable discovery and speed up input entry. Command names, command group names, parameters and certain parameter values can be automatically inserted into the command line by pressing the Tab key.

Tab completion is enabled by default in Azure Cloud Shell and in most Linux distributions. Starting in Azure CLI version 2.49, you can enable tab completion for the Azure CLI in PowerShell. Follow these steps:

  1. Create or edit the profile stored in the variable $PROFILE. The simplest way is to run notepad $PROFILE in PowerShell. For more information, see How to create your profile and Profiles and execution policy.

  2. Add the following code to your PowerShell profile:

    Register-ArgumentCompleter -Native -CommandName az -ScriptBlock {
        param($commandName, $wordToComplete, $cursorPosition)
        $completion_file = New-TemporaryFile
        $env:ARGCOMPLETE_USE_TEMPFILES = 1
        $env:_ARGCOMPLETE_STDOUT_FILENAME = $completion_file
        $env:COMP_LINE = $wordToComplete
        $env:COMP_POINT = $cursorPosition
        $env:_ARGCOMPLETE = 1
        $env:_ARGCOMPLETE_SUPPRESS_SPACE = 0
        $env:_ARGCOMPLETE_IFS = "`n"
        $env:_ARGCOMPLETE_SHELL = 'powershell'
        az 2>&1 | Out-Null
        Get-Content $completion_file | Sort-Object | ForEach-Object {
            [System.Management.Automation.CompletionResult]::new($_, $_, "ParameterValue", $_)
        }
        Remove-Item $completion_file, Env:\_ARGCOMPLETE_STDOUT_FILENAME, Env:\ARGCOMPLETE_USE_TEMPFILES, Env:\COMP_LINE, Env:\COMP_POINT, Env:\_ARGCOMPLETE, Env:\_ARGCOMPLETE_SUPPRESS_SPACE, Env:\_ARGCOMPLETE_IFS, Env:\_ARGCOMPLETE_SHELL
    }
    
  3. To display all available options in the menu, add Set-PSReadlineKeyHandler -Key Tab -Function MenuComplete to your PowerShell profile.

See also