Azure Disk Encryption ARM template for Windows VM

I had an article about a healthy Windows virtual machine in Azure and got a feedback that the virtual machine should have disk encryption in place. That feedback is very valuable and it drove me to do more research and added disk encryption support into the template.

In this article, let’s look into disk encryption extension construct a little bit and a few notes around bundling it into an ARM template.

For those who don’t know about Azure Disk Encryption, this article is a very good start

If you work with Azure Security Center you will one-hundred-percent see OS disk encryption flagged in virtual machine recommendation.

You can get rid of the flag by enabling disk encryption either turning off policy in Azure Policy. This article is not going to recommend you to turn it off.

ARM Template Structure

Azure Disk Encryption can be enabled via Azure PowerShell or Azure CLI. That is normally seen in remediation. In a real-world scenario you would like to see a virtual machine during its creation include disk encryption process. This is technically possible thanks to Disk Encryption VM extension. Below is the sample extension schema for disk encryption for Windows VM:

    "variables": {
        "encryptionOperation": "EnableEncryption",
        "KeyEncryptionAlgorithm": "RSA-OAEP",
        "KeyVaultURL": "[concat('https://', parameters('keyVaultName'), '')]",
        "KeyVaultResourceId": "[concat(subscription().id,'/resourceGroups/',parameters('keyVaultRgName'),'/providers/Microsoft.KeyVault/vaults/', parameters('keyVaultName'))]",
        "KeyEncryptionKeyURL": "[concat('https://',parameters('keyVaultName'),'',parameters('keyName'),'/',parameters('keyVersion'))]",
        "VolumeType": "All"
        "type": "Microsoft.Compute/virtualMachines/extensions",
        "name": "[concat(parameters('vmName'),'/diskEncryption')]",
        "apiVersion": "2019-03-01",
        "location": "[parameters('location')]",
        "dependsOn": [
            "[resourceId('Microsoft.Compute/virtualMachines/', parameters('vmName'))]"
        "properties": {
            "publisher": "Microsoft.Azure.Security",
            "type": "AzureDiskEncryption",
            "typeHandlerVersion": "2.2",
            "autoUpgradeMinorVersion": true,
            "settings": {
                "EncryptionOperation": "[variables('encryptionOperation')]",
                "KeyEncryptionAlgorithm": "[variables('KeyEncryptionAlgorithm')]",
                "KeyVaultURL": "[variables('KeyVaultURL')]",
                "KeyEncryptionKeyURL": "[variables('KeyEncryptionKeyURL')]",
                "KeyVaultResourceId": "[variables('KeyVaultResourceId')]",
                "KekVaultResourceId": "[variables('KeyVaultResourceId')]",
                "VolumeType": "[variables('VolumeType')]"

Previously if you have worked with disk encryption extension you need to supply AAD Application (aka service principal object). In the newer version (2.2) you only need to specify key vault and key used for encryption.

Disk encryption support has been updated in healthy Windows virtual machine here.


As of this writing creating key is not supported in Azure ARM template so you cann’t bundle key vault creation in it. You must create a key prior to supplying it to disk encryption extension.

There would be a bug in Azure Portal reflecting to the state of the disk encryption. If you go to the virtual machine you can see both disks are encrypted. However, if you check status using Azure PowerShell or go to Disk it doesn’t show up.

Both OS and data disk are encrypted using BitLocker


Troubleshooting disk encryption extension deployment may be time consuming and may lead to hopeless. First, you need to know where to look at extension log (C:\WindowsAzure\Logs\Plugins\Microsoft.Azure.Security.AzureDiskEncryption\{version} ). BitlockerExtension.log gives you details in sequential process the extension runs before completing or throwing exception.

Let’s see an example from BitlockerExtension.log 

2019-12-28T07:17:23.4563096Z [Info]: SendEncryptionSettingsToHost Start
2019-12-28T07:17:23.4771457Z [Info]: SendEncryptionSettingsToHost diskEncryptionPostUri:


inner exception , stack trace: at Microsoft.Cis.Security.BitLocker.BitlockerIaasVMExtension.WireProtocol.WireProtocolMessage.SendEncryptionSettingsToHost(String diskEncryptionData) in X:\bt\1075670\repo\src\BitLocker\BitlockerIaasVMExtension\WireProtocol\WireProtocolMessage.cs:line 133
at Microsoft.Cis.Security.BitLocker.BitlockerIaasVMExtension.BitlockerExtension.SendEncryptionSettingsToHostV2(VmEncryptionSettings vmSettings) in X:\bt\1075670\repo\src\BitLocker\BitlockerIaasVMExtension\BitlockerExtension.cs:line 921
at Microsoft.Cis.Security.BitLocker.BitlockerIaasVMExtension.BitlockerExtension.SendEncryptionSettingsToHost() in X:\bt\1075670\repo\src\BitLocker\BitlockerIaasVMExtension\BitlockerExtension.cs:line 808
at Microsoft.Cis.Security.BitLocker.BitlockerIaasVMExtension.BitlockerExtension.GenerateAndUploadProtectors() in X:\bt\1075670\repo\src\BitLocker\BitlockerIaasVMExtension\BitlockerExtension.cs:line 1093
at Microsoft.Cis.Security.BitLocker.BitlockerIaasVMExtension.BitlockerExtension.EnableEncryption() in X:\bt\1075670\repo\src\BitLocker\BitlockerIaasVMExtension\BitlockerExtension.cs:line 1452
at Microsoft.Cis.Security.BitLocker.BitlockerIaasVMExtension.BitlockerExtension.HandleEncryptionOperations() in X:\bt\1075670\repo\src\BitLocker\BitlockerIaasVMExtension\BitlockerExtension.cs:line 1708
at Microsoft.Cis.Security.BitLocker.BitlockerIaasVMExtension.BitlockerExtension.OnEnable() in X:\bt\1075670\repo\src\BitLocker\BitlockerIaasVMExtension\BitlockerExtension.cs:line 1859

This kind of log looks ambiguous and may give a frustration until you know exactly what the function SendEncryptionSettingsToHost()  does. Doing  a bit of decompiling the extension utility, here is what it does:

    public static void SendEncryptionSettingsToHost(string diskEncryptionData)
      string empty = string.Empty;
      string str1 = string.Empty;
      uint num = 3;
      WireProtocolMessage.Logger.LogMessage("SendEncryptionSettingsToHost Start");
      string str2 = WireProtocolMessage.BuildDiskEncryptionDataUri();
      WireProtocolMessage.Logger.Log(Microsoft.WindowsAzure.GuestAgent.Plugins.Logging.LogLevel.Info, "SendEncryptionSettingsToHost diskEncryptionPostUri: {0}", (object) str2);
      for (uint index = 0; index < num; ++index)
        using (HttpClient httpClient = new HttpClient())
          httpClient.BaseAddress = new Uri(str2);
          httpClient.DefaultRequestHeaders.Add("x-ms-version", "2015-04-05");
          using (StringContent stringContent = new StringContent(diskEncryptionData, Encoding.UTF8, "text/xml"))
            using (HttpResponseMessage result = httpClient.PostAsync(str2, (HttpContent) stringContent).Result)
              str1 = result.Content.ReadAsStringAsync().Result;
              WireProtocolMessage.Logger.Log(Microsoft.WindowsAzure.GuestAgent.Plugins.Logging.LogLevel.Info, "SendEncryptionSettingsToHost responseContent:{0}, ReasonPhrase:{1}, StatusCode:{2}", (object) str1, (object) result.ReasonPhrase, (object) result.StatusCode);
              if (result.IsSuccessStatusCode)
                WireProtocolMessage.Logger.LogMessage("SendEncryptionSettingsToHost recieved successful diskEncryption response");
              WireProtocolMessage.Logger.Log(Microsoft.WindowsAzure.GuestAgent.Plugins.Logging.LogLevel.Info, "SendEncryptionSettingsToHost failed. Attempting again. retryCount: {0}", (object) index);
      throw new BitlockerFailedToSendEncryptionSettingsException(string.Format("Failed to send DiskEncryptionData, Check KeyVault inputs, ResourceIds and retry encryption operation:{0}", (object) str1));

So the extension utility sends a POST request including diskEncryptionData object to an Azure Instance Metadata Service at address There are three times of retry set in the function. So normally if you encounter HTTP request failure chances are your request body (diskEncryptionData) is in a wrong format.

You can trace back to see what is constructed to be diskEncryptionData object. In fact, it is the value in publicSetting property from setting file located in C:\Packages\Plugins\Microsoft.Azure.Security.AzureDiskEncryption\\RuntimeSettings

When the deployment of disk encryption extension starts, information is collected and is written to .setting file. You can go to check if reference of key vault ID or other variable’s values are correct.

This entry was posted in Secure Development and tagged , . Bookmark the permalink.

1 Response to Azure Disk Encryption ARM template for Windows VM

  1. Pingback: Deploy a healthy development Windows virtual machine - Microsoft Azure Security RandomnessMicrosoft Azure Security Randomness

Leave a Reply

Your email address will not be published.