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
43 changes: 41 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ urlFragment: confidential-computing-samples

![MIT license badge](https://img.shields.io/badge/license-MIT-green.svg)

**Last Updated:** September 2026
**Last Updated:** November 2026

> **Heads up — this is the new home for Azure Confidential Computing samples.**
> [`https://aka.ms/accsamples`](https://aka.ms/accsamples) now redirects here.
Expand All @@ -47,7 +47,13 @@ Confidential computing is the protection of data-in-use through isolating comput
- Any data processed using these samples
- **AI-Assisted Content:** Several samples in this repository were created with significant assistance from AI coding tools, primarily [GitHub Copilot](https://github.com/features/copilot) inside [Visual Studio Code](https://code.visualstudio.com/), to showcase modern AI-assisted development. While functional, AI-assisted code should always be reviewed by qualified security professionals before use in sensitive scenarios.

## 🆕 What's New (September 2026)
## What's New (November 2026)

| Addition | Description |
|---|---|
| **[Confidential Azure Container Apps samples](azure-container-apps-sample/README.md)** | UAE North DC4 hello world and a tested [native Azure Functions walkthrough](azure-container-apps-sample/functions-on-azure-container-apps/README.md#start-here) with Node.js 24, fresh ACR builds, managed identity, private Blob host storage, placement/health verification, and portal/API access instructions. Resources are retained until explicitly cleaned up. |

### Previously (September 2026)

| Addition | Description |
|---|---|
Expand Down Expand Up @@ -78,6 +84,13 @@ Confidential computing is the protection of data-in-use through isolating comput

## Prerequisites

Requirements vary by sample; follow the selected guide rather than installing
every tool below. The [Container Apps quickstart](azure-container-apps-sample/container-apps-acc/README.md#prerequisites)
uses PowerShell and Az modules without Docker, Python or `confcom`. The
[Functions walkthrough](azure-container-apps-sample/functions-on-azure-container-apps/README.md#2-prepare-azure-prerequisites)
adds Azure CLI and role-assignment permissions; local development uses Node.js
24 and Azurite, and remote ACR builds avoid a local Docker requirement.

- [Azure subscription](https://azure.microsoft.com/free/)
- [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli?view=azure-cli-latest) (v2.60+) with `confcom` extension
- [Azure PowerShell](https://learn.microsoft.com/en-us/powershell/azure/install-azure-powershell?view=azps-latest) (Az module 12.0+)
Expand All @@ -98,6 +111,25 @@ az confcom --version

This repository is organized by Azure service type and deployment method:

### [Azure Container Apps Samples](azure-container-apps-sample/README.md)

- **[Confidential hello world](azure-container-apps-sample/container-apps-acc/README.md)** - Deploy a public quickstart container on a dedicated DC4 confidential workload profile in UAE North.
- **[Functions on confidential Container Apps](azure-container-apps-sample/functions-on-azure-container-apps/README.md#start-here)** - End-to-end native Functions HTTP sample, including local testing, identity/scoped roles, private host-storage networking, fresh Node.js 24 image builds, ARM deployment, verification, portal/API links, and cleanup.

Start with the HTML quickstart, then optionally add Functions to the same
confidential environment and dedicated group. Both were tested in UAE North on
9 November 2026; check current regional availability, quota and organizational
policy before deployment. Both expose anonymous public endpoints and retain
billable resources until explicitly cleaned up. Functions additionally verifies
private host-storage connectivity; HTTP 200 alone is not proof of host health.
These are configuration/functional checks, not independent hardware attestation.
No Azure App Service deployment is included.

For explicit evidence, use the [confidential-compute validation steps](azure-container-apps-sample/README.md#validate-confidential-compute-placement)
for each app. They show the app-to-environment-to-DC4 binding, check revision
running/health states, provide portal cross-checks, and distinguish Azure-reported
configuration from independent cryptographic attestation.

### [ACI Samples](/aci-samples/README.md)
Azure Container Instances with AMD SEV-SNP confidential computing:
- **BuildRandomACI.ps1** - Create confidential ACI with hello-world container
Expand Down Expand Up @@ -184,6 +216,13 @@ three sample JSON messages, and verify them on read-back using passwordless Entr
authentication. Tries Korea Central first, then UAE North on regional
availability failures.

### [Azure Container Apps Samples](azure-container-apps-sample/README.md)
Deploy Microsoft's hello-world container with HTTPS ingress on an explicitly
selected **DC4 confidential workload profile** in **UAE North**. Includes ARM
infrastructure, PowerShell deployment and placement checks, and customer setup
instructions. Also includes a Node.js Functions-on-Container-Apps hello-world
example and a placeholder for a future App Services example.

### Utility Snippets
Reusable PowerShell snippets for common confidential computing tasks:
- `snippet-createDES.ps1` - Create Disk Encryption Set for Confidential VMs
Expand Down
153 changes: 153 additions & 0 deletions azure-container-apps-sample/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# Azure Container Apps samples

[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
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 |
| [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
container image and does not require Docker, a private registry, or application
code changes.

Then follow the **[Functions deployment walkthrough](functions-on-azure-container-apps/README.md#start-here)**
to run application code on that confidential environment. It covers local
Azurite testing, scoped permissions, private storage networking, immutable image
deployment, host-health checks, and generating your own portal and API links.
The hello-world route is `GET /api/hello`, which returns JSON rather than an HTML
page.

## Deployment order and verification

1. Deploy and verify the [confidential environment and HTML quickstart](container-apps-acc/README.md#start-here).
2. Optionally add the [native Functions JSON endpoint](functions-on-azure-container-apps/README.md#start-here)
to that environment and resource group. It can share the existing DC4 node.
3. Use each guide's placement and endpoint checks. For Functions, also confirm
host-storage health and Blob lease acquisition; HTTP 200 alone is insufficient.
4. Use the portal and application links generated by your own deployment.

The base quickstart needs no image build or role assignments. Functions adds
ACR, managed identity/scoped roles, storage, and a Blob private endpoint with
VNet-linked DNS; it requires additional tools and role-assignment permissions.
Neither guide deploys the Azure App Service platform.

## Validate confidential-compute placement

Follow these checks for **each app**, including when both share one environment.
They implement the [Microsoft Learn verification procedure](https://learn.microsoft.com/azure/container-apps/confidential-compute#verify-your-confidential-compute-configuration):
read the app's assigned profile, then look up that exact profile's type in the
environment the app actually references. A profile merely named `confidential`
is not sufficient; its type must be a DC-series type. These examples require
**DC4** specifically, with no ordinary-compute fallback.

### Read-only PowerShell verification

Run from the repository root with PowerShell 7 and `Az.Accounts` installed.
Replace the subscription and group with your deployment values. Use
`ca-acc-hello` for the HTML example or `ca-functions-hello` for Functions
(or your customized app name), and the environment name you deployed.
This only reads Azure resources; it does not deploy or change them.

```powershell
$ErrorActionPreference = 'Stop'
$subscriptionId = '<your-subscription-id>'
$resourceGroup = '<your-dedicated-demo-resource-group>'
$environmentName = 'cae-acc-hello'
$appName = 'ca-acc-hello'

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
$assignedProfile = @($environment.properties.workloadProfiles |
Where-Object name -eq $app.properties.workloadProfileName)
$revisionName = $app.properties.latestReadyRevisionName
$revision = Get-ArmJson -Path "$($app.id)/revisions/${revisionName}?api-version=2025-07-01"
if ($revision.properties.active -ne $true -or
$revision.properties.runningState -notin @('Running', 'RunningAtMaxScale') -or
$revision.properties.healthState -ne 'Healthy' -or
$revision.properties.replicas -lt 1) {
throw 'Expected an active, running, healthy revision with at least one replica; inspect revisions and replicas.'
}
[pscustomobject]@{
AppId = $app.id
EnvironmentId = $environment.id
Location = $app.location
AssignedProfile = $app.properties.workloadProfileName
ProfileType = $assignedProfile[0].workloadProfileType
MinimumNodes = $assignedProfile[0].minimumCount
MaximumNodes = $assignedProfile[0].maximumCount
LatestReadyRevision = $revisionName
RevisionActive = $revision.properties.active
Replicas = $revision.properties.replicas
RevisionRunningState = $revision.properties.runningState
RevisionHealthState = $revision.properties.healthState
AppUrl = $appUrl
CheckedAtUtc = [datetime]::UtcNow.ToString('o')
Evidence = 'Azure-reported confidential placement, not hardware attestation'
} | Format-List
```

The shared guard **throws** unless the app references the expected environment,
both resources report `Succeeded` in UAE North, 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,
`Running` or `RunningAtMaxScale`, and `Healthy`. `RunningAtMaxScale` is normal
when the configured maximum replica count has been reached; it does not by
itself indicate a failure. If a check fails,
do not report successful confidential placement; inspect the named resource
and rerun after resolving the cause.

Expected output includes `AssignedProfile: confidential`, `ProfileType: DC4`,
`RevisionRunningState: Running` (or `RunningAtMaxScale`),
`RevisionActive: True`, at least one replica, and `RevisionHealthState: Healthy`, plus the
actual app/environment IDs and timestamp. Save that output if you need an
auditable configuration snapshot. Repeat with the second app name if deployed.
Run each guide's endpoint checks separately; Functions also needs its
[host-storage health checks](functions-on-azure-container-apps/README.md#5-verify-placement-http-response-and-host-storage).

### Cross-check in Azure portal

1. Open the **container app**, not just the resource group. In **JSON View**,
note `properties.environmentId` and `properties.workloadProfileName`.
2. Open that exact environment and select **Workload profiles**. Match the
assigned profile name and confirm its type is **DC4**. You can also inspect
`properties.workloadProfiles` in the environment's JSON View.
3. Return to the app's **Revisions and replicas** view. Confirm the latest
revision is ready and its replica is running. Repeat for the Functions app.
4. An additional `Consumption` profile in the environment is not a failure:
what matters is that **this app** selects the DC4 profile, not Consumption.

### What this proves, and what it does not

These are live **Azure control-plane configuration and runtime-state checks**.
Microsoft documents an app assigned to a DC-series profile as running on
confidential compute infrastructure. An HTTP response, container image, app
name, private endpoint, or presence of an unused DC4 profile does not establish
that assignment.

The checks are **not independent cryptographic hardware attestation**. These
examples do not retrieve a hardware report, validate an attestation token or
release secrets based on attested claims. If your threat model requires that
evidence, this walkthrough alone does not meet it. Do not equate private Blob
storage connectivity or HTTPS with proof of a trusted execution environment.

## Cost and security boundaries

Both samples create billable resources and retain them until you explicitly
clean them up. Regional availability and quota must be checked in your own
subscription. Configuration verification is not independent hardware
attestation. The endpoints are public and anonymous; send no sensitive data.
Private Blob networking in Functions does not make its HTTP ingress private.

Deleting one app leaves the DC4 environment and other support resources
billable. Deleting the dedicated group removes all examples and supporting
resources in it. Review the inventory and the guide's cleanup instructions first.
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#Requires -Version 7.0
#Requires -Modules Az.Accounts, Az.Resources
[CmdletBinding()]
param(
[Parameter(Mandatory)][guid]$SubscriptionId,
[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',
[ValidatePattern('^[a-z][a-z0-9-]{0,30}[a-z0-9]$')][string]$AppName = 'ca-acc-hello',
[switch]$ValidateOnly
)

$ErrorActionPreference = 'Stop'
. (Join-Path $PSScriptRoot 'DeploymentHelpers.ps1')

if (-not (Get-AzContext)) {
throw 'Sign in with Connect-AzAccount before running this script.'
}
$currentContext = Get-AzContext
$context = Set-AzContext -SubscriptionId $SubscriptionId -TenantId $currentContext.Tenant.Id -ErrorAction Stop
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 "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"
$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.'
}
$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."
}

$group = Get-AzResourceGroup -ErrorAction Stop | Where-Object ResourceGroupName -eq $ResourceGroupName
if ($group) {
if ($group.Tags['sample'] -ne 'container-apps-acc' -or
@(Get-AzResource -ResourceGroupName $ResourceGroupName -ErrorAction Stop).Count -ne 0) {
throw 'Refusing to deploy into an existing populated or unrelated resource group. Use a new dedicated group; use Test-ContainerApp.ps1 to verify an existing deployment.'
}
}
else {
$null = New-AzResourceGroup -Name $ResourceGroupName -Location uaenorth `
-Tag @{ sample = 'container-apps-acc' } -ErrorAction Stop
}

$parameters = @{
ResourceGroupName = $ResourceGroupName
TemplateFile = Join-Path $PSScriptRoot 'main.json'
TemplateParameterObject = @{
location = 'uaenorth'
environmentName = $EnvironmentName
appName = $AppName
}
ErrorAction = 'Stop'
}
$deploymentName = "acc-hello-$([guid]::NewGuid().ToString('N').Substring(0, 8))"
try {
# Az.Resources can return an empty string rather than no output on success.
$errors = @(Test-AzResourceGroupDeployment @parameters | Where-Object { $null -ne $_ -and $_ -cne '' })
if ($errors.Count -gt 0) {
throw "ARM validation failed: $($errors | ConvertTo-Json -Depth 20 -Compress)"
}
if ($ValidateOnly) {
Write-Host "ARM validation passed. Only the empty resource group '$ResourceGroupName' has been created; no app or environment deployed."
return
}
$deployment = New-AzResourceGroupDeployment -Name $deploymentName @parameters
if ($deployment.ProvisioningState -ne 'Succeeded') {
throw "Deployment '$deploymentName' did not succeed: $($deployment.ProvisioningState)"
}
& (Join-Path $PSScriptRoot 'Test-ContainerApp.ps1') -SubscriptionId $SubscriptionId `
-ResourceGroupName $ResourceGroupName -EnvironmentName $EnvironmentName -AppName $AppName
}
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."
Write-Warning "Resource group: https://portal.azure.com/#resource/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/overview"
throw
}
Loading
Loading