Customer-Managed Keys for Confidential AVD: A Complete Lifecycle Guide

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

📋 TL;DR: What you’ll learn

  • 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.

  1. AMD SEV-SNP hardware encrypts the VM’s memory using keys held inside the CPU. Even the hypervisor cannot read it.
  2. vTPM measures the boot chain and seals the VM guest state. Tampering with the boot loader is detectable.
  3. Confidential OS disk encryption encrypts the disk at rest. The encryption type DiskWithVMGuestState covers both the disk content and the VM guest state (UEFI variables, vTPM state). VMGuestStateOnly only covers the guest state and uses PMK for the disk.
  4. 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:

Bicep – KeyVault/CMK/main.bicep (key sections)
@description('Name of the Key Vault to create.')
param keyVaultName string

@description('Name of the User Assigned Managed Identity that the Disk Encryption Set will use.')
param diskEncryptionSetIdentityName string

@description('Name of the CMK key to create inside the Key Vault.')
param keyName string

@description('RSA key size. 3072 or 4096 recommended for CVM workloads.')
@allowed([2048, 3072, 4096])
param keySize int = 3072

@description('Key rotation period as ISO 8601 duration (e.g. P1Y = 1 year).')
param keyRotationPeriod string = 'P1Y'

@description('Days before key expiry at which to send a notification via Event Grid.')
param keyExpiryNotificationDays int = 30

@description('Subnet Resource ID for the Key Vault private endpoint.')
param pepSubnetId string

@description('Resource ID of the privatelink.vaultcore.azure.net Private DNS Zone.')
param privateDnsZoneId string

@description('Optional Log Analytics Workspace ID for diagnostic settings.')
param logAnalyticsWorkspaceId string = ''

// User Assigned Managed Identity for the Disk Encryption Set
resource desIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
  name: diskEncryptionSetIdentityName
  location: location
  tags: tags
}

// Key Vault - Premium SKU for HSM-backed keys
// soft-delete + purge protection are MANDATORY for managed disk CMK
resource keyVault 'Microsoft.KeyVault/vaults@2023-07-01' = {
  name: keyVaultName
  location: location
  tags: tags
  properties: {
    sku: { family: 'A', name: 'premium' }
    tenantId: tenant().tenantId
    enableRbacAuthorization: true
    enableSoftDelete: true
    softDeleteRetentionInDays: 90
    enablePurgeProtection: true  // Required for managed disk CMK - cannot be disabled once set
    enabledForDiskEncryption: true
    publicNetworkAccess: 'Disabled'
    networkAcls: {
      bypass: 'AzureServices'
      defaultAction: 'Deny'
    }
  }
}

// Private endpoint - keeps all traffic off the public internet
resource pepNic 'Microsoft.Network/privateEndpoints@2023-09-01' = {
  name: '${keyVaultName}-pep'
  location: location
  tags: tags
  properties: {
    subnet: { id: pepSubnetId }
    privateLinkServiceConnections: [
      {
        name: '${keyVaultName}-plsc'
        properties: {
          privateLinkServiceId: keyVault.id
          groupIds: ['vault']
        }
      }
    ]
  }
}

// Crypto Service Encryption User - DES identity needs this to wrap/unwrap DEK at runtime
var cryptoServiceEncryptionUserRoleId = 'e147488a-f6f5-4113-8e2d-b22465e65bf6'

resource desIdentityCryptoUser 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
  name: guid(keyVault.id, desIdentity.id, cryptoServiceEncryptionUserRoleId)
  scope: keyVault
  properties: {
    roleDefinitionId: subscriptionResourceId('Microsoft.Authorization/roleDefinitions', cryptoServiceEncryptionUserRoleId)
    principalId: desIdentity.properties.principalId
    principalType: 'ServicePrincipal'
  }
}

// RSA key with notification-only rotation policy
// type: 'Notify' - do NOT use 'Rotate'. CVM does not support automatic rotation.
// The Notify action fires the KeyNearExpiry Event Grid event for our alerting pipeline.
resource cmkKey 'Microsoft.KeyVault/vaults/keys@2023-07-01' = {
  name: keyName
  parent: keyVault
  tags: tags
  properties: {
    kty: 'RSA'
    keySize: keySize
    keyOps: ['wrapKey', 'unwrapKey']
    rotationPolicy: {
      attributes: { expiryTime: keyRotationPeriod }
      lifetimeActions: [
        {
          action: { type: 'Notify' }
          trigger: { timeBeforeExpiry: 'P${keyExpiryNotificationDays}D' }
        }
      ]
    }
  }
  dependsOn: [desIdentityCryptoUser]
}

output keyVaultId string = keyVault.id
output cmkKeyVersionlessUri string = '${keyVault.properties.vaultUri}keys/${keyName}'
output desIdentityId string = desIdentity.id

📄 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.

⚠️ Important: 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:

Bicep – DiskEncryptionSet/cmk-kv.bicep
@description('Resource ID of the User Assigned Managed Identity with Key Vault Crypto Service Encryption User role.')
param userAssignedIdentityId string

@description('The versionless key URI from Key Vault (e.g. https://.vault.azure.net/keys/).')
param keyVaultKeyUri string

// encryptionType must be ConfidentialVmEncryptedWithCustomerKey for CVM.
// This is different from EncryptionAtRestWithCustomerKey used by standard managed disks.
// rotationToLatestKeyVersionEnabled: false because CVM does NOT support automatic key rotation.
// The DES update and VM drain/deallocate sequence must happen manually (see Rotate-CMK.ps1).
resource diskEncryptionSet 'Microsoft.Compute/diskEncryptionSets@2025-01-02' = {
  name: diskEncryptionSetName
  location: location
  tags: tags
  identity: {
    type: 'UserAssigned'
    userAssignedIdentities: {
      '${userAssignedIdentityId}': {}
    }
  }
  properties: {
    activeKey: {
      keyUrl: keyVaultKeyUri
    }
    encryptionType: 'ConfidentialVmEncryptedWithCustomerKey'
    rotationToLatestKeyVersionEnabled: false
  }
}

output diskEncryptionSetId string = diskEncryptionSet.id

📄 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

  1. Approval gate – a human sign-off is required before any change is made. CMK infrastructure changes affect all session hosts sharing the DES.
  2. 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.
  3. Deploy Disk Encryption Set – picks up the Key Vault outputs via stageDependencies and deploys the DES. Outputs the DES resource ID.
  4. Deploy Alerts – deploys the Event Grid system topic and Azure Monitor alert rule. Controlled by the deployAlerts parameter 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.

⚠️ Important: The DES, the Key Vault, and all session host VMs must be in the same Azure region and the same subscription. The Key Vault can technically be in a different subscription but must be in the same Entra tenant and the same region as the DES. Plan your resource group structure accordingly before deploying.

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.

Bicep – EventGrid/cmk-key-expiry-alert.bicep (key sections)
@description('Email addresses to notify when the CMK key is approaching expiry.')
param notificationEmails array

@description('Optional webhook URI (e.g. Logic App, Power Automate). Leave empty to skip.')
param webhookUri string = ''

// Action Group - email receivers built from the notificationEmails array
resource actionGroup 'Microsoft.Insights/actionGroups@2023-01-01' = {
  name: actionGroupName
  location: 'global'
  tags: tags
  properties: {
    groupShortName: actionGroupShortName
    enabled: true
    emailReceivers: [
      for (email, i) in notificationEmails: {
        name: 'cmk-expiry-email-${i}'
        emailAddress: email
        useCommonAlertSchema: true
      }
    ]
    webhookReceivers: !empty(webhookUri) ? [
      {
        name: 'cmk-expiry-webhook'
        serviceUri: webhookUri
        useCommonAlertSchema: true
      }
    ] : []
  }
}

// Event Grid System Topic scoped to the Key Vault
resource eventGridTopic 'Microsoft.EventGrid/systemTopics@2023-12-15-preview' = {
  name: eventGridTopicName
  location: location
  tags: tags
  properties: {
    source: keyVaultId
    topicType: 'Microsoft.KeyVault.vaults'
  }
}

// Event subscription - routes KeyNearExpiry, KeyExpired, KeyNewVersionCreated to Azure Monitor
resource nearExpiryEventSubscription 'Microsoft.EventGrid/systemTopics/eventSubscriptions@2023-12-15-preview' = {
  name: 'sub-cmk-key-near-expiry'
  parent: eventGridTopic
  properties: {
    eventDeliverySchema: 'CloudEventSchemaV1_0'
    filter: {
      includedEventTypes: [
        'Microsoft.KeyVault.KeyNearExpiry'
        'Microsoft.KeyVault.KeyExpired'
        'Microsoft.KeyVault.KeyNewVersionCreated'
      ]
    }
    destination: {
      endpointType: 'AzureMonitorAlert'
      properties: { resourceId: nearExpiryAlert.id }
    }
    retryPolicy: {
      maxDeliveryAttempts: 30
      eventTimeToLiveInMinutes: 1440
    }
  }
}

📄 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.

💡 Quick tip: Event Grid only fires 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:

  1. Validate pre-requisites: Azure CLI session, access to the DES, host pool, and key source.
  2. Discover all session host VMs in the resource group whose OS disk references the target DES.
  3. Enable drain mode on the AVD host pool so no new sessions start on the affected hosts.
  4. Wait for active user sessions to end, or force logoff after a configured timeout.
  5. Deallocate all session host VMs — and wait for confirmed Deallocated state before proceeding.
  6. Create a new key version (Key Vault: az keyvault key rotate, Managed HSM: az keyvault key create).
  7. Update the DES to reference the new key URL and verify the active key URL updated correctly.
  8. Start all session host VMs and wait for confirmed Running state.
  9. 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.

