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:
- When to use Azure Virtual Machine (VM) Encryption and when to avoid it.
- How to configure Azure VM Encryption including script walk through.
- What Encryption looks like and what the common limitations look like. (Click to read article)
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:
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.