In this blog post, I will walk you through the complete lifecycle of Customer-Managed Keys (CMK) for Confidential Azure Virtual Desktop session hosts. This is a follow-up to my earlier post on Building Confidential AVD Images with Azure Image Builder, where I introduced the difference between platform-managed and customer-managed keys. Here we go deeper: setting up the Key Vault and Disk Encryption Set with Bicep, understanding the key rotation constraint that is unique to Confidential VMs, alerting before a key expires, and knowing what actually happens to your session hosts if a key is revoked.
All source code including the new Bicep modules, scripts, and pipelines is available on GitHub.
⭐ yannickdils/confidentialavd
Bicep modules, PowerShell scripts, Azure DevOps pipelines, and Event Grid alerting for Confidential AVD
- Why customer-managed keys and when your auditors will ask for them
- The one CMK constraint unique to Confidential VMs that catches teams by surprise
- Key Vault setup with Bicep — including UAMI, private endpoint, and diagnostic settings
- Disk Encryption Set with the correct CVM-specific encryption type
- Automated alerting 30 days before your key expires via Event Grid and Azure Monitor
- The full 9-step offline rotation procedure with a PowerShell script that handles everything
- What actually happens to your session hosts when a key is revoked
Why customer-managed keys?
When you deploy a Confidential VM without a Disk Encryption Set, Azure uses platform-managed keys (PMK) to protect the OS disk and VM guest state. Microsoft generates and manages these keys, and they never leave the hardware security boundary of the AMD SEV-SNP processor. That is already a big step up from a standard VM, but for regulated workloads the question you will hear from your auditors is: who controls the key material?
With customer-managed keys (CMK) the answer is you. Your organisation creates the key in an Azure Key Vault that you own and control. The Disk Encryption Set (DES) holds a reference to that key and uses it to wrap the data encryption key (DEK) on your disk. Microsoft’s storage service cannot read your disk without going through your Key Vault, and you can revoke access at any time.
For healthcare, financial services, and government workloads this is often a hard compliance requirement rather than a nice-to-have.

Figure 1 – The four encryption layers in a Confidential AVD session host with customer-managed keys
How the encryption layers fit together
Before writing any Bicep it helps to understand the chain of trust that CMK adds on top of Confidential VM encryption.
- AMD SEV-SNP hardware encrypts the VM’s memory using keys held inside the CPU. Even the hypervisor cannot read it.
- vTPM measures the boot chain and seals the VM guest state. Tampering with the boot loader is detectable.
- Confidential OS disk encryption encrypts the disk at rest. The encryption type
DiskWithVMGuestStatecovers both the disk content and the VM guest state (UEFI variables, vTPM state).VMGuestStateOnlyonly covers the guest state and uses PMK for the disk. - Customer-managed key wraps the data encryption key used for the OS disk. Your Key Vault is the root of trust.
The Disk Encryption Set sits between your Key Vault and the managed disk. It holds the reference to your key, and the managed disk service uses the DES’s User Assigned Managed Identity to call Key Vault and wrap or unwrap the DEK when data is read or written.
What is different about CMK for Confidential VMs?
There is one important constraint you need to know before you start: automatic key rotation is not supported for Confidential VMs. For standard managed disks you can enable auto-rotation on the DES and the system re-encrypts the DEK with the new key version in the background without rebooting the VM. For Confidential VMs this path does not exist.
Even with a rotation policy set on the key, the DES update and VM drain/deallocate sequence still need to happen manually. The rotation policy in this post is intentionally set to Notify only, not Rotate, so that we get the Event Grid alert without an automatic key version change that the VMs are not ready for.
Every VM sharing the same Disk Encryption Set must be stopped and deallocated before the DES can be updated to a new key version. If even one VM is still running, none of the VMs will receive the new key. This has direct operational implications for AVD: you need to drain the host pool, wait for or force logoff of active user sessions, deallocate the VMs, rotate the key, and then start everything back up.
That is why the rotation PowerShell script in this post handles the full 9-step sequence rather than just updating a key URL.
Building the Key Vault with Bicep