PowerShell – Rotate-CMK.ps1 (VM discovery and drain)
# STEP 2 - Discover session host VMs using the target DES
$desId = az disk-encryption-set show `
    --name $DiskEncryptionSetName `
    --resource-group $DiskEncryptionSetResourceGroup `
    --query "id" -o tsv

$allVms = (az vm list --resource-group $HostsResourceGroup -o json | ConvertFrom-Json)
$targetVms = $allVms | Where-Object {
    $_.storageProfile.osDisk.managedDisk.diskEncryptionSet.id -eq $desId
}

# STEP 3 - Enable drain mode on each session host
foreach ($sh in $sessionHosts) {
    $shBody = @{ properties = @{ allowNewSession = $false } } | ConvertTo-Json -Depth 5
    $shBody | Out-File $shTempFile -Encoding UTF8
    az rest --method PATCH --uri $shUri --body "@$shTempFile" | Out-Null
}

# STEP 4 - Wait for sessions or force logoff after timeout
while ((Get-Date) -lt $deadline) {
    $sessions = ... | Where-Object { $_.properties.sessionState -eq 'Active' }
    if ($sessions.Count -eq 0) { break }
    Start-Sleep -Seconds 60
}

# STEP 5 - Deallocate and wait for confirmed Deallocated state
foreach ($vm in $targetVms) {
    az vm deallocate --resource-group $HostsResourceGroup --name $vm.name --no-wait
}
# Polls every 30 seconds until all VMs reach 'VM deallocated'
PowerShell – Rotate-CMK.ps1 (key rotation, DES update, restart)
# STEP 6 - Create new key version
# Standard Key Vault:
$rotateJson = az keyvault key rotate --vault-name $KeyVaultName --name $KeyName
$newKeyUrl = ($rotateJson | ConvertFrom-Json).key.kid

# Managed HSM (use -HsmName instead of -KeyVaultName):
# az keyvault key create --hsm-name $HsmName --name $KeyName --kty RSA-HSM ...

# STEP 7 - Update DES and verify
az disk-encryption-set update `
    --name $DiskEncryptionSetName `
    --resource-group $DiskEncryptionSetResourceGroup `
    --key-url $newKeyUrl

# STEP 8 - Start VMs and wait for confirmed Running state
foreach ($vm in $targetVms) {
    az vm start --resource-group $HostsResourceGroup --name $vm.name --no-wait
}
# Polls every 30 seconds until all VMs reach 'VM running'

# STEP 9 - Re-enable new sessions on every session host
foreach ($sh in $sessionHosts) {
    $shBody = @{ properties = @{ allowNewSession = $true } } | ConvertTo-Json -Depth 5
    az rest --method PATCH --uri $shUri --body "@$shTempFile" | Out-Null
}

To run the rotation for a standard Key Vault deployment:

PowerShell – run Rotate-CMK.ps1
.\Scripts\Rotate-CMK.ps1 `
    -SubscriptionName "sub-avd-prd" `
    -HostPoolResourceGroup "rg-avd-prd-hp" `
    -HostPoolName "hp-avd-prd-weu-001" `
    -HostsResourceGroup "rg-avd-prd-hosts" `
    -DiskEncryptionSetName "des-avd-prd-cmk-weu-001" `
    -DiskEncryptionSetResourceGroup "rg-avd-prd-cmk" `
    -KeyVaultName "kv-avd-cmk-prd-weu-001" `
    -KeyName "cmk-avd-prd-weu-001"

📄 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.

⚠️ Important: Do not delete the old key version from Key Vault immediately after rotation. The storage service re-encrypts the DEK with the new key version in the background. Until that re-encryption completes, the old key version must still be accessible. The soft-delete retention of 90 days protects you here even if someone attempts to delete the old version manually.

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

  1. 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.
  2. Test the rotation procedure in a non-production host pool first. The 9-step drain and deallocate sequence has user impact. Run with -DryRun to validate all resources before your first live rotation.
  3. 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.
  4. 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.
  5. Store the DES ID in your hostpool config JSON. The AVD-DeployCMK pipeline outputs the DES resource ID. Add it to your hostpool JSON so new session hosts automatically inherit CMK on deployment.
  6. Add notification emails in the alert parameters file before deploying. The notificationEmails array in cmk-key-expiry-alert.parameters.json is where you set who gets the 30-day warning. Do not leave it pointing at placeholder addresses.

References

1 Comment on “Customer-Managed Keys for Confidential AVD: A Complete Lifecycle Guide

Leave a Reply

Your email address will not be published. Required fields are marked *

Please reload

Please Wait