Public/New-ContainerAppRevision.ps1
|
# Copyright (c) 2026 Jeffrey Snover. All rights reserved. # Licensed under the MIT License. See LICENSE file in the project root. function New-ContainerAppRevision { <# .SYNOPSIS Deploys a new Azure Container App revision at 0% traffic. .DESCRIPTION Wraps `az containerapp update --revision-suffix` to create a new revision without shifting any traffic to it. This is the safe blue-green deploy first step (t/1500 Phase 3). This cmdlet is distinct from Deploy-TaxEditorImage, which shifts immediately to 100% traffic. Use New-ContainerAppRevision when you want to probe the new revision via its FQDN (Get-ContainerAppRevision -Mode Fqdn) before promoting via Set-ContainerAppTraffic. CRITICAL: The revision name returned by az is verified non-empty. A silent empty return would break downstream steps 4/8/10 of the deploy chain. .PARAMETER ImageRef Full image reference including tag (e.g. 'ghcr.io/jpsnover/taxonomy-editor:latest'). .PARAMETER RevisionSuffix Suffix appended to the app name to form the revision name (e.g. 'deploy-abc1234-12345'). Must be unique. .PARAMETER EnvVars Optional hashtable of additional environment variables to set on the revision. Keys and values are passed as KEY=val pairs via --set-env-vars. .PARAMETER AppName Container App name. Default: 'taxonomy-editor'. .PARAMETER ResourceGroup Azure resource group. Default: 'ai-triad'. .EXAMPLE New-ContainerAppRevision ` -ImageRef 'ghcr.io/jpsnover/taxonomy-editor:sha-abc1234' ` -RevisionSuffix 'deploy-abc1234-12345' .EXAMPLE New-ContainerAppRevision ` -ImageRef 'ghcr.io/jpsnover/taxonomy-editor:sha-abc1234' ` -RevisionSuffix 'deploy-abc1234-12345' ` -EnvVars @{ FEATURE_FLAG = '1'; BUILD_ID = 'abc1234' } .LINK Get-ContainerAppRevision .LINK Set-ContainerAppTraffic .LINK Disable-ContainerAppRevision .LINK Show-AITriadHelp #> [CmdletBinding()] param( [Parameter(Mandatory)] [string]$ImageRef, [Parameter(Mandatory)] [string]$RevisionSuffix, [Parameter()] [hashtable]$EnvVars, [Parameter()] [string]$AppName = 'taxonomy-editor', [Parameter()] [string]$ResourceGroup = 'ai-triad' ) Set-StrictMode -Version Latest $AzCmd = Get-Command az -ErrorAction SilentlyContinue if (-not $AzCmd) { throw (New-ActionableError ` -Goal "Create new Container App revision with suffix '$RevisionSuffix'" ` -Problem 'Azure CLI (az) not found on PATH' ` -Location 'New-ContainerAppRevision' ` -NextSteps @('Install Azure CLI: https://aka.ms/installazurecli', 'Ensure az is on your PATH')) } $AzArgs = @( 'containerapp', 'update', '--name', $AppName, '--resource-group', $ResourceGroup, '--image', $ImageRef, '--revision-suffix', $RevisionSuffix, '--output', 'json' ) if ($EnvVars -and $EnvVars.Count -gt 0) { $EnvPairs = @($EnvVars.GetEnumerator() | ForEach-Object { "$($_.Key)=$($_.Value)" }) $AzArgs += '--set-env-vars' $AzArgs += $EnvPairs } $Json = Invoke-Az $AzArgs -CallerName 'New-ContainerAppRevision' # Parse the JSON response and extract latestRevisionName. # Use JSON output (not --query -o tsv) so we can detect az returning an # empty field vs az failing — a silent empty breaks Steps 4/8/10 downstream. $Obj = $Json | ConvertFrom-Json $RevisionName = $null if ($Obj.PSObject.Properties['properties']) { if ($Obj.properties.PSObject.Properties['latestRevisionName']) { $RevisionName = $Obj.properties.latestRevisionName } } if ([string]::IsNullOrWhiteSpace($RevisionName)) { throw (New-ActionableError ` -Goal "Create new Container App revision with suffix '$RevisionSuffix'" ` -Problem 'az containerapp update returned empty latestRevisionName — cannot confirm revision was created' ` -Location 'New-ContainerAppRevision' ` -NextSteps @( 'Check az containerapp revision list to see if the revision was partially created', 'Verify the --revision-suffix is unique and does not conflict with an existing revision', 'Re-run with -Verbose to see the raw az output')) } [PSCustomObject]@{ RevisionName = $RevisionName ImageRef = $ImageRef Suffix = $RevisionSuffix Timestamp = [System.DateTime]::UtcNow.ToString('o') } } |