Figure 2 – Key Vault, Disk Encryption Set, and Confidential VM session host resource relationships
The CMK Key Vault module at ComponentLibrary/KeyVault/CMK/main.bicep deploys everything needed in a single Bicep file: the Key Vault itself, the User Assigned Managed Identity for the DES, the private endpoint, role assignments, the RSA key with rotation policy, and optional diagnostic settings. You can review the key parts here, or view the full module on GitHub:
📄 ComponentLibrary/KeyVault/CMK/main.bicep
Key Vault module for CMK – includes UAMI, private endpoint, role assignments, RSA key, and diagnostic settings
A few things worth highlighting from the updated module. The UAMI for the DES is now created inside the same module rather than being a separate deployment, which keeps everything in one place and makes the outputs straightforward to wire into the DES stage of the pipeline. The private endpoint is fully configured including the Private DNS Zone Group, so the DES and pipeline agents can reach the vault without any public internet exposure. The logAnalyticsWorkspaceId parameter is optional: if you leave it empty, the diagnostic settings resource is skipped entirely.
enablePurgeProtection: true cannot be disabled once set on a Key Vault. Azure enforces this as a hard requirement for managed disk CMK. Make sure the vault name is correct before deploying, because you cannot delete and recreate a vault with the same name during the soft-delete retention window.Disk Encryption Set for Confidential VMs
The Disk Encryption Set module at ComponentLibrary/DiskEncryptionSet/cmk-kv.bicep is intentionally minimal. It receives the UAMI resource ID and versionless key URI as inputs from the Key Vault stage, and outputs the DES resource ID for the session host deployment. You can review the key parts here, or view the full module on GitHub:
📄 ComponentLibrary/DiskEncryptionSet/cmk-kv.bicep
DES module with ConfidentialVmEncryptedWithCustomerKey encryption type for standard Key Vault
The keyVaultKeyUri accepts the versionless URI output from the Key Vault stage (for example https://kv-avd-cmk-prd-weu-001.vault.azure.net/keys/cmk-avd-prd-weu-001). Using the versionless URI means the DES resolves to the current active key version at the time it wraps the DEK. The pipeline passes this automatically via the stage dependency outputs.
Deploying with the AVD-DeployCMK pipeline
The Pipelines/AVD-DeployCMK.yml pipeline wires the Key Vault and DES modules together with a proper stage dependency chain so you never have to manually copy outputs between deployments.
📄 Pipelines/AVD-DeployCMK.yml
Azure DevOps pipeline – four stages with approval gate, What-If preview, and wired stage outputs
- Approval gate – a human sign-off is required before any change is made. CMK infrastructure changes affect all session hosts sharing the DES.
- Deploy CMK Key Vault – runs a What-If preview first, then deploys the Key Vault module. Outputs the Key Vault ID, versionless key URI, and DES identity ID as pipeline variables for the next stage.
- Deploy Disk Encryption Set – picks up the Key Vault outputs via
stageDependenciesand deploys the DES. Outputs the DES resource ID. - Deploy Alerts – deploys the Event Grid system topic and Azure Monitor alert rule. Controlled by the
deployAlertsparameter and skipped if set to false.
The DES resource ID from a successful run is what you set in your hostpool config JSON so the AVD-DeployAdditionalHosts pipeline can pick it up when provisioning new session hosts.
Referencing the DES in session host deployments
Nothing changes in the session host Bicep itself. The existing avd-sessionhosts-cc.bicep already accepts a diskEncryptionSetId parameter and sets securityEncryptionType to DiskWithVMGuestState when a DES ID is provided. With CMK in place the session host deployment is identical to what was covered in the first blog post, the only difference is that the DES now points to your Key Vault key rather than to a Managed HSM key.
Alerting before the key expires

Figure 3 – Event Grid, Azure Monitor, and Action Group wired together for CMK key expiry alerting
Because auto-rotation is not available for CVMs, you must rotate the key manually before it expires. If the key expires and you have not rotated, your session hosts will fail to start after they are next deallocated. Running hosts are not immediately affected, but the moment a host is stopped for any reason (patching, scaling, a crash), it will not come back up until the key situation is resolved.
The alerting module at ComponentLibrary/EventGrid/cmk-key-expiry-alert.bicep deploys the full alerting stack in one shot: the Action Group with email receivers (and an optional webhook), the Event Grid System Topic scoped to your Key Vault, and the Azure Monitor alert rule. You pass in the email addresses as a parameter array and the module wires everything together.
📄 ComponentLibrary/EventGrid/cmk-key-expiry-alert.bicep
Full alerting stack – Action Group, Event Grid System Topic, and Azure Monitor alert in one module
We subscribe to three event types. KeyNearExpiry fires 30 days out and is your main trigger to start planning the rotation window. KeyExpired fires if rotation was missed and acts as a critical escalation. KeyNewVersionCreated confirms a successful rotation. The retry policy keeps the event in the queue for up to 24 hours if the alert destination is temporarily unavailable.
KeyNearExpiry for keys that have an expiration date set. Keys created without an expiry date are silent. The CMK key Bicep module always sets an expiry via the rotation policy, so this is handled automatically if you use the module as written. If you have an existing key without an expiry date, add one in Key Vault before relying on this alerting.Rotating the key: the full procedure

Figure 4 – The 9-step offline CMK key rotation procedure for Confidential AVD session hosts
When you receive the near-expiry alert, the rotation sequence is:
- Validate pre-requisites: Azure CLI session, access to the DES, host pool, and key source.
- Discover all session host VMs in the resource group whose OS disk references the target DES.
- Enable drain mode on the AVD host pool so no new sessions start on the affected hosts.
- Wait for active user sessions to end, or force logoff after a configured timeout.
- Deallocate all session host VMs — and wait for confirmed
Deallocatedstate before proceeding. - Create a new key version (Key Vault:
az keyvault key rotate, Managed HSM:az keyvault key create). - Update the DES to reference the new key URL and verify the active key URL updated correctly.
- Start all session host VMs and wait for confirmed
Runningstate. - Disable drain mode so the host pool accepts new user sessions again.
The script at Scripts/Rotate-CMK.ps1 handles all nine steps in sequence. Let me walk through the important parts.
To run the rotation for a standard Key Vault deployment:
📄 Scripts/Rotate-CMK.ps1
Full 9-step rotation script with VM discovery, drain mode, deallocation polling, key rotation, restart polling, and safety rollback
For Managed HSM keys substitute -HsmName for -KeyVaultName. Use -ForceLogoff to disconnect active sessions automatically after the drain timeout instead of aborting. Always run with -DryRun first — the script will validate all resources and show exactly what it would do without making any changes. If the session drain times out without -ForceLogoff, the script automatically re-enables new sessions on the host pool before exiting so users are not left in a degraded state.
What happens when a key is revoked

Figure 5 – Impact and recovery path when CMK access is removed from the Disk Encryption Set
Revoking a CMK is done by disabling the key in Key Vault or removing the role assignment that grants the DES identity access. The effect is not instantaneous but it is serious:
- Running session hosts are not immediately affected. The DEK is cached in the storage layer and existing disk I/O continues. Once the cache expires (typically within minutes to hours) new disk reads and writes will fail.
- Deallocated session hosts will not start. The storage service calls Key Vault to unwrap the DEK at VM start time. If Key Vault returns an access denied error, the VM power-on fails with an allocation error.
- New session host deployments using the affected DES will fail at the disk attachment stage.
To recover from an accidental revocation: restore the role assignment or re-enable the key in Key Vault. Because purge protection is enabled, soft-deleted keys remain recoverable for 90 days. After restoring access the storage service will retry the key operations automatically.
This behaviour is also the mechanism used for emergency data destruction: if you need to make disks permanently unreadable, purge the key from Key Vault after the soft-delete retention period and the encrypted data on the disks becomes cryptographically inaccessible. No data was overwritten and the VMs still exist, but the DEK can never be unwrapped again.
Repository changes summary
Here is what is new or updated in the repository after this post:
📄 ComponentLibrary/KeyVault/CMK/main.bicep
Key Vault module – includes UAMI, private endpoint, role assignments, RSA key with notify-only rotation policy, and optional diagnostic settings
📄 ComponentLibrary/KeyVault/CMK/main.parameters.json
Parameters file – fill in keyVaultName, pepSubnetId, privateDnsZoneId, and tags for your environment
📄 ComponentLibrary/DiskEncryptionSet/cmk-kv.bicep
DES module with ConfidentialVmEncryptedWithCustomerKey encryption type and expanded comments on rotation behaviour
📄 ComponentLibrary/EventGrid/cmk-key-expiry-alert.bicep
Full alerting stack – deploys Action Group with email/webhook receivers, Event Grid System Topic, and Azure Monitor alert rule
📄 ComponentLibrary/EventGrid/cmk-key-expiry-alert.parameters.json
Alert parameters – set notificationEmails array and optionally a webhookUri for pipeline automation
📄 Scripts/Rotate-CMK.ps1
Full 9-step rotation script with VM discovery, drain mode, deallocation/startup polling, safety rollback, and -DryRun support
📄 Pipelines/AVD-DeployCMK.yml
Four-stage pipeline with approval gate, What-If preview, and wired stage outputs for Key Vault ID, key URI, and DES ID
Operational checklist

Figure 6 – Six things to put in your runbook before going to production with CMK
- Set an expiry date on every key. Keys without an expiry date do not trigger Event Grid near-expiry events. The Bicep module sets this automatically via the rotation policy.
- Test the rotation procedure in a non-production host pool first. The 9-step drain and deallocate sequence has user impact. Run with
-DryRunto validate all resources before your first live rotation. - Plan your rotation window. With a one-year key expiry and 30-day notification you have a full month to schedule a maintenance window. Do not rotate on the expiry date itself.
- Never delete a key version immediately after rotation. Wait until all VMs are confirmed running and healthy. Rely on the 90-day soft-delete retention rather than manual deletion.
- Store the DES ID in your hostpool config JSON. The
AVD-DeployCMKpipeline outputs the DES resource ID. Add it to your hostpool JSON so new session hosts automatically inherit CMK on deployment. - Add notification emails in the alert parameters file before deploying. The
notificationEmailsarray incmk-key-expiry-alert.parameters.jsonis where you set who gets the 30-day warning. Do not leave it pointing at placeholder addresses.
References
- Rotate customer-managed keys for Azure confidential VMs – Microsoft Learn
- Server-side encryption of Azure Managed Disks – Microsoft Learn
- Monitoring Key Vault with Azure Event Grid – Microsoft Learn
- About Azure confidential VMs – Microsoft Learn
- Building Confidential AVD Images with Azure Image Builder – tunecom.be
- Thomas Van Laere – Confidential Computing series
1 Comment on “Customer-Managed Keys for Confidential AVD: A Complete Lifecycle Guide”