These issues are being tracked at #15529.
On Windows, there is a known issue of PowerShell when calling native .exe
executables or .bat
, .cmd
Command Prompt scripts: PowerShell/PowerShell#1995.
In short, Windows native command invoked from PowerShell will be parsed by Command Prompt again. This behavior contradicts the doc About Quoting Rules.
As az
is a Command Prompt script (at C:\Program Files (x86)\Microsoft SDKs\Azure\CLI2\wbin\az.cmd
), it will have issues when invoked from PowerShell. These issues don't happen when invoking a PowerShell cmdlet:
# Double quotes are preserved
> Write-Host '"some quoted text"'
"some quoted text"
# Double quotes are lost
> python.exe -c "import sys; print(sys.argv[1])" '"some quoted text"'
some quoted text
In order for a symbol to be received by Azure CLI, you will have to take both PowerShell and Command Prompt's parsing into consideration. If a symbol still exists after 2 rounds of parsing, Azure CLI will receive it.
To prevent this, you may use stop-parsing symbol --%
between az
and arguments.
The stop-parsing symbol (--%), introduced in PowerShell 3.0, directs PowerShell to refrain from interpreting input as PowerShell commands or expressions. When it encounters a stop-parsing symbol, PowerShell treats the remaining characters in the line as a literal.
For instance,
az --% vm create --name xxx
But keep in mind that the command still needs to be escaped following the Command Prompt syntax.
# Use --% to stop PowerShell from parsing the argument and escape double quotes
# following the Command Prompt syntax
> python.exe --% -c "import sys; print(sys.argv[1])" "\"some quoted text\""
"some quoted text"
This causes the argument to be parsed again by Command Prompt and breaks the argument. This typically happens when passing a URL with query string to az
:
> az 'https://graph.microsoft.com/v1.0/me/events?$orderby=createdDateTime&$skip=20' --debug
az: 'https://graph.microsoft.com/v1.0/me/events?$orderby=createdDateTime' is not in the 'az' command group.
'$skip' is not recognized as an internal or external command,
operable program or batch file.
In general, when running az "a&b"
in PowerShell,
- Since there is no whitespace in the argument, PowerShell will strip the quotes and pass the argument to Command Prompt
- The ampersand
&
is parsed again by Command Prompt as a command separator b
is treated as a separate command by Command Prompt, instead of part of the argument
> az "a&b" --debug
az: 'a' is not in the 'az' command group.
'b' is not recognized as an internal or external command,
operable program or batch file.
This is what cmd.exe
or Windows system sees:
>az a&b --debug
az: 'a' is not in the 'az' command group.
'b' is not recognized as an internal or external command,
operable program or batch file.
To solve it:
# When quoted by single quotes ('), double quotes (") are preserved by PowerShell and sent
# to Command Prompt, so that ampersand (&) is treated as a literal character
> az '"a&b"' --debug
Command arguments: ['a&b', '--debug']
# Escape double quotes (") with backticks (`) as required by PowerShell
> az "`"a&b`"" --debug
Command arguments: ['a&b', '--debug']
# Escape double quotes (") by repeating them
> az """a&b""" --debug
Command arguments: ['a&b', '--debug']
# With a whitespace in the argument, double quotes (") are preserved by PowerShell and
# sent to Command Prompt
> az "a&b " --debug
Command arguments: ['a&b ', '--debug']
# Use --% to stop PowerShell from parsing the argument
> az --% "a&b" --debug
Command arguments: ['a&b', '--debug']
This issue is tracked at PowerShell/PowerShell#1995 (comment)
This typically happens when passing a JSON to az
. This is because double quotes within the JSON string are lost when calling a native .exe
file within PowerShell.
# Wrong! Note that the double quotes (") are lost
> python.exe -c "import sys; print(sys.argv)" '{"key": "value"}'
['-c', '{key: value}']
This is what cmd.exe
or Windows system sees:
>python.exe -c "import sys; print(sys.argv)" "{"key": "value"}"
['-c', '{key: value}']
To solve it:
# Escape double quotes (") with backward-slashes (\) as required by Command Prompt,
# then quote the string with single quotes (') as required by PowerShell
> python.exe -c "import sys; print(sys.argv)" '{\"key\": \"value\"}'
['-c', '{"key": "value"}']
# First escape double quotes with backticks (`) as required by PowerShell,
# then escape double quotes with backward-slash (\) as required by Command Prompt,
# then quote the string with double quotes (") as required by PowerShell
> python.exe -c "import sys; print(sys.argv)" "{\`"key\`": \`"value\`"}"
['-c', '{"key": "value"}']
# First escape double quotes by repeating it as required by PowerShell,
# then escape double quotes with backward-slash (\) as required by Command Prompt,
# then quote the string with double quotes (") as required by PowerShell
> python.exe -c "import sys; print(sys.argv)" "{\""key\"": \""value\""}"
['-c', '{"key": "value"}']
# Stop PowerShell parsing
> python.exe --% -c "import sys; print(sys.argv)" "{\"key\": \"value\"}"
['-c', '{"key": "value"}']
The same applies to az
:
# Wrong!
> az '{"key": "value"}' --debug
Command arguments: ['{key: value}', '--debug']
# Correct
> az '{\"key\": \"value\"}' --debug
Command arguments: ['{"key": "value"}', '--debug']
> az "{\`"key\`": \`"value\`"}" --debug
Command arguments: ['{"key": "value"}', '--debug']
> az "{\""key\"": \""value\""}" --debug
Command arguments: ['{"key": "value"}', '--debug']
> az --% "{\"key\": \"value\"}" --debug
Command arguments: ['{"key": "value"}', '--debug']
For complex arguments like 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 At symbol (@
) is splatting operator in PowerShell, so it should be quoted.
az ad app create ... --required-resource-accesses "@manifest.json"
You may also use @-
to read from stdin
:
Get-Content -Path manifest.json | az ad app create ... --required-resource-accesses "@-"