Edit

Azure Disk Encryption sample scripts

Important

Azure Disk Encryption is scheduled for retirement on September 15, 2028. Until that date, you can continue to use Azure Disk Encryption without disruption. On September 15, 2028, ADE-enabled workloads will continue to run, but encrypted disks will fail to unlock after VM reboots, resulting in service disruption.

Use encryption at host for new VMs, or consider Confidential VM sizes with OS disk encryption for confidential computing workloads. All ADE-enabled VMs (including backups) must migrate to encryption at host before the retirement date to avoid service disruption. See Migrate from Azure Disk Encryption to encryption at host for details.

Applies to: ✔️ Windows VMs

This article provides sample scripts for preparing pre-encrypted VHDs and other tasks.

Note

All scripts refer to the latest, non-AAD version of ADE, except where noted.

Sample PowerShell scripts for Azure Disk Encryption

  • List all encrypted VMs in your subscription

You can find all ADE-encrypted VMs and the extension version, in all resource groups present in a subscription, by using the Find_1passAdeVersion_VM.ps1 PowerShell script.

Alternatively, these cmdlets show all ADE-encrypted VMs (but not the extension version):

$osVolEncrypted = {(Get-AzVMDiskEncryptionStatus -ResourceGroupName $_.ResourceGroupName -VMName $_.Name).OsVolumeEncrypted}
$dataVolEncrypted= {(Get-AzVMDiskEncryptionStatus -ResourceGroupName $_.ResourceGroupName -VMName $_.Name).DataVolumesEncrypted}
Get-AzVm | Format-Table @{Label="MachineName"; Expression={$_.Name}}, @{Label="OsVolumeEncrypted"; Expression=$osVolEncrypted}, @{Label="DataVolumesEncrypted"; Expression=$dataVolEncrypted}
  • List all encrypted VMSS instances in your subscription

    You can find all ADE-encrypted virtual machine scale sets instances and the extension version, in all resource groups present in a subscription, by using the Find_1passAdeVersion_VMSS.ps1 PowerShell script.

  • List all disk encryption secrets used for encrypting VMs in a key vault

Get-AzKeyVaultSecret -VaultName $KeyVaultName | where {$_.Tags.ContainsKey('DiskEncryptionKeyFileName')} | format-table @{Label="MachineName"; Expression={$_.Tags['MachineName']}}, @{Label="VolumeLetter"; Expression={$_.Tags['VolumeLetter']}}, @{Label="EncryptionKeyURL"; Expression={$_.Id}}

Using the Azure Disk Encryption prerequisites PowerShell script

If you're already familiar with the prerequisites for Azure Disk Encryption, you can use the Azure Disk Encryption prerequisites PowerShell script. For an example of using this PowerShell script, see the Encrypt a VM Quickstart. You can remove the comments from a section of the script, starting at line 211, to encrypt all disks for existing VMs in an existing resource group.

The following table shows which parameters you can use in the PowerShell script:

Parameter Description Mandatory?
$resourceGroupName Name of the resource group to which the key vault belongs. The script creates a new resource group with this name if one doesn't exist. True
$keyVaultName Name of the key vault in which to place encryption keys. The script creates a new vault with this name if one doesn't exist. True
$location Location of the key vault. Ensure that the key vault and VMs to be encrypted are in the same location. Get a location list with Get-AzLocation. True
$subscriptionId Identifier of the Azure subscription to use. Get your Subscription ID with Get-AzSubscription. True
$aadAppName Name of the Microsoft Entra application that writes secrets to Key Vault. The script creates a new application with this name if one doesn't exist. If this app already exists, pass the aadClientSecret parameter to the script. False
$aadClientSecret Client secret of the Microsoft Entra application that is created earlier. False
$keyEncryptionKeyName Name of optional key encryption key in Key Vault. The script creates a new key with this name if one doesn't exist. False

Resource Manager templates

Encrypt or decrypt VMs without a Microsoft Entra app

Encrypt or decrypt VMs by using a Microsoft Entra app

Prepare a pre-encrypted Windows VHD

The following sections explain how to prepare a pre-encrypted Windows VHD for deployment as an encrypted VHD in Azure IaaS. Use this information to prepare and boot a fresh Windows VM (VHD) on Azure Site Recovery or Azure. For more information about how to prepare and upload a VHD, see Upload a generalized VHD and use it to create new VMs in Azure.

Update group policy to allow non-TPM for OS protection

Configure the BitLocker Group Policy setting BitLocker Drive Encryption, which you'll find under Local Computer Policy > Computer Configuration > Administrative Templates > Windows Components. Change this setting to Operating System Drives > Require additional authentication at startup > Allow BitLocker without a compatible TPM, as shown in the following figure:

Microsoft Antimalware in Azure

Install BitLocker feature components

For Windows Server 2012 and later, use the following command:

dism /online /Enable-Feature /all /FeatureName:BitLocker /quiet /norestart

For Windows Server 2008 R2, use the following command:

ServerManagerCmd -install BitLockers

Prepare the OS volume for BitLocker by using bdehdcfg

To compress the OS partition and prepare the machine for BitLocker, execute the bdehdcfg if needed:

bdehdcfg -target c: shrink -quiet

Protect the OS volume by using BitLocker

Use the manage-bde command to enable encryption on the boot volume by using an external key protector. Also place the external key (.bek file) on the external drive or volume. Encryption is enabled on the system/boot volume after the next reboot.

manage-bde -on %systemdrive% -sk [ExternalDriveOrVolume]
reboot

Note

Prepare the VM with a separate data/resource VHD for getting the external key by using BitLocker.

Upload encrypted VHD to an Azure storage account

After BitLocker encryption is enabled, the local encrypted VHD needs to be uploaded to your storage account.

    Add-AzVhd [-Destination] <Uri> [-LocalFilePath] <FileInfo> [[-NumberOfUploaderThreads] <Int32> ] [[-BaseImageUriToPatch] <Uri> ] [[-OverWrite]] [ <CommonParameters>]

Upload the secret for the pre-encrypted VM to your key vault

Upload the disk encryption secret that you got previously as a secret in your key vault. To do so, grant the set secret permission and the wrapkey permission to the account that uploads the secrets.

# Typically, account Id is the user principal name (in user@domain.com format)
$upn = (Get-AzureRmContext).Account.Id
Set-AzKeyVaultAccessPolicy -VaultName $kvname -UserPrincipalName $acctid -PermissionsToKeys wrapKey -PermissionsToSecrets set

# In cloud shell, the account ID is a managed service identity, so specify the username directly
# $upn = "user@domain.com"
# Set-AzKeyVaultAccessPolicy -VaultName $kvname -UserPrincipalName $acctid -PermissionsToKeys wrapKey -PermissionsToSecrets set

# When running as a service principal, retrieve the service principal ID from the account ID, and set access policy to that
# $acctid = (Get-AzureRmContext).Account.Id
# $spoid = (Get-AzureRmADServicePrincipal -ServicePrincipalName $acctid).Id
# Set-AzKeyVaultAccessPolicy -VaultName $kvname -ObjectId $spoid -BypassObjectIdValidation -PermissionsToKeys wrapKey -PermissionsToSecrets set

Disk encryption secret not encrypted with a KEK

To set up the secret in your key vault, use Set-AzKeyVaultSecret. The passphrase is encoded as a base64 string and then uploaded to the key vault. In addition, make sure that the following tags are set when you create the secret in the key vault.


 # This is the passphrase that was provided for encryption during the distribution installation
 $passphrase = "contoso-password"

 $tags = @{"DiskEncryptionKeyEncryptionAlgorithm" = "RSA-OAEP"; "DiskEncryptionKeyFileName" = "LinuxPassPhraseFileName"}
 $secretName = [guid]::NewGuid().ToString()
 $secretValue = [Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($passphrase))
 $secureSecretValue = ConvertTo-SecureString $secretValue -AsPlainText -Force

 $secret = Set-AzKeyVaultSecret -VaultName $KeyVaultName -Name $secretName -SecretValue $secureSecretValue -tags $tags
 $secretUrl = $secret.Id

Use the $secretUrl in the next step for attaching the OS disk without using a KEK.

Disk encryption secret encrypted with a KEK

Before you upload the secret to the key vault, you can optionally encrypt it by using a key encryption key. Use the wrap API to first encrypt the secret by using the key encryption key. The output of this wrap operation is a base64 URL encoded string, which you can then upload as a secret by using the Set-AzKeyVaultSecret cmdlet.

    # This is the passphrase that was provided for encryption during the distribution installation
    $passphrase = "contoso-password"

    Add-AzKeyVaultKey -VaultName $KeyVaultName -Name "keyencryptionkey" -Destination Software
    $KeyEncryptionKey = Get-AzKeyVaultKey -VaultName $KeyVault.OriginalVault.Name -Name "keyencryptionkey"

    $apiversion = "2015-06-01"

    ##############################
    # Get Auth URI
    ##############################

    $uri = $KeyVault.VaultUri + "/keys"
    $headers = @{}

    $response = try { Invoke-RestMethod -Method GET -Uri $uri -Headers $headers } catch { $_.Exception.Response }

    $authHeader = $response.Headers["www-authenticate"]
    $authUri = [regex]::match($authHeader, 'authorization="(.*?)"').Groups[1].Value

    Write-Host "Got Auth URI successfully"

    ##############################
    # Get Auth Token
    ##############################

    $uri = $authUri + "/oauth2/token"
    $body = "grant_type=client_credentials"
    $body += "&client_id=" + $AadClientId
    $body += "&client_secret=" + [Uri]::EscapeDataString($AadClientSecret)
    $body += "&resource=" + [Uri]::EscapeDataString("https://vault.azure.net")
    $headers = @{}

    $response = Invoke-RestMethod -Method POST -Uri $uri -Headers $headers -Body $body

    $access_token = $response.access_token

    Write-Host "Got Auth Token successfully"

    ##############################
    # Get KEK info
    ##############################

    $uri = $KeyEncryptionKey.Id + "?api-version=" + $apiversion
    $headers = @{"Authorization" = "Bearer " + $access_token}

    $response = Invoke-RestMethod -Method GET -Uri $uri -Headers $headers

    $keyid = $response.key.kid

    Write-Host "Got KEK info successfully"

    ##############################
    # Encrypt passphrase by using a KEK
    ##############################

    $passphraseB64 = [Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($Passphrase))
    $uri = $keyid + "/encrypt?api-version=" + $apiversion
    $headers = @{"Authorization" = "Bearer " + $access_token; "Content-Type" = "application/json"}
    $bodyObj = @{"alg" = "RSA-OAEP"; "value" = $passphraseB64}
    $body = $bodyObj | ConvertTo-Json

    $response = Invoke-RestMethod -Method POST -Uri $uri -Headers $headers -Body $body

    $wrappedSecret = $response.value

    Write-Host "Encrypted passphrase successfully"

    ##############################
    # Store secret
    ##############################

    $secretName = [guid]::NewGuid().ToString()
    $uri = $KeyVault.VaultUri + "/secrets/" + $secretName + "?api-version=" + $apiversion
    $secretAttributes = @{"enabled" = $true}
    $secretTags = @{"DiskEncryptionKeyEncryptionAlgorithm" = "RSA-OAEP"; "DiskEncryptionKeyFileName" = "LinuxPassPhraseFileName"}
    $headers = @{"Authorization" = "Bearer " + $access_token; "Content-Type" = "application/json"}
    $bodyObj = @{"value" = $wrappedSecret; "attributes" = $secretAttributes; "tags" = $secretTags}
    $body = $bodyObj | ConvertTo-Json

    $response = Invoke-RestMethod -Method PUT -Uri $uri -Headers $headers -Body $body

    Write-Host "Stored secret successfully"

    $secretUrl = $response.id

Use $KeyEncryptionKey and $secretUrl in the next step for attaching the OS disk by using a KEK.

Specify a secret URL when you attach an OS disk

Without using a KEK

While you attach the OS disk, pass $secretUrl. The URL is generated in the "Disk-encryption secret not encrypted with a KEK" section.

    Set-AzVMOSDisk `
            -VM $VirtualMachine `
            -Name $OSDiskName `
            -SourceImageUri $VhdUri `
            -VhdUri $OSDiskUri `
            -Windows `
            -CreateOption FromImage `
            -DiskEncryptionKeyVaultId $KeyVault.ResourceId `
            -DiskEncryptionKeyUrl $SecretUrl

Using a KEK

When you attach the OS disk, pass $KeyEncryptionKey and $secretUrl. The URL is generated in the "Disk encryption secret encrypted with a KEK" section.

    Set-AzVMOSDisk `
            -VM $VirtualMachine `
            -Name $OSDiskName `
            -SourceImageUri $CopiedTemplateBlobUri `
            -VhdUri $OSDiskUri `
            -Windows `
            -CreateOption FromImage `
            -DiskEncryptionKeyVaultId $KeyVault.ResourceId `
            -DiskEncryptionKeyUrl $SecretUrl `
            -KeyEncryptionKeyVaultId $KeyVault.ResourceId `
            -KeyEncryptionKeyURL $KeyEncryptionKey.Id