Note – Update 2018-11-13: The Azure team has released a new disk encryption method that is much less complex! You can read more about it here: https://docs.microsoft.com/en-us/azure/security/azure-security-disk-encryption-overview
This post will walk you through the configuration necessary to encrypt a virtual machine’s hard drives in Azure. This post applies only to Windows VMs running in Microsoft Azure; the process is different for Linux VMs.
I should note that this does not make use of a key-encrypting key, which may be important to you.
A huge shout-out to The Cloud Ranger for providing a script for me to disassemble and provide in a guide.
- Build a virtual machine and supporting infrastructure. This is pretty quick to do with the Azure template here, or you can follow along with the PowerShell instructions below: https://github.com/Azure/azure-quickstart-templates/tree/master/101-vm-simple-windows
- Build an Azure Key Vault (using PowerShell)
- Create an Azure AD Service Principal and assign it permissions
- Enable Encryption on the VM
You need the Azure PowerShell cmdlets installed to do this. If you are on Windows 10, installing the cmdlets is super-easy: open an administrative PowerShell window and type:
This will install the Azure modules. If you’re on Windows 7, you need to download and install from this link: https://azure.microsoft.com/en-us/documentation/articles/powershell-install-configure/
Next, log into your Azure account:
Set up some variables we’ll use later.
$resourceGroupName = <enter the name of the resource group you want to use> $location = <enter the location of the resource group> $storageaccountname = <the name of the storage account> $storageaccounttype = "Standard_LRS" $vmName = "encryptedVM"
If your resource group does not already exist, run
New-AzureRmResourceGroup -name $resourcegroupName -location $location
Likewise, if your storage account does not already exist, run
New-AzureRmStorageAccount -Name $storageaccountname -ResourceGroupName $resourcegroupName –Type $storageaccounttype -Location $location
Next, we need to prepare the virtual network.
Create the subnet:
$subnet1 = New-AzureRmVirtualNetworkSubnetConfig -Name “Subnet-1” -AddressPrefix 10.1.1.0/24
and create the virtual network:
$vnet = New-AzureRmVirtualNetwork -Name “labVNET” -ResourceGroupName $resourcegroupName -Location $location -AddressPrefix 10.1.0.0/16 -Subnet $subnet1
Next, we need to create a virtual Network Interface Card (NIC) that will be part of the VM and connect to the network we just created:
$nicName = “encryptedVMNIC”
Create a public IP for this VM, so we can RDP into it:
$pipName = $nickname + "pip" $pip = New-AzureRmPublicIpAddress -Name $pipName -ResourceGroupName $resourcegroupName -Location $location -AllocationMethod Dynamic
And now we feed these settings into the Network Card:
$nic = New-AzureRmNetworkInterface -Name $nicName -ResourceGroupName $resourcegroupName -Location $location -SubnetId $vnet.Subnets.Id -PublicIpAddressId $pip.Id
Now we proceed to setting up the VM Configuration itself, referencing the stuff we’ve already built. First, gather the local administrator credentials (noting, of course, that the login name can’t be admin, administrator, or root, and the password must be over 12 characters)
$cred = Get-Credential -Message “Type the name and password of the local administrator account.”
Next, we configure the virtual hard disk file. This file will be created based on the latest Windows Server 2012 R2 image, which Microsoft makes available. We set up a name, then indicate where the VM will be stored using some fancy PowerShell manipulation:
$vmosdiskname = $vmName + "OSDisk" $vmosDiskUri = (Get-AzureRmStorageAccount -ResourceGroupName $resourcegroupName -Name $storageAccountName).PrimaryEndpoints.Blob.ToString() + "vhds/" + $vmosdiskname + ".vhd"
Now we configure the VM size
$vm = New-AzureRmVMConfig -VMName $vmName -VMSize “Standard_A2”
And the operating system configuration:
$vm = Set-AzureRmVMOperatingSystem -VM $vm -Windows -ComputerName $vmName -Credential $cred -ProvisionVMAgent
Select the source image, which is published by Microsoft, is Windows Server with the SKU 2012 R2 Datacenter, and the latest version:
$vm = Set-AzureRmVMSourceImage -VM $vm -PublisherName MicrosoftWindowsServer -Offer WindowsServer -Skus 2012-R2-Datacenter -Version “latest”
Add the virtual network interface that we created earlier
$vm = Add-AzureRmVMNetworkInterface -VM $vm -Id $nic.Id
Reference the disk that we created earlier, and indicate it is a generalized image:
$vm = Set-AzureRmVMOSDisk -VM $vm -Name $vmosdiskname -VhdUri $vmosDiskUri -CreateOption fromImage
And now that we’ve populated the VM Image, we view the configuration:
And now we can tell Azure to create the VM for us:
New-AzureRmVM -ResourceGroupName $resourcegroupname -Location $location -VM $vm
Prior to encrypting the VM, there’s some prerequisites that are required. The Key Vault stores the decryption key for Bitlocker. The Service Principal is used to log in to the Key Vault to store the key for the VM.
First, we need to set up some variables. If you’ve already set up some of these, you can skip them.
$resourceGroupname = <enter the name of the resource group you want to use> $location = <enter the location of the resource group> $storageaccountname = <the name of the storage account> $storageaccounttype = <the type of storage account> $vmName = "myTestVM" #These variables are unique to this section: $aadAppName = "KeyVaultADApp" $defaultHomePage = "https://aad.romyn.ca" $identifierUri = "https://aad.romyn.ca" $aadClientSecret = "Use a good password, please"
Creating the Key Vault is pretty straightforward:
$vaultname = <Name of your key vault> New-AzureRmKeyVault -VaultName $vaultname -ResourceGroupName $resourcegroupname -Location $location
Then, in order to allow the encryption to function, we need to indicate that it is enabled for disk encryption:
Set-AzureRmKeyVaultAccessPolicy -VaultName $vaultname -ResourceGroupName $resourcegroupname -EnabledForDiskEncryption
$AADApp = New-AzureRmADApplication -DisplayName $aadAppName -HomePage $defaultHomePage -IdentifierUris $identifierUri -Password $aadClientSecret $servicePrincipal = New-AzureRmADServicePrincipal -ApplicationId $AADApp.ApplicationId $aadClientID = $servicePrincipal.ApplicationId
Next, we collect some important variables – these could be embedded in-line but this helps keep my head straight, and as a general coding guideline it’s best to set up variables rather than calculate it in-line, in most cases:
$SvcPrincipals = (Get-AzureRmADServicePrincipal -SearchString $aadAppName) $aadClientID = $servicePrincipal.ApplicationId $diskencryptionVaultID = (Get-AzureRmKeyVault -VaultName $vaultname).resourceID $diskencryptionVaulturl = (get-AzurermKeyVault –VaultName $vaultname).VaultUri
Finally, we grant specific permissions to the AAD service principal to be able to write the key to Key Vault:
Set-AzureRmKeyVaultAccessPolicy -VaultName $vaultname -ServicePrincipalName $aadClientID -PermissionsToKeys ‘wrapKey’ -PermissionsToSecrets ‘set’
Now that we’ve got all the parts in place, the actual encryption process is pretty painless. We simply issue a command to encrypt the VM. This will restart the VM, and can take quite some time to complete – 15 minutes or longer
Set-AzureRmVMDiskEncryptionExtension -ResourceGroupName $resourcegroupname -VMName $vmName -AadClientID $aadClientID -AadClientSecret $aadClientSecret -DiskEncryptionKeyVaultId $diskencryptionVaultID -DiskEncryptionKeyVaultUrl $diskencryptionVaulturl
Once the VM comes back online, log in and view the BitLocker status. You can also check the encryption status by issuing the following PowerShell command:
Get-AzureRmVmDiskEncryptionStatus -ResourceGroupName $resourcegroupName -VMName $vmName