diff --git a/azure-container-apps-sample/README.md b/azure-container-apps-sample/README.md index 6418da4..51e94c0 100644 --- a/azure-container-apps-sample/README.md +++ b/azure-container-apps-sample/README.md @@ -3,12 +3,14 @@ [Repository overview](../README.md) Two implemented examples demonstrate explicit binding to a **confidential DC4 -workload profile in UAE North**. Choose a guide based on the endpoint and +workload profile in UAE North**. The HTML quickstart also offers explicit +region selection for West Europe; the Functions workflow remains UAE +North-specific. Choose a guide based on the endpoint and prerequisites you need: | Sample | Status | | --- | --- | -| [Confidential hello world](container-apps-acc/README.md) | ARM template and PowerShell deployment using a DC4 confidential workload profile in UAE North | +| [Confidential hello world](container-apps-acc/README.md) | ARM template and PowerShell deployment using DC4 in UAE North by default, with explicit region selection for West Europe | | [Functions on Azure Container Apps](functions-on-azure-container-apps/README.md) | Tested native Functions hello world on confidential DC4 in UAE North: Node.js 24, fresh ACR builds, managed identity, private Blob host storage, deployment and verification | Start with **Confidential hello world**. It uses Microsoft's public quickstart @@ -59,13 +61,14 @@ $subscriptionId = '' $resourceGroup = '' $environmentName = 'cae-acc-hello' $appName = 'ca-acc-hello' +$location = 'uaenorth' # Use 'westeurope' only for the HTML quickstart. Connect-AzAccount -Subscription $subscriptionId . .\azure-container-apps-sample\container-apps-acc\DeploymentHelpers.ps1 $resourceBase = "/subscriptions/$subscriptionId/resourceGroups/$resourceGroup/providers/Microsoft.App" $app = Get-ArmJson -Path "$resourceBase/containerApps/${appName}?api-version=2025-07-01" $environment = Get-ArmJson -Path "$resourceBase/managedEnvironments/${environmentName}?api-version=2025-07-01" -$appUrl = Assert-ConfidentialContainerApp -App $app -Environment $environment +$appUrl = Assert-ConfidentialContainerApp -App $app -Environment $environment -Location $location $assignedProfile = @($environment.properties.workloadProfiles | Where-Object name -eq $app.properties.workloadProfileName) $revisionName = $app.properties.latestReadyRevisionName @@ -96,7 +99,7 @@ if ($revision.properties.active -ne $true -or ``` The shared guard **throws** unless the app references the expected environment, -both resources report `Succeeded` in UAE North, the assigned profile is +both resources report `Succeeded` in the explicitly expected region, the assigned profile is `confidential` with type `DC4` and minimum/maximum one node, and the latest revision is ready. It also checks the examples' HTTPS ingress settings. The additional revision read requires an active revision, at least one replica, diff --git a/azure-container-apps-sample/container-apps-acc/Deploy-ContainerApp.ps1 b/azure-container-apps-sample/container-apps-acc/Deploy-ContainerApp.ps1 index b0a239b..d41b3ab 100644 --- a/azure-container-apps-sample/container-apps-acc/Deploy-ContainerApp.ps1 +++ b/azure-container-apps-sample/container-apps-acc/Deploy-ContainerApp.ps1 @@ -3,6 +3,7 @@ [CmdletBinding()] param( [Parameter(Mandatory)][guid]$SubscriptionId, + [ValidateSet('uaenorth', 'westeurope')][string]$Location = 'uaenorth', [ValidatePattern('^[a-zA-Z0-9._()-]{1,90}$')] [string]$ResourceGroupName = "rg-container-apps-acc-$([guid]::NewGuid().ToString('N').Substring(0, 8))", [ValidatePattern('^[a-z][a-z0-9-]{0,30}[a-z0-9]$')][string]$EnvironmentName = 'cae-acc-hello', @@ -21,20 +22,19 @@ $context = Set-AzContext -SubscriptionId $SubscriptionId -TenantId $currentConte if ($context.Environment.Name -ne 'AzureCloud') { throw 'This sample supports Azure public cloud only.' } -Write-Host "Subscription: $SubscriptionId; tenant: $($context.Tenant.Id); region: uaenorth" +Write-Host "Subscription: $SubscriptionId; tenant: $($context.Tenant.Id); region: $Location" Write-Host "Resource group: $ResourceGroupName; environment: $EnvironmentName; app: $AppName" Write-Warning 'One dedicated DC4 node will incur ongoing charges. Nothing is automatically deleted, including on failure.' - foreach ($namespace in @('Microsoft.App', 'Microsoft.Network')) { $provider = Get-ArmJson -Path "/subscriptions/$SubscriptionId/providers/${namespace}?api-version=2021-04-01" if ($provider.registrationState -ne 'Registered') { throw "Register $namespace with Register-AzResourceProvider -ProviderNamespace $namespace and wait for Registered, then retry." } } -$regionalBase = "/subscriptions/$SubscriptionId/providers/Microsoft.App/locations/uaenorth" +$regionalBase = "/subscriptions/$SubscriptionId/providers/Microsoft.App/locations/$Location" $types = Get-ArmJson -Path "${regionalBase}/availableManagedEnvironmentsWorkloadProfileTypes?api-version=2025-07-01" if (-not ($types.value | Where-Object name -eq 'DC4')) { - throw 'UAE North does not advertise DC4 for this subscription. No other compute or region will be used.' + throw "$Location does not advertise DC4 for this subscription. No other compute or region will be used." } $usage = Get-ArmJson -Path "${regionalBase}/usages?api-version=2025-07-01" $quota = @($usage.value | Where-Object { $_.name.value -eq 'ManagedEnvironmentCount' }) @@ -42,7 +42,7 @@ if ($quota.Count -ne 1 -or $null -eq $quota[0].currentValue -or $null -eq $quota throw 'Could not determine the regional managed-environment quota. Check Azure quota before retrying.' } if ($quota[0].currentValue -ge $quota[0].limit) { - throw "UAE North managed-environment quota exhausted ($($quota[0].currentValue)/$($quota[0].limit)). Request quota; no fallback is permitted." + throw "$Location managed-environment quota exhausted ($($quota[0].currentValue)/$($quota[0].limit)). Request quota; no fallback is permitted." } $group = Get-AzResourceGroup -ErrorAction Stop | Where-Object ResourceGroupName -eq $ResourceGroupName @@ -53,7 +53,7 @@ if ($group) { } } else { - $null = New-AzResourceGroup -Name $ResourceGroupName -Location uaenorth ` + $null = New-AzResourceGroup -Name $ResourceGroupName -Location $Location ` -Tag @{ sample = 'container-apps-acc' } -ErrorAction Stop } @@ -61,7 +61,7 @@ $parameters = @{ ResourceGroupName = $ResourceGroupName TemplateFile = Join-Path $PSScriptRoot 'main.json' TemplateParameterObject = @{ - location = 'uaenorth' + location = $Location environmentName = $EnvironmentName appName = $AppName } @@ -83,7 +83,7 @@ try { throw "Deployment '$deploymentName' did not succeed: $($deployment.ProvisioningState)" } & (Join-Path $PSScriptRoot 'Test-ContainerApp.ps1') -SubscriptionId $SubscriptionId ` - -ResourceGroupName $ResourceGroupName -EnvironmentName $EnvironmentName -AppName $AppName + -ResourceGroupName $ResourceGroupName -EnvironmentName $EnvironmentName -AppName $AppName -Location $Location } catch { Write-Warning "Validation, deployment or verification failed. Resources are retained in '$ResourceGroupName'. Inspect deployment '$deploymentName' and any partial resources; this is NOT a successful confidential deployment." diff --git a/azure-container-apps-sample/container-apps-acc/DeploymentHelpers.ps1 b/azure-container-apps-sample/container-apps-acc/DeploymentHelpers.ps1 index 4d90b2b..f982d2e 100644 --- a/azure-container-apps-sample/container-apps-acc/DeploymentHelpers.ps1 +++ b/azure-container-apps-sample/container-apps-acc/DeploymentHelpers.ps1 @@ -13,7 +13,8 @@ function Assert-ConfidentialContainerApp { [CmdletBinding()] param( [Parameter(Mandatory)][object]$App, - [Parameter(Mandatory)][object]$Environment + [Parameter(Mandatory)][object]$Environment, + [ValidateSet('uaenorth', 'westeurope')][string]$Location = 'uaenorth' ) if ($App.properties.provisioningState -ne 'Succeeded' -or @@ -30,9 +31,9 @@ function Assert-ConfidentialContainerApp { $profiles[0].minimumCount -ne 1 -or $profiles[0].maximumCount -ne 1) { throw 'Expected explicit binding to the confidential DC4 profile with exactly one node; refusing ordinary-compute fallback.' } - if ($App.location -notin @('uaenorth', 'UAE North') -or - $Environment.location -notin @('uaenorth', 'UAE North')) { - throw 'Expected the app and environment in UAE North.' + if (($App.location -replace '\s', '') -ne $Location -or + ($Environment.location -replace '\s', '') -ne $Location) { + throw "Expected the app and environment in $Location." } $ingress = $App.properties.configuration.ingress if ($ingress.external -ne $true -or $ingress.allowInsecure -ne $false -or diff --git a/azure-container-apps-sample/container-apps-acc/README.md b/azure-container-apps-sample/container-apps-acc/README.md index 3056fcb..53e9cd5 100644 --- a/azure-container-apps-sample/container-apps-acc/README.md +++ b/azure-container-apps-sample/container-apps-acc/README.md @@ -3,7 +3,8 @@ [All Container Apps samples](../README.md) | [Repository overview](../../README.md) Deploy Microsoft's hello-world container to **Azure Container Apps** in **UAE -North**, using a dedicated **DC4 confidential workload profile**. Then verify the +North** by default, using a dedicated **DC4 confidential workload profile**. An +explicit **West Europe** region selection is also available. Then verify the app's Azure-reported placement and open its public HTTPS page. This example follows [Confidential compute in Azure Container Apps](https://learn.microsoft.com/azure/container-apps/confidential-compute) @@ -15,6 +16,16 @@ DC4 binding and ready revision were verified, and the public endpoint returned HTTP 200 with the hello-world page. Availability, quota and organizational policies still need checking in your own subscription. +**Tested in West Europe:** 10 November 2026. The same template deployed +successfully and passed live DC4 placement, revision-health and HTTPS checks. +See [observed results](#west-europe-observed-results-10-november-2026). + +> **Region selection:** Confidential compute in Azure Container Apps is a GA +> product. This sample adds region selection with `-Location westeurope`, while +> retaining UAE North as the default. Check regional availability, quota and +> capacity for your subscription. There is no fallback to another region or +> ordinary compute. + For an application-code example, continue with [Functions on Azure Container Apps](../functions-on-azure-container-apps/README.md). It uses the native Functions integration with Node.js 24, a fresh runtime-image @@ -56,7 +67,7 @@ Secure Key Release, application authentication, or protection of external storag ## Resources and cost ```text -New dedicated resource group (UAE North) +New dedicated resource group (UAE North by default; West Europe explicitly) Virtual network (10.42.0.0/16) Delegated infrastructure subnet (10.42.0.0/23), with NSG Container Apps environment @@ -90,8 +101,8 @@ New dedicated resource group (UAE North) ## Prerequisites - An Azure public-cloud subscription authorized for confidential Container Apps. - The linked documentation currently lists **UAE North**. Availability and capacity - can change; this sample intentionally permits only `uaenorth`. + Availability and capacity can change; this sample permits `uaenorth` (default) + and explicit selection of `westeurope`. - Permission to create a resource group, VNet, NSG, managed environment and container app (for example Contributor at subscription scope). **Owner is not required**; this example does not activate PIM roles or create role assignments. Activate @@ -157,6 +168,63 @@ and Network API `2024-05-01`. Default names are `cae-acc-hello` and `ca-acc-hello`. Optional `-EnvironmentName` and `-AppName` parameters customize those names; use the same names for verification. +### Select West Europe + +Use the same sign-in prerequisites and a new dedicated group. Pass the region +to **both** deployment and verification; omitting it selects UAE North. + +```powershell +$resourceGroup = "rg-container-apps-acc-weu-$([guid]::NewGuid().ToString('N').Substring(0, 8))" +.\Deploy-ContainerApp.ps1 -SubscriptionId $subscriptionId ` + -ResourceGroupName $resourceGroup -Location westeurope -ValidateOnly +$result = .\Deploy-ContainerApp.ps1 -SubscriptionId $subscriptionId ` + -ResourceGroupName $resourceGroup -Location westeurope +$result | Format-List +.\Test-ContainerApp.ps1 -SubscriptionId $subscriptionId ` + -ResourceGroupName $resourceGroup -Location westeurope | Format-List +``` + +The script checks profiles and quota in the selected region. Passing preflight +does not guarantee capacity at provisioning time. + +### West Europe observed results (10 November 2026) + +Verified at **18:26:29 UTC**, using Container Apps API `2025-07-01`: + +| Check | Observed result | +| --- | --- | +| Regional discovery | West Europe advertised DC4; regional environment usage was 0 of 50 before deployment | +| Preflight | 27 offline tests passed; ARM validation and what-if succeeded, with exactly four resource creates | +| Deployment | `acc-hello-weu-20261110` succeeded in `rg-container-apps-acc-weu-20261110` | +| Environment/app | `cae-acc-hello` and `ca-acc-hello` both reported `Succeeded` in West Europe | +| Explicit placement | App selected `confidential`; matching profile type `DC4`, minimum 1 / maximum 1 node | +| Revision | `ca-acc-hello--950ijs1`: active, `Healthy`, `RunningAtMaxScale`, one replica | +| Public endpoint | HTTPS returned HTTP 200 and the `Azure Container Apps` page signature | +| Identity/RBAC | App identity `None`; public Microsoft image, no application role assignment required | + +The template was deployed through the authenticated Azure CLI, with +`location=westeurope environmentName=cae-acc-hello appName=ca-acc-hello`; +the PowerShell Az context was not signed in. Live resources were read through +`az rest`, then checked using this sample's shared placement and endpoint +helpers. The offline suite separately verifies the PowerShell deployment +workflow's regional parameter routing. + +The first immediate post-deployment check encountered environment state +`Updating`, despite ARM reporting deployment success. Bounded readiness polling +then observed `Succeeded`; no redeployment or configuration workaround was +needed. `RunningAtMaxScale` is expected with the sample's maximum of one replica. +Azure also added its default Consumption profile, but the app remained +explicitly bound to DC4. + +No regional or ordinary-compute fallback, policy bypass, or AKS restart was +performed. Resources were initially retained, then removed on request on +10 November 2026. At 19:22 UTC, both the sample resource group and its +service-managed infrastructure group were verified absent. The test endpoint +is no longer a live demo. These results record one successful deployment; +availability and capacity must still be checked for your subscription. +Configuration verification does not provide independent cryptographic hardware +attestation. + Expect environment creation and DC4 provisioning to take several minutes. Success produces `AppUrl` and `PortalUrl`, both fully qualified HTTPS links, plus resource IDs and the verified profile. The page is Microsoft's Container Apps @@ -185,7 +253,8 @@ Verification requires: - Provisioning and latest-revision readiness settle within a bounded wait of 60 checks, 10 seconds apart. A temporarily `Updating` environment after ARM deployment is retried; failed or unexpected states terminate immediately. -- Both resources report `Succeeded` in UAE North. +- Both resources report `Succeeded` in the explicitly expected region (UAE North + by default; pass `-Location westeurope` to select West Europe). - The app references the expected environment and its `confidential` DC4 profile, with exactly one dedicated node. - The latest revision is ready and ingress is public, HTTPS-only, on target port 80. @@ -224,6 +293,9 @@ Use this deployment's subscription, resource group and environment name there. The Functions app explicitly selects the same `confidential` DC4 profile and can share its existing node with the quickstart app. +The sibling Functions workflow remains **UAE North-specific**; do not reuse a +West Europe environment with it unchanged. + The Functions guide adds a registry, user-assigned identity, scoped role assignments, host storage and private Blob networking. Unlike this quickstart, it requires permission to create role assignments. Its additional subnet @@ -238,7 +310,7 @@ respective guides; a successful hello-world response alone is insufficient. | --- | --- | | Provider is not registered | Register the named provider in the selected subscription and wait for `Registered`. | | Authorization/PIM error | Confirm tenant/subscription and activate an appropriate role; retry after propagation. | -| No DC4 profile, zero/exhausted quota, or insufficient capacity | Check UAE North support and request service access/quota/capacity through Azure support. Do not substitute ordinary compute. | +| No DC4 profile, zero/exhausted quota, or insufficient capacity | Check availability in the selected region and request service access/quota/capacity through Azure support. Do not substitute ordinary compute. | | ARM validation or deployment fails | Read the terminating error and the resource group's **Deployments** details. Inspect partial resources; they are retained. | | Organizational networking policy rejects deployment | The template includes VNet injection and a subnet NSG. Review any additional private-ingress, outbound connectivity, address-space or tagging requirements with your administrator; do not bypass policy. | | Deployment succeeded but verification failed | Inspect the revision and profile, then rerun the verification script. Do not treat this as a verified confidential deployment. | diff --git a/azure-container-apps-sample/container-apps-acc/Test-ContainerApp.ps1 b/azure-container-apps-sample/container-apps-acc/Test-ContainerApp.ps1 index 0105d13..344f99b 100644 --- a/azure-container-apps-sample/container-apps-acc/Test-ContainerApp.ps1 +++ b/azure-container-apps-sample/container-apps-acc/Test-ContainerApp.ps1 @@ -3,6 +3,7 @@ [CmdletBinding()] param( [Parameter(Mandatory)][guid]$SubscriptionId, + [ValidateSet('uaenorth', 'westeurope')][string]$Location = 'uaenorth', [Parameter(Mandatory)][ValidatePattern('^[a-zA-Z0-9._()-]{1,90}$')][string]$ResourceGroupName, [ValidatePattern('^[a-z][a-z0-9-]{0,30}[a-z0-9]$')][string]$EnvironmentName = 'cae-acc-hello', [ValidatePattern('^[a-z][a-z0-9-]{0,30}[a-z0-9]$')][string]$AppName = 'ca-acc-hello' @@ -38,7 +39,7 @@ for ($attempt = 1; $attempt -le 60; $attempt++) { Write-Host "Waiting for provisioning and revision readiness ($attempt/60)." Start-Sleep -Seconds 10 } -$url = Assert-ConfidentialContainerApp -App $app -Environment $environment +$url = Assert-ConfidentialContainerApp -App $app -Environment $environment -Location $Location Test-HelloWorldEndpoint -Uri $url [pscustomobject]@{ diff --git a/azure-container-apps-sample/container-apps-acc/main.json b/azure-container-apps-sample/container-apps-acc/main.json index 0bb98e7..77b1bcf 100644 --- a/azure-container-apps-sample/container-apps-acc/main.json +++ b/azure-container-apps-sample/container-apps-acc/main.json @@ -5,8 +5,8 @@ "location": { "type": "string", "defaultValue": "uaenorth", - "allowedValues": ["uaenorth"], - "metadata": { "description": "Region documented for confidential Container Apps. No regional fallback." } + "allowedValues": ["uaenorth", "westeurope"], + "metadata": { "description": "Explicit deployment region. West Europe requires live DC4 availability checks; no regional fallback." } }, "environmentName": { "type": "string", diff --git a/azure-container-apps-sample/container-apps-acc/tests/Test-Sample.ps1 b/azure-container-apps-sample/container-apps-acc/tests/Test-Sample.ps1 index d531ea3..f8157d5 100644 --- a/azure-container-apps-sample/container-apps-acc/tests/Test-Sample.ps1 +++ b/azure-container-apps-sample/container-apps-acc/tests/Test-Sample.ps1 @@ -26,7 +26,7 @@ function New-Fixture { @{ Environment = @{ id = '/subscriptions/test/resourceGroups/test/providers/Microsoft.App/managedEnvironments/test' - location = 'uaenorth' + location = $state.fixtureLocation ?? 'uaenorth' properties = @{ provisioningState = 'Succeeded' workloadProfiles = @(@{ name = 'confidential'; workloadProfileType = 'DC4'; minimumCount = 1; maximumCount = 1 }) @@ -34,7 +34,7 @@ function New-Fixture { } App = @{ id = '/subscriptions/test/resourceGroups/test/providers/Microsoft.App/containerApps/test' - location = 'uaenorth' + location = $state.fixtureLocation ?? 'uaenorth' properties = @{ provisioningState = 'Succeeded' environmentId = '/subscriptions/test/resourceGroups/test/providers/Microsoft.App/managedEnvironments/test' @@ -54,12 +54,14 @@ function Get-AzResourceGroup { if ($state.existing) { @{ ResourceGroupName = 'rg-offline'; Tags = @{ sample = 'container-apps-acc' } } } } function Get-AzResource { if ($state.populated) { @{ Name = 'unrelated' } } } -function New-AzResourceGroup { $state.groupCreates++ } +function New-AzResourceGroup { param($Location) $state.groupCreates++; $state.groupLocation = $Location } function Test-AzResourceGroupDeployment { if ($state.invalid) { @{ Code = 'InvalidTemplate'; Message = 'test rejection' } } elseif ($state.validationEmptyString) { '' } } function New-AzResourceGroupDeployment { + param($TemplateParameterObject) + $state.templateLocation = $TemplateParameterObject.location $state.deployments++ if ($state.deployFails) { throw 'Mock capacity rejection' } @{ ProvisioningState = 'Succeeded' } @@ -69,6 +71,7 @@ function Invoke-AzRestMethod { if ($state.restFails) { return @{ StatusCode = 403; Content = 'Forbidden' } } $data = switch -Wildcard ($Path) { '*availableManagedEnvironmentsWorkloadProfileTypes*' { + $state.profilePath = $Path if ($state.noProfile) { @{ value = @() } } else { @{ value = @(@{ name = 'DC4' }) } } } @@ -94,6 +97,10 @@ function Invoke-WebRequest { } function Start-Sleep {} function Reset-Mocks { + $state.fixtureLocation = 'uaenorth' + $state.groupLocation = $null + $state.templateLocation = $null + $state.profilePath = $null $state.existing = $false $state.populated = $false $state.groupCreates = 0 @@ -128,7 +135,8 @@ Case 'ARM template binds DC4, HTTPS and bounded scaling' { $envResource = $template.resources | Where-Object type -eq 'Microsoft.App/managedEnvironments' $appResource = $template.resources | Where-Object type -eq 'Microsoft.App/containerApps' Assert ($template.resources.Count -eq 4) 'Unexpected supporting resources.' - Assert ($template.parameters.location.allowedValues.Count -eq 1 -and $template.parameters.location.allowedValues[0] -eq 'uaenorth') 'Region fallback allowed.' + Assert ($template.parameters.location.defaultValue -eq 'uaenorth') 'Default region changed.' + Assert (($template.parameters.location.allowedValues -join ',') -eq 'uaenorth,westeurope') 'Unexpected region allowed.' Assert ($envResource.properties.workloadProfiles.Count -eq 1) 'Unexpected additional profiles.' Assert ($envResource.properties.appLogsConfiguration.destination -ceq '') 'Expected disabled centralized logging via empty ARM destination.' $profile = $envResource.properties.workloadProfiles[0] @@ -148,6 +156,15 @@ Case 'Accept valid Azure-reported confidential placement' { $f = New-Fixture Assert ((Assert-ConfidentialContainerApp -App $f.App -Environment $f.Environment) -eq 'https://test.example.com') 'Wrong URL.' } +Case 'Accept explicitly selected West Europe and reject region mismatch' { + $f = New-Fixture + $f.App.location = 'West Europe' + $f.Environment.location = 'westeurope' + Assert ((Assert-ConfidentialContainerApp -App $f.App -Environment $f.Environment -Location westeurope) -eq 'https://test.example.com') 'West Europe rejected.' + Expect-Failure { Assert-ConfidentialContainerApp -App $f.App -Environment $f.Environment } 'Expected the app and environment' + $f.Environment.location = 'uaenorth' + Expect-Failure { Assert-ConfidentialContainerApp -App $f.App -Environment $f.Environment -Location westeurope } 'Expected the app and environment' +} Case 'Accept UAE North display name returned by the live API' { $f = New-Fixture $f.App.location = 'UAE North' @@ -177,7 +194,7 @@ foreach ($change in @( @{ Name = 'unready revision'; Edit = { param($f) $f.App.properties.latestReadyRevisionName = 'old' }; Pattern = 'not ready' }, @{ Name = 'insecure ingress'; Edit = { param($f) $f.App.properties.configuration.ingress.allowInsecure = $true }; Pattern = 'HTTPS-only' }, @{ Name = 'failed provisioning'; Edit = { param($f) $f.Environment.properties.provisioningState = 'Failed' }; Pattern = 'Succeeded' }, - @{ Name = 'wrong region'; Edit = { param($f) $f.App.location = 'eastus' }; Pattern = 'UAE North' }, + @{ Name = 'wrong region'; Edit = { param($f) $f.App.location = 'eastus' }; Pattern = 'Expected the app and environment in uaenorth' }, @{ Name = 'malformed FQDN'; Edit = { param($f) $f.App.properties.configuration.ingress.fqdn = 'example.com/other' }; Pattern = 'FQDN' } )) { Case "Reject $($change.Name)" { @@ -248,4 +265,11 @@ Case 'HTTP verification failure propagates from deployment' { Reset-Mocks; $state.content = 'wrong app' Expect-Failure { & $deployScript @deployArgs } 'signature' } +Case 'Explicit West Europe flows through discovery, deployment and verification' { + Reset-Mocks; $state.fixtureLocation = 'westeurope' + $result = & $deployScript @deployArgs -Location westeurope + Assert ($state.profilePath -match '/locations/westeurope/') 'Discovery used the wrong region.' + Assert ($state.groupLocation -eq 'westeurope' -and $state.templateLocation -eq 'westeurope') 'Deployment used the wrong region.' + Assert ($result.Location -eq 'westeurope' -and $result.WorkloadProfileType -eq 'DC4') 'Verification used the wrong region.' +} Write-Host "$script:passed offline tests passed."