Private/Invoke-Az.ps1
|
# Copyright (c) 2026 Jeffrey Snover. All rights reserved. # Licensed under the MIT License. See LICENSE file in the project root. function Invoke-Az { <# .SYNOPSIS Runs the Azure CLI with a captured stdout string and non-zero-exit error handling. .DESCRIPTION Thin wrapper around `& az ...` that: - passes args as a positional array (avoids Windows quoting traps) - captures stdout as a single string for JSON parsing - throws ActionableError on non-zero exit - returns $null when stdout is empty (matches PS pipeline conventions) Exists so cmdlets can mock the Azure CLI in Pester (t/1498). .PARAMETER Arguments The az CLI argument array, e.g. @('containerapp', 'revision', 'list', '--name', 'x'). Do NOT include 'az' itself. .PARAMETER CallerName Location string used in the thrown error's ActionableError. #> [CmdletBinding()] param( [Parameter(Mandatory)] [string[]]$Arguments, [Parameter()] [string]$CallerName = 'Invoke-Az' ) Set-StrictMode -Version Latest # Capture stdout and stderr separately by filtering type: PS represents # stderr from native commands as [ErrorRecord] objects when using 2>&1. # Mixing them corrupts JSON output (az update emits warnings to stderr # that az revision list/show do not — discovered in t/1500 staging dry-run). $RawOutput = & az @Arguments 2>&1 $ExitCode = $LASTEXITCODE $StdoutLines = @($RawOutput | Where-Object { $_ -isnot [System.Management.Automation.ErrorRecord] }) $StderrLines = @($RawOutput | Where-Object { $_ -is [System.Management.Automation.ErrorRecord] } | ForEach-Object { $_.ToString() }) foreach ($Line in $StderrLines) { Write-Verbose "[az stderr] $Line" } if ($ExitCode -ne 0) { throw (New-ActionableError ` -Goal "Run az $($Arguments -join ' ')" ` -Problem "Azure CLI exit $ExitCode" ` -Location $CallerName ` -NextSteps @('Verify az login: az account show', 'Check the arguments passed to Invoke-Az', "Stderr: $($StderrLines -join '; ')", "Output: $(($StdoutLines | Out-String).Trim())")) } if (-not $StdoutLines) { return $null } ($StdoutLines | Out-String).Trim() } |