Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

$ character in az uri string being interpreted by powershell as a command #20686

Closed
5 tasks done
abelal83 opened this issue Nov 15, 2023 · 6 comments
Closed
5 tasks done

Comments

@abelal83
Copy link

Prerequisites

Steps to reproduce

When assigning output of an AZ cli rest command where URI contains a $ symbol powershell attempts to parse the value of $, even though the string is in quoted with single quotes.

Expected behavior

$t = az rest --uri 'https://management.azure.com/subscriptions/d904052e-5309-4000-9a3f-XXXXXXXXX/resourceGroups/plt-XXXXX-dev-rg-01/providers/Microsoft.Authorization/roleManagementPolicies?api-version=2020-10-01&$filter=asTarget()'

Actual behavior

$t = az rest --uri 'https://management.azure.com/subscriptions/d904052e-5309-4000-9a3f-XXXXXXXXX/resourceGroups/plt-XXXXX-dev-rg-01/providers/Microsoft.Authorization/roleManagementPolicies?api-version=2020-10-01&$filter=asTarget()'
'$filter' is not recognized as an internal or external command,
operable program or batch file.

Error details

'$filter' is not recognized as an internal or external command,
operable program or batch file.

Environment data

Name                           Value
----                           -----
PSVersion                      7.3.9
PSEdition                      Core
GitCommitId                    7.3.9
OS                             Microsoft Windows 10.0.19045
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Visuals

When I run just the below in powershell it works fine, but obviously I'm not assigning return value to anything.

az rest --uri 'https://management.azure.com/subscriptions/d904052e-5309-4000-9a3f-XXXXXXXXX/resourceGroups/plt-XXXXX-dev-rg-01/providers/Microsoft.Authorization/roleManagementPolicies?api-version=2020-10-01&$filter=asTarget()'

@abelal83 abelal83 added the Needs-Triage The issue is new and needs to be triaged by a work group. label Nov 15, 2023
@jborean93
Copy link
Collaborator

I don't see this behaviour. Even when calling an exe that just outputs the args provided to it you can see the $ is treated literally

image

How exactly are you calling it in PowerShell?

@abelal83
Copy link
Author

abelal83 commented Nov 15, 2023

Hi, thanks so much for the super quick response.

I'm calling it as below:

image

(red marked boxes are to mask senstive info)

When I don't include $filter it works as expected (I've included | ConvertFrom-Json to keep output small)

image

It's very strange, I was expecting any special chars in '' to be ignored.

@mklement0
Copy link
Contributor

mklement0 commented Nov 15, 2023

The problem is that the az CLI is implemented as a batch file (az.cmd) and that cmd.exe inappropriately parses a batch file's arguments as if they had been submitted from inside a cmd.exe session.

When PowerShell rebuilds the process command line behind the scenes it decides which arguments to double-quote strictly based on whether a given argument contains spaces.

Thus, any space-less argument that happens to contain cmd.exe metacharacters such as & breaks batch-file invocations; a simple repro is:

# -> 'b' is not recognized as an internal or external command, operable program or batch file.
cmd /c echo 'a&b'

cmd.exe is one of the programs that exhibits the old, broken argument-passing behavior in the default mode of the $PSNativeCommandArgumentPassing preference variable, Windows, so the workaround is to use embedded double-quoting:

# -> OK
cmd /c echo '"a&b"'

Therefore:

$t = az rest --uri '"https://management.azure.com/subscriptions/d904052e-5309-4000-9a3f-XXXXXXXXX/resourceGroups/plt-XXXXX-dev-rg-01/providers/Microsoft.Authorization/roleManagementPolicies?api-version=2020-10-01&$filter=asTarget()"'

See also:

@abelal83
Copy link
Author

@mklement0 thanks very much for your detailed explanation. What confused me was why running command without "$x = ..." worked fine but makes perfect sense with your explanation.

I can confirm your suggestion works perfectly. Thank you.

@microsoft-github-policy-service microsoft-github-policy-service bot removed the Needs-Triage The issue is new and needs to be triaged by a work group. label Nov 15, 2023
Copy link
Contributor

microsoft-github-policy-service bot commented Nov 15, 2023

📣 Hey @abelal83, how did we do? We would love to hear your feedback with the link below! 🗣️

🔗 https://forms.office.com/r/P926k48jRJ

@mklement0
Copy link
Contributor

Glad to hear it helped, @abelal83.

I should mention that the '"..."' workaround comes with a caveat:

Should the Azure CLI ever move away from a batch-file entry point, the workaround will break.

A future-proof alternative is to call via cmd /c (using --% is also an option, but comes with many limitations):

$t = cmd /c 'az rest --uri "https://management.azure.com/subscriptions/d904052e-5309-4000-9a3f-XXXXXXXXX/resourceGroups/plt-XXXXX-dev-rg-01/providers/Microsoft.Authorization/roleManagementPolicies?api-version=2020-10-01&$filter=asTarget()"'

Small caveat: If you explicitly set $PSNativeCommandArgumentPassing = 'Standard', the cmd /c workaround breaks too (only the --% approach is truly independent of $PSNativeCommandArgumentPassing).

This highlights the general problems with Windows mode (which #15143 was meant to address by obviating the need for this mode altogether):

  • You generally need to be aware of what executables it applies to, and there's the risk of the list of this list of executables growing piecemeal over time.

  • The workarounds using embedded quoting / manual \-escaping of truly embedded " chars. rely on historical, broken behavior that never made any sense and may confuse newcomers.

  • For a given third-party CLI - such as in the case at hand - you may not even know what type of executable it is, nor should you have to.

  • If a given third-party CLI switches from a batch file to a different executable type in a future version, said workarounds will break.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants