There’s two methods for Encryption-At-Rest for Virtual Machines. Those methods are Storage Service Encryption and Azure Disk Encryption. I won’t go into too much detail as to the differences and what Encryption-At-Rest is as all that is documented very well here.
The high-level is that Storage Service Encryption (which is enabled by default and cannot be disabled) encrypts your data at rest. Azure Disk Encryption encrypts the OS and data disks at rest. More on the differences here.
The purpose of this article is to provide a script and demonstrate different scenarios in which my script can be used to help provide an automated method which can encrypt your OS and Data disks as well as automatically creating a Key Vault if one does not exist including the Access Policy configuration.
See the bottom of this post for the code.
The first four lines of code are the only variables that you will have to change. These four variables are:
$keyVaultName = "KeyVaultName"
$keyVaultrgName = "KeyVaultRGName"
$vmRgName = "VMName"
$vmName = "VMRGName"
$keyVaultName should contain the name of your Key Vault. The key vault should be enabled for disk encryption and should have an access policy granted your account the ability to work with keys.
$keyVaultrgName should contain the name of the Resource Group that houses your Key Vault.
$vmRgName should contain the name of the Resource Group that houses your Virtual Machine that you want to enable encryption for.
$vmName should be the name of your Virtual Machine that you want to enable encryption for.
VERY IMPORTANT: As always, it is imperitave that you first backup your Virtual Machine before attempting Azure Disk Encryption. That way, if something goes wrong with your Virtual Machine during the encryption process, it is recoverable.
Let’s take a look at several scenarios in which this script works.
Scenario 1 – Virtual Machine does not exist
My variables look like this:
$keyVaultName = "ShudKeyVault"
$keyVaultrgName = "ShudKeyVault"
$vmRgName = "vmblog00"
$vmName = "vmblog00"
The Virtual Machine, vmblog00, does not exist.
After running, Enable-AzureDiskEncryption,ps1, the script detects the Virtual Machine does not exist, at least not in the Resource Group that was specified. Therefore, the script terminates.
Scenario 2 – Virtual Machine exists but not running
My variables look like this:
$keyVaultName = "ShudKeyVault"
$keyVaultrgName = "ShudKeyVault"
$vmRgName = "vmblog01"
$vmName = "vmblog01"
The Virtual Machine, vmblog01, does exist and is located within a Resource Group also called vmblog01. This Virtual Machine is currently stopped and deallocated as can be seen below.
After running, Enable-AzureDiskEncryption,ps1, the script detects the Virtual Machine does exist but also detects that the Virtual Machine is not running. Therefore, the script terminates.
Scenario 3 – Virtual Machine is running and Key Vault Exists
My variables look the same as Scenario 2. The only difference here is that the Virtual Machine is running now.
$keyVaultName = "ShudKeyVault"
$keyVaultrgName = "ShudKeyVault"
$vmRgName = "vmblog01"
$vmName = "vmblog01"
We can see that the ShudKeyVault exists within the ShudKeyVault Resource Group.
If we look at the Access Policy for this Key Vault, we can see that Disk Encryption is enabled and my user account has access to manage Keys.
After running, Enable-AzureDiskEncryption,ps1, the script detects the Virtual Machine does exist, detects the Key Vault exists. Therefore, the script creates a new Key in the format of [vmname]-[5 random digits]. For our example, the Key name generated is vmblog01-44643. The disks are then encrypted leveraging this Key.
After a bit of time waiting for the disk encryption, our disks are now encrypted. We did not have any data disks, so we can see only the OS Disk was encrypted and the ProgressMessage displays Provisioning succeeded.
Taking a look at the Key Vault keys, we can see our vmblog01-44643 key.
Scenario 4 – Virtual Machine is running and Key Vault Does not exist
My variables are now completely different. We have a new virtual machine as well as a Key Vault that does not exist.
$keyVaultName = "ShudKeyVault02"
$keyVaultrgName = "ShudKeyVault02"
$vmRgName = "vmblog02"
$vmName = "vmblog02"
We can see ShudKeyVault02 does not exist in the ShudKeyVault02 Resource Group.
We can also see the vmblog02 Virtual Machine exists and is up and running.
After running, Enable-AzureDiskEncryption,ps1, the script detects the Virtual Machine does exist and that the Key Vault does not exist. Therefore, the script asks you if you want to create a new Key Vault. It asks you for a y (for yes) or for a n (for no). If we choose any other character such as the letter k, the script asks again. If you choose n, the script terminates. See the following screenshots for examples of both.
Let’s re-run Enable-AzureDiskEncryption.ps1. This time, we will choose y for Yes. What will happen is a Key Vault with the name of ShudKeyVault02 will be created in the ShudKeyVault02 Resource Group. The Key Vault will be enabled for Disk Encryption. An Access Policy for the Key Vault will be created with the existing user who is logged in getting full permissions for key management.
And if we take a look at the Key Vault’s permissions we can see the new Key Vault is enabled for Disk Encryption and the existing user has full permissions for key management.
Just as with the original Key Vault Key that was created, it is in the format of [vmname]-[5 random digits]. For this scenario the Key name generated is vmblog02-97815. The disks are then encrypted leveraging this Key.
Scenario 5 – Virtual Machine is already encrypted
My variables look like this:
$keyVaultName = "ShudKeyVault"
$keyVaultrgName = "ShudKeyVault"
$vmRgName = "vmblog01"
$vmName = "vmblog01"
As we already encrypted the vmblog01 Virtual Machine earlier in this article with the ShudKeyVault, we would expect to get a message that the Virtual Machine is already encrypted and the script terminates. This detection occurs if the Virtual Machine still exists and is in a running state. If the Virtual Machine doesn’t exist, the results in Scenario 1 will occur. If the Virtual Machine is not running, the results in Scenario 2 will occur.
After running, Enable-AzureDiskEncryption,ps1, the script does indeed detect the Virtual Machine is already encrypted and the script terminates.
The Code
As promised, the code is as follows… Let me know in the comments if you have any questions.
$keyVaultName = "ShudKeyVault02"
$keyVaultrgName = "ShudKeyVault02"
$vmRgName = "vmblog02"
$vmName = "vmblog02"
try
{
Write-Host "Verifying Virtual Machine Exists... " -ForegroundColor Yellow
$VMStatus = Get-AzVM -VMName $vmName -ResourceGroupName $vmRgName -Status -ErrorAction Stop
$VMInfo = Get-AzVM -VMName $vmName -ResourceGroupName $vmRgName -ErrorAction Stop
$VMRegion = $VMInfo.Location
Write-Host "Verified Virtual Machine $vmName exists. Virtual Machine is located in the $VMRegion Azure Region." -ForegroundColor Green
Write-Host " "
Write-Host "Verifying Virtual Machine is Running... " -ForegroundColor Yellow
# Need to ensure Virtual Machine is in a running state in order to be encrypted.
$VMPowerState = $VMStatus.Statuses[1].DisplayStatus
if ($VMPowerState -ne "VM running")
{
Write-Host "ERROR: Virtual Machine $vmName is not running. Cannot proceed with encryption. Terminating Script." -ForegroundColor Red
exit
}
else
{
Write-Host "Verified Virtual Machine $vmName is running." -ForegroundColor Green
}
Write-Host " "
Write-Host "Verifying Virtual Machine is not already encrypted... " -ForegroundColor Yellow
# Need to ensure the Virtual Machine is not already encrypted.
$VMEncryptstatus = Get-AzVmDiskEncryptionStatus -ResourceGroupName $vmRgName -VMName $vmName
$VMOSEncryptstatus = $VMEncryptstatus.OSVolumeEncrypted
$VMDataEncryptstatus = $VMEncryptstatus.DataVolumesEncrypted
if (($VMOSEncryptstatus -eq "Encrypted") -or ($VMDataEncryptstatus -eq "Encrypted"))
{
Write-Host "ERROR: The Virtual Machine is already encrypted. Script Terminating." -ForegroundColor Red
exit
}
else
{
Write-Host "Verified Virtual Machine $vmName is not already encrypted." -ForegroundColor Green
}
}
catch
{
Write-Host "ERROR: The Virtual Machine $vmName does not exist. Terminating Script." -ForegroundColor Red
exit
}
try
{
<#
Checking for the existence of the Key Vault. If key vault not found,
an opportunity to create a new key vault with the same name is provided.
If you choose to create a new key vault, it will be created in the same region
the Virtual Machine lives in. This is why the Virtual Machine Check is before
the Key Vault Check.
#>
Write-Host " "
Write-Host "Verifying Key Vault Exists... " -ForegroundColor Yellow
$keyVault = Get-AzKeyVault -VaultName $keyVaultName -ResourceGroupName $keyVaultrgName
if ($keyVault)
{
Write-Host "Verified Key Vault $keyVaultName exists." -ForegroundColor Green
$diskEncryptionKeyVaultUrl = $keyVault.VaultUri;
$keyVaultResourceId = $keyVault.ResourceId;
}
else
{
throw "ERROR: The Key Vault $keyVaultName does not exist."
}
}
catch
{
Write-Host $_ -ForegroundColor Red
Write-Host " "
Write-Host "Providing opportunity to create a new Key Vault..." -ForegroundColor Yellow
Write-Host "Would you like to create a new Key Vault with the name of $($keyVaultName)?" -ForegroundColor White
$YesOrNo = Read-Host "Please enter your response (y/n)"
while("y","n" -notcontains $YesOrNo )
{
$YesOrNo = Read-Host "Please enter your response (y/n)"
}
if ($YesOrNo -eq "y")
{
try
{
Write-Host " "
Write-Host "Creating Resource Group with the name of $keyVaultrgName in the $VMRegion Azure Region to store the Key Vault $keyVaultName" -ForegroundColor Yellow
$CheckResourceGroupExists = Get-AZResourceGroup -Name $keyVaultrgName -ErrorAction SilentlyContinue
if ($CheckResourceGroupExists)
{
Write-Host "Resource Group $keyVaultrgName already exists." -ForegroundColor Green
}
else
{
New-AzResourceGroup -Name $keyVaultrgName -Location $VMRegion | Out-Null
Write-Host "Resource Group $keyVaultrgName successfully created." -ForegroundColor Green
Start-Sleep 3
}
Write-Host " "
Write-Host "Creating New Key Vault with the name of $keyVaultName in the $VMRegion Azure Region." -ForegroundColor Yellow
$keyVault = New-AzKeyVault -VaultName $keyVaultName -ResourceGroupName $keyVaultrgName -Location $VMRegion -ErrorAction Stop
Write-Host "Key Vault $keyVaultName successfully created." -ForegroundColor Green
Start-Sleep 3
$diskEncryptionKeyVaultUrl = $keyVault.VaultUri;
$keyVaultResourceId = $keyVault.ResourceId;
}
catch
{
$ErrorMessage = $_.Exception.Message
Write-Host "ERROR: Key Vault could not be created." -ForgroundColor Red
Write-Host "ERROR MESSAGE: $ErrorMessage" -ForegroundColor Red
}
try
{
Write-Host " "
Write-Host "Configuring Key Vault Access Policy to enable Disk Encryption." -ForegroundColor Yellow
Set-AzKeyVaultAccessPolicy -VaultName $keyVaultName -ResourceGroupName $keyVaultrgName -EnabledForDiskEncryption -ErrorAction Stop
Write-Host "Key Vault Policy successfully enabled for disk encryption." -ForegroundColor Green
$SignedInAdmin = (Get-AzContext).Account.ID
$objID=(Get-AzADUser -UserPrincipalName $SignedInAdmin).Id
Write-Host " "
Write-Host "Configuring Key Vault Access Policy to enable user $SignedInAdmin full permissions for key usage." -ForegroundColor Yellow
Set-AzKeyVaultAccessPolicy -VaultName $keyVaultName `
-ResourceGroupName $keyVaultrgName `
-ObjectId $objID `
-PermissionsToKeys create,delete,list,get,verify,encrypt,wrapkey `
-ErrorAction Stop
Write-Host "Successfully configured Key Vault Access Policy to enable user $SignedInAdmin full permissions for key usage." -ForegroundColor Green
}
catch
{
$ErrorMessage = $_.Exception.Message
Write-Host "Error setting access policies on the Key Vault $keyVaultName. Please update Key Vault Policy manually and re-run script."
Write-Host "ERROR MESSAGE: $ErrorMessage" -ForegroundColor Red
}
}
else
{
Write-Host "ERROR: The Key Vault $keyVaultName does not exist. Terminating Script." -ForegroundColor Red
exit
}
}
<#
Extra Check and Balance that the Virtual Machine Exists. Not completely necessary due to
the earlier try/catch.
#>
if ($VMStatus)
{
# Set the Key Vault Key Name using the Virtual Machine Name followed by _ followed by 5 random digits.
$keyVaultKeyName = $vmName + "-" + (get-random -minimum 10000 -max 99999)
try
{
Write-Host " "
Write-Host "Adding Key Vault Key $keyVaultKeyName to the $keyVaultName Key Vault." -ForegroundColor Yellow
Add-AzKeyVaultKey -VaultName $keyVaultName `
-Name $keyVaultKeyName `
-Destination "Software" `
-ErrorAction Stop | Out-Null
Write-Host "Successfully created the $keyVaultKeyName key in the $keyVaultName Key Vault." -ForegroundColor Green
}
catch
{
$ErrorMessage = $_.Exception.Message
Write-Host "ERROR: Could not add the $keyVaultKeyName to the $keyVaultName Key Vault. Terminating Script." -ForegroundColor Red
Write-Host "ERROR MESSAGE: $ErrorMessage" -ForegroundColor Red
exit
}
try
{
Write-Host " "
Write-Host "Verifying the Key Vault Key $keyVaultKeyName is instantiated and the key identifier is captured." -ForegroundColor Yellow
$keyEncryptionKeyUrl = (Get-AzKeyVaultKey -VaultName $keyVaultName -Name $keyVaultKeyName -ErrorAction Stop).Key.kid
Write-Host "Key Vault Key Identifier for Key Vault Key $keyVaultKeyName successfully captured." -ForegroundColor Green
}
catch
{
Write-Host "ERROR: Could not obtain the Key Encryption Key URL. Terminating Script." -ForegroundColor Red
}
try
{
Write-Host " "
Write-Host "###########################################" -ForegroundColor Yellow
Write-Host "Beginning Disk Encryption utilizing the following resources: " -ForegroundColor Yellow
Write-Host "VMName: $VMName" -ForegroundColor Yellow
Write-Host "VMResourceGroupName: $vmRgName" -ForegroundColor Yellow
Write-Host "KeyVaultName: $keyVaultName" -ForegroundColor Yellow
Write-Host "KeyVaultResourceGroupName: $keyVaultrgName" -ForegroundColor Yellow
Write-Host "KeyVaultKeyName: $keyVaultKeyName" -ForegroundColor Yellow
Write-Host " "
Write-Host "Please be patient as the Virtual Machine ($vmName)'s disks are encrypted." -ForegroundColor Yellow
Write-Host "###########################################" -ForegroundColor Yellow
Set-AzVMDiskEncryptionExtension -ResourceGroupName $vmRgName `
-VMName $vmName `
-DiskEncryptionKeyVaultUrl $diskEncryptionKeyVaultUrl `
-DiskEncryptionKeyVaultId $keyVaultResourceId `
-KeyEncryptionKeyUrl $keyEncryptionKeyUrl `
-KeyEncryptionKeyVaultId $keyVaultResourceId `
-VolumeType "All" #Uncomment this line if you only want to encrypt the OS Disk.
-Force `
-ErrorAction Stop | Out-Null
}
catch
{
$ErrorMessage = $_.Exception.Message
Write-Host "Failed to enable disk encryption on $vmName. Terminating Script." -ForegroundColor Red
Write-Host "ERROR MESSAGE: $ErrorMessage" -ForegroundColor Red
exit
}
}
else
{
<#
Extra Check and Balance that the Virtual Machine Exists. Not completely necessary due to
the earlier try/catch.
#>
Write-Host "ERROR: The Virtual Machine $vmName does not exist. Terminating Script." -ForegroundColor Red
exit
}
Write-Host " "
Write-Host "Verifying Encryption Status..." -ForegroundColor Yellow
do
{
$VMEncryptstatus = Get-AzVmDiskEncryptionStatus -ResourceGroupName $vmRgName -VMName $vmName
$EncryptionProgress = $VMEncryptstatus.ProgressMessage
$VMEncryptstatus
Start-Sleep 10
} until ($EncryptionProgress -like "*Provisioning succeeded*")
DAvid says
Hey
Get script how evey on my VMs it Error
Any idea why ?
OsVolumeEncrypted : NotEncrypted
DataVolumesEncrypted : NoDiskFound
OsVolumeEncryptionSettings :
ProgressMessage : [2.4.0.19] Failed to configure bitlocker as expected. Exception: ProtectKeyWithExternalKey failed with 2150121487, InnerException: , stack
trace: at Microsoft.Cis.Security.BitLocker.BitlockerIaasVMExtension.BitlockerWmi.Win32EncryptableVolumeWrap.ProtectKeyWithExternalkey()
in C:\__w\1\s\src\BitLocker\BitlockerIaasVMExtension\BitlockerWMI\Win32EncryptableVolumeWrap.cs:line 208
at Microsoft.Cis.Security.BitLocker.BitlockerIaasVMExtension.BitlockerWmi.Win32EncryptableVolumeWrap.GenerateBitlockerKey(Boolean
backupKeyToAD) in C:\__w\1\s\src\BitLocker\BitlockerIaasVMExtension\BitlockerWMI\Win32EncryptableVolumeWrap.cs:line 561
at Microsoft.Cis.Security.BitLocker.BitlockerIaasVMExtension.BitlockerExtension.GenerateProtectorForVolume(EncryptableVolume vol) in
C:\__w\1\s\src\BitLocker\BitlockerIaasVMExtension\BitlockerExtension.cs:line 163
at Microsoft.Cis.Security.BitLocker.BitlockerIaasVMExtension.BitlockerExtension.GenerateAndUploadProtectors() in
C:\__w\1\s\src\BitLocker\BitlockerIaasVMExtension\BitlockerExtension.cs:line 1179
at Microsoft.Cis.Security.BitLocker.BitlockerIaasVMExtension.BitlockerExtension.EnableEncryption() in
C:\__w\1\s\src\BitLocker\BitlockerIaasVMExtension\BitlockerExtension.cs:line 1792
at Microsoft.Cis.Security.BitLocker.BitlockerIaasVMExtension.BitlockerExtension.HandleEncryptionOperations(ILogger logger) in
C:\__w\1\s\src\BitLocker\BitlockerIaasVMExtension\BitlockerExtension.cs:line 2145
at Microsoft.Cis.Security.BitLocker.BitlockerIaasVMExtension.BitlockerExtension.OnEnable() in
C:\__w\1\s\src\BitLocker\BitlockerIaasVMExtension\BitlockerExtension.cs:line 2275
Elan Shudnow says
Doing a little searching online, it sounds like others that have this exact error is causing by Intune Policy being applied to the machines preventing Bitlocker configuration from being applied.