Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions azure-container-apps-sample/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -59,13 +61,14 @@ $subscriptionId = '<your-subscription-id>'
$resourceGroup = '<your-dedicated-demo-resource-group>'
$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
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -21,28 +22,27 @@ $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' })
if ($quota.Count -ne 1 -or $null -eq $quota[0].currentValue -or $null -eq $quota[0].limit) {
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
Expand All @@ -53,15 +53,15 @@ if ($group) {
}
}
else {
$null = New-AzResourceGroup -Name $ResourceGroupName -Location uaenorth `
$null = New-AzResourceGroup -Name $ResourceGroupName -Location $Location `
-Tag @{ sample = 'container-apps-acc' } -ErrorAction Stop
}

$parameters = @{
ResourceGroupName = $ResourceGroupName
TemplateFile = Join-Path $PSScriptRoot 'main.json'
TemplateParameterObject = @{
location = 'uaenorth'
location = $Location
environmentName = $EnvironmentName
appName = $AppName
}
Expand All @@ -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."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
84 changes: 78 additions & 6 deletions azure-container-apps-sample/container-apps-acc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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. |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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]@{
Expand Down
4 changes: 2 additions & 2 deletions azure-container-apps-sample/container-apps-acc/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Loading
Loading