Enabling IaaS Encryption- How to configure VM Encryption

This is the second blog within a 3-part series detailing my investigations on the current state of enabling Infrastructure as a Service (IaaS) Encryption functionality.  Details are accurate as of July 2018.  

Throughout the series, I will explore the following articles:

In this article I will run through the PowerShell commands that are used to enable encryption while explaining each section to provide an insight into the requirements and why.

Encryption COMMands

To test out VM Encryption, I went through the process of setting up Encryption on both a managed disk VM and an unmanaged disk VM. Both VMs are running Windows Server 2016.

It should be noted that Microsoft publish their own instructions for enabling VM encryption here.  It is likely that the instructions below will become outdated in time. 

To enable Encryption, the following command needs to be run:

Set-AzureRmVMDiskEncryptionExtension -ResourceGroupName $VMRGName -VMName $VMName -DiskEncryptionKeyVaultUrl $KeyVault.VaultUri -DiskEncryptionKeyVaultId $KeyVault.ResourceId -AadClientID $AADID -AadClientSecret $AADSecret -KeyEncryptionKeyUrl $KEK.Key.Kid -KeyEncryptionKeyVaultId $KeyVault.ResourceId -ErrorAction Stop

This command requires several pieces of information which is gathered into the following sections:

vm details

This is the resource group and name of the VM that will be encrypted, I defined these as variables:

$VMRGName = “Leo-TestDomain”

$VMName = “adVM”

key vault details

The VM requires a Key Vault to store the bitlocker keys, this is defined using the following settings:

-DiskEncryptionKeyVaultUrl $KeyVault.VaultUri -DiskEncryptionKeyVaultId $KeyVault.ResourceId

To create the Key Vault, I used the following settings:

IaaS Encryption

I used a premium Key Vault to allow the Key Encryption Key (KEK) to be stored in a Hardware Security Module (More on the KEK later). It’s important to enable the advanced access policy to allow new and existing VM’s to be provisioned for encryption, although these settings can be modified after deployment.

$KVRGName = “Leo-EncryptionTest-01”

$KVName = “Leo-EncryptionTest-01”

Then I get the Key Vault object using the defined variables:

$KeyVault = Get-AzureRmKeyVault -VaultName $KVName -ResourceGroupName $KVRGName

This object has the properties for the Key Vault URL and the resource ID.

Key Encryption Key DETAILS

A KEK allows Azure to automatically change the actual bitlocker keys on a regular basis, this means that anyone recovering a bitlocker key only has a limited time window to use it before it becomes useless. 

To define a KEK the following arguments are needed:

-KeyEncryptionKeyUrl $KEK.Key.Kid -KeyEncryptionKeyVaultId $KeyVault.ResourceId

To create the KEK, the first thing I did is define the name:

$KEKName = “Test-KEK-01”

I then created a new key using the following command:

$KEK = Add-AzureKeyVaultKey -VaultName $KVName -Name $KEKName -Destination HSM

Note:  This command is using the Azure namespace not AzureRM however it still works for ARM Key Vaults. Since I’m using a premium Key Vault I am able to set the destination to HSM, the alternative is ‘Software’ if using a standard Key Vault.

The KEK object now has a nested key object, this key object has the ‘kid’ this is the URL for that specific key. The Key Vault which stores the KEK resourceID is also required.

vm authentication

Finally, the VM must be able to authenticate against the Key Vault to add it’s keys in. To automate this process, a service principle is required in the Azure Active Directory (Azure AD) tenant.

This can be  a confusing process and is explained in more detail here. Basically, a new Azure ‘Application’ is required, this has an identity and a password. A Service Principle is then provisioned for the application which inherits the application password and allows for direct authentication with Azure AD integrated objects such as Key Vault.

To start off with, I generated a new password:

$AADSecret = [Guid]::NewGuid()

A new unique identity for the application is also created, this needs to exist but isn’t used anywhere:

$identifierUri = [string]::Format(“http://localhost:8080/{0}”,[Guid]::NewGuid().ToString(“N”));

The application is then created:

$AADSPName = “VM Encryption Service”

$azureAdApplication = New-AzureRmADApplication -DisplayName $AADSPName -IdentifierUris $identifierUri -Password $AADSecret

The Service Principle is then created from the new application:

$servicePrincipal = New-AzureRmADServicePrincipal –ApplicationId $azureAdApplication.ApplicationId

The Service Principle ID is then stored for later use:

$AADID = $servicePrincipal.ApplicationId

final script

$VMRGName = “Leo-TestDomain”

$VMName = “adVM”

$KVRGName = “Leo-EncryptionTest-01”

$KVName = “Leo-EncryptionTest-01”

$KEKName = “Test-KEK-01”

$AADSPName = “VM Encryption Service”

#ApplicationID requires a password, new GUID provides a hard to guess password

$AADSecret = [Guid]::NewGuid()

#All Azure AD Applications require a unique url to identify themselves, it is not required that this goes anywhere

$identifierUri = [string]::Format(“http://localhost:8080/{0}”,[Guid]::NewGuid().ToString(“N”));

#Create the new Application

$azureAdApplication = New-AzureRmADApplication -DisplayName $AADSPName -IdentifierUris $identifierUri -Password $AADSecret

#Create a new Service Principal to allow access to the Application

$servicePrincipal = New-AzureRmADServicePrincipal –ApplicationId $azureAdApplication.ApplicationId

#Get the Service Principal ID to use when authenticating with the key vault

$AADID = $servicePrincipal.ApplicationId

$KeyVault = Get-AzureRmKeyVault -VaultName $KVName -ResourceGroupName $KVRGName

$KEK = Add-AzureKeyVaultKey -VaultName $KVName -Name $KEKName -Destination Software

Set-AzureRmKeyVaultAccessPolicy -VaultName $KVName -ServicePrincipalName $AADID -PermissionsToKeys ‘WrapKey’ -PermissionsToSecrets ‘Set’ -ResourceGroupName $KVRGName

try

{

Set-AzureRmVMDiskEncryptionExtension -ResourceGroupName $VMRGName -VMName $VMName -DiskEncryptionKeyVaultUrl $KeyVault.VaultUri -DiskEncryptionKeyVaultId $KeyVault.ResourceId -AadClientID $AADID -AadClientSecret $AADSecret -KeyEncryptionKeyUrl $KEK.Key.Kid -KeyEncryptionKeyVaultId $KeyVault.ResourceId -ErrorAction Stop

}

catch

{

#if failure existing extension must be removed before attempting again

Remove-AzureRmVMDiskEncryptionExtension -ResourceGroupName $VMRGName -VMName $VMName

}

You may have noticed I added a try/catch statement. During my testing I noticed if the deployment of the extension failed re-running, the extension would simply return the previous error message. To get around this issue simply run the remove command and then re-attempt the set command.

Conclusion

It’s a shame that configuration of encryption isn’t currently integrated into the Azure portal and creating custom service principles are necessary.  Over time, I expect that this process will be simplified, the preview of Managed Service Identity gives some hope that this will become a fully integrated process.  In the final part of my series I will look at what some of the experiences look like once disk encryption has been enabled. 

Share on Facebook
Share on Twitter
Share on LinkedIn

Related blogs

two people at desk looking at code

AOVPN DPC V4.0 is Now Live!

Today we’re very excited to announce the release of AOVPN DPC 4.0 with support for Windows 11! AOVPN Dynamic Profile Configurator is now functional with