Azure / azure-cli

Azure Command-Line Interface
MIT License
3.95k stars 2.93k forks source link

AZ CLI 2.40 on Windows Powershell fails to run '&' invocation operator with parameters #23890

Closed katriendg closed 6 months ago

katriendg commented 1 year ago

az feedback auto-generates most of the information requested below, as of CLI version 2.0.62

Related command

Any AZ command with arguments using an invocation operator.

Describe the bug In PowerShell on Windows, with CLI 2.40, using PS 5.2, 7.2.6 or 7.3.preview7, all versions fail to run script blocks with the invocation (call) operator '&' and passing an arguments array. It looks like parsing is not done correctly and could be linked to this article https://github.com/Azure/azure-cli/blob/dev/doc/quoting-issues-with-powershell.md

To Reproduce Try out a block like this in your PowerShell terminal, the result is Azure CLI returning an error that the 'account show' is misspelled or cmdlet not found.

$arglist = @("account", "show")
& "az" $arglist

Expected behavior The expected behavior is that 'az account show' should be executed and the result of the JSON shown on screen.

Environment summary OS: Windows 11 insider build PowerShell 7.2.6 AZ CLI: 2.40

I also tested this on PowerShell in CloudShell and there it runs as expected. Als on PowerShell on Linux WSL2 it works fine, both using Azure CLI 2.40. It seems the issue is only arising on Windows.

yonzhan commented 1 year ago

@jiasli for awareness

tj-spyro commented 1 year ago

I think I have the same issue and it is affecting our ability to use the call operator and mock az using pester for testing, for example:

Describe "mocking az" {
  It "mock az" {
    function GetAzVersion {
      & az version
    }
    Mock az { Write-Warning "$args"; Write-Output "az successfully mocked" }
    GetAzVersion | Should -Be "az successfully mocked"
    Should -Invoke az -Exactly -Times 1 -ParameterFilter { $args[0] -eq 'version' }
  }
}

The expected behaviour is for the tests to pass as the az command was not invoked but the mock was. The actual behaviour is that the az command is invoked with the following output:

Starting discovery in 1 files.
Discovery found 1 tests in 74ms.
Running tests.
[-] mocking az.mock az 1.9s (1.87s|25ms)
 Expected 'az successfully mocked', but got @('{', '  "azure-cli": "2.40.0",', '  "azure-cli-core": "2.40.0",', '  "azure-cli-telemetry": "1.0.8",', '  "extensions": {', '    "azure-devops": "0.25.0",', '    "desktopvirtualization": "0.2.0",', '    "interactive": "0.4.5",', '    "sentinel": "0.1.1"', '  }', ...1 more).
 at GetAzVersion | Should -Be "az successfully mocked", C:\Temp\azpester\mocking-az.tests.ps1:9
 at <ScriptBlock>, C:\Temp\azpester\mocking-az.tests.ps1:9
Tests completed in 2.18s
Tests Passed: 0, Failed: 1, Skipped: 0 NotRun: 0
cveld commented 1 year ago

In my own wording:

In az 2.39 the following was still possible:

az @("extension", "list")

In az 2.40 this breaks: 'extension list' is misspelled or not recognized by the system.

With Az cli 2.39 Get-Command az returns the following:

CommandType: Application
Name: az.cmd                                               
Version: 0.0.0.0
Source: C:\Program Files (x86)\Microsoft SDKs\Azure\CLI2\wbin\az.cmd

With Az cli 2.40 Get-Command az returns the following:

CommandType: ExternalScript  
Name: az.ps1                                                                                      
Version: <blank>
Source: C:\Program Files (x86)\Microsoft SDKs\Azure\CLI2\wbin\az.ps1

Workaround found, with az 2.40:

pwsh -command az @("extension", "list")

works.

Permanent solution

Inside az.ps1 we have changed $args to @args which seems a simple fix.

Jawz84 commented 1 year ago

This can be solved by adapting the build_scripts/windows/scripts/az.ps1 script:

$env:AZ_INSTALLER="MSI"
& "$PSScriptRoot\..\python.exe" -IBm azure.cli $args

# SIG # Begin signature block
# <generated signature omitted>

should become

$env:AZ_INSTALLER="MSI"
& "$PSScriptRoot\..\python.exe" -IBm azure.cli @args  # notice the @ instead of the $ here with @args

# SIG # Begin signature block
# <generated signature omitted>

Maintainers should re-generate the signature block ofcourse. So I won't be offering a pull request at this time.

This can be used as a local work-around too. Just change $args to @args in your az.ps1. (Find where it lives by using get-command az)

Thanks @cveld for drawing my attention to this issue :-)

mjhilton commented 1 year ago

We are encountering the same/very similar issue in a script that is bundled with our project, and customers are now unable to login if using 2.40 on Windows. A simplified version of our login script looks like this:

$loginArgs = @();
$loginArgs += @("--username=""exampleusername""");
$loginArgs += @("--password=""examplepassword""");
$loginArgs += @("--tenant=""exampletenant""");

az login --service-principal $loginArgs

This runs in a non-interactive PowerShell session, and so just appears to hang indefinitely for the user. When run in an interactive session, you get the Password: prompt as if no password were provided, because the args aren't parsed/passed properly.

EmilDamsbo commented 1 year ago

This has had me fixing infrastructure code on so many release branches in different repositories. Please, for the love of all that is good. Fix this issue with 2.41 so I don't have to go and fix even more stuff when it inevitably breaks on an older branch of mine which needs hotfixing.

ghost commented 1 year ago

Thank you for your feedback. This has been routed to the support team for assistance.

yonzhan commented 1 year ago

The next version will revert that change and will be shipped on 10.12

EmilDamsbo commented 1 year ago

@yonzhan will this be done in such a way that the workaround mentioned in this thread will still work? So both these snippets would perform the same:

$env:AZ_INSTALLER="MSI"
& "$PSScriptRoot\..\python.exe" -IBm azure.cli $args

and

$env:AZ_INSTALLER="MSI"
& "$PSScriptRoot\..\python.exe" -IBm azure.cli @args  # notice the @ instead of the $ here with @args

Example courtesy of @Jawz84

yonzhan commented 1 year ago

If 2.39 works, 2.41 works.

Jawz84 commented 1 year ago

The next version will revert that change and will be shipped on 10.12

Thank you for taking this up and informing us.

jiasli commented 6 months ago

Duplicate of https://github.com/Azure/azure-cli/issues/23797

jiasli commented 6 months ago

@cveld, az @("extension", "list") is actually passing an array @("extension", "list") to az.ps1 as $arg[0]. This is not what az.ps1 is designed to take.

This can be verified with:

test.ps1:

$args.GetType()
$args[0].GetType()
$args[0][0].GetType()
> ./test.ps1 @("extension", "list")

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array
True     True     Object[]                                 System.Array
True     True     String                                   System.Object