For the 5th and last part of the Azure Security Center Blog series we will handle the biggest category in the recommendations, the Virtual Machine Recommendations. Many thanks to my colleague Tom Klaver for helping me with this part!
Here are the other parts:
- Azure Security Center Blog Series | Part 4
- Azure Security Center Blog Series | Part 3
- Azure Security Center Blog Series | Part 2
- Azure Security Center Blog Series | Part 1
Contents
Apply disk encryption
Azure Security Center recommends that disk encryption is applied if you have Windows or Linux VM disks that are not encrypted using Azure Disk Encryption. Disk Encryption lets you encrypt Windows and Linux IaaS VM disks. Encryption is recommended for both the OS and data volumes on your VM. Disk Encryption uses the industry standard BitLocker feature of Windows and the DM-Crypt feature of Linux. These features provide OS and data encryption to help protect and safeguard your data and meet your organizational security and compliance commitments.
Disk Encryption is integrated with Azure Key Vault to help you control and manage the disk encryption keys and secrets in your Key Vault subscription, while ensuring that all data in the VM disks are encrypted at rest in your Azure Storage.
To resolve this recommendation you can use the following plan of action.
First the Azure Disk Encryption Prerequisite Setup Script (link) needs to be executed once per subscription.
There are a couple of parameters that need to be added while executing the script. The following parameters need to be added while executing the script:
- resourceGroupName – This is the resource group where the keyvault will be created
- keyVaultName – Name of the keyvault that will be created
- location – this is the location of the keyVault
- subscriptionId – this is the subscription id where the VMs are that need to be encrypted
- aadAppName – This is the name of the Azure AD Application
- keyEncryptionKeyName – This is the name of the key in keyvault
The VM and the keyvault need to be in the same region.
The script will output the following:
- AAD Client ID
- AAD Client Secret
- KeyVaultURL
- KeyVaultResourceID
- KeyEncryptionKey URL
When you execute the script you need to log in to the Azure Portal. The script will create a Azure AD Application, a KeyVault with a Key Encryption Key and will set the appropriate rights in the KeyVault for the Azure AD Application.
Before enabling disk encryption on the VM there must be a backup of the VM.
The VM will be rebooted when disk encryption is enabled
There are a couple of options for enabling disk encryption, the most easy way to enable this by running a JSON template called Enable-ade-running-vm which is available on the Azure Quickstart templates Github (link).
When running the Enable-ade-running-vm JSON template, this template will ask you for the following parameters:
- VMName: Name of the VM that needs encryption.
- AAD Client ID: This was a output from the prereq script.
- AAD Client Secret: This was a output from the prereq script.
- KeyVaultName: This is the name of the KeyVault that was created by the prereq script.
- KeyVaultResourceGroup: This the name of the resource group of th KeyVault that was created by the prereq script.
- KeK or NoKek
- KeyEncryptionKey URL
- VolumeType (OS Disk only or All Disks)
After the script is finished there is a new secret created in the keyvault with the name like this.

Disk encryption can also be enabled by using powershell, the following powershell script will enable the encryption:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
$keyVault = Get-AzureRmKeyVault -VaultName $keyVaultName -ResourceGroupName $rgName; $diskEncryptionKeyVaultUrl = $keyVault.VaultUri; $keyVaultResourceId = $keyVault.ResourceId; $keyEncryptionKeyUrl = (Get-AzureKeyVaultKey -VaultName $keyVaultName -Name myKey).Key.kid; Set-AzureRmVMDiskEncryptionExtension -ResourceGroupName $rgName ` -VMName $vmName ` -AadClientID $app.ApplicationId ` -AadClientSecret $securePassword ` -DiskEncryptionKeyVaultUrl $diskEncryptionKeyVaultUrl ` -DiskEncryptionKeyVaultId $keyVaultResourceId ` -KeyEncryptionKeyUrl $keyEncryptionKeyUrl ` -KeyEncryptionKeyVaultId $keyVaultResourceId |
Encypted VM Back-up
If you want to backup a VM with disk encryption, the Azure Recovery Services Vault needs access to the keys in the KeyVault.
This can be done by the following actions:
- Go to the KeyVault
- Go to access policies > Add New
- Select the principle Backup Management Service.
- The required permissions are prefilled for Key permissions and Secret permissions.
If your VM is encrypted by using BEK only, permissions only for secrets are required, so you must remove the selection for Key permissions.
After a successful encryption the Azure Portal still not showed that the disk of the VM is encrypted. This looks like a bug, you can check the encryption status with this powershell script:
1 2 3 4 5 6 |
Login-AzureRmAccount $Subscription = Get-AzureRmSubscription | Out-GridView -Title "Select Subscription" -OutputMode Single Select-AzureRmSubscription -SubscriptionId $Subscription.Id #Selectable Variables $VMName = Get-AzureRmVM | Out-GridView -Title "Select VM" -OutputMode Single Get-AzureRmVMDiskEncryptionStatus -VMName $VMName.Name -ResourceGroupName $VMName.ResourceGroupName |
Restore to new VM
If you want to restore a restore point to a new VM you can use the following scripts:
- RestoreVM-ADE-MD.ps1 – for Windows VM with Managed Disks
- RestoreVM-ADE-UMD.ps1 – for Windows VM with Unmanaged Disks
- RestoreVM-ADE-MD_Linux.ps1 – for Linux VM with Managed Disks
These scripts create a new VM with the same name with a addition of -RESTORE behind the VM name. The script is very straightforward. The scripts can be downloaded here.
The following parameters should be filled:
- $recoveryVaultName – Name of Virtual Machine to be restored
- $vmName – Name of Virtual Machine to be restored
- $storageAccount – Name of new Storage Account to use for restore
- $keyvaultName – Name of the Key Vault to retrieve Key Encryption Key (KEK) and Bitlocker Encryption Key (BEK)
- $virtualNetwork – Name of Virtual Network to use for VM restore
- $virtualNetworkRG – Name of Resource Group of Virtual Network
- $subnet – Name of Subnet
Mount disk to Azure VM
Before mounting a restored disk to a Azure VM you must get a .BEK file from the Key vault. You can do this by running the following script:
Change the following variables:
- $keyVaultName
- $kekName
- $bekFilePath
- $adTenant (in format johndejager.onmicrosoft.com)
Select the secret of the disk that you want to restore (if multiple disks are attached).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
################################################################################### Login-AzureRMAccount $VM = Get-AzureRmVM | Out-GridView -Title "Select VM" -OutputMode Single $vmName=$VM.Name $keyVaultName = 'KEYVAULTNAME'; $Secret = Get-AzureKeyVaultSecret -VaultName $keyVaultName | where {($_.Tags.MachineName -eq $vmName) -and ($_.ContentType -eq ‘Wrapped BEK’)} ################################################################################### # Initialize names of keyvault, secret, kek, bekFilePath to place the retrieved BEK file ################################################################################### $SecretName = $Secret.Name $kekName = 'KEKNAME'; $bekFilePath = 'C:\bek\FileName.BEK'; ################################################################################### # This requires Azure SDK to be installed on the machine # Initialize ADAL libraries to make REST API called against KeyVault REST APIs. ################################################################################### # Load ADAL Assemblies $adal = "${env:ProgramFiles(x86)}\Microsoft SDKs\Azure\PowerShell\ServiceManagement\Azure\Services\Microsoft.IdentityModel.Clients.ActiveDirectory.dll" $adalforms = "${env:ProgramFiles(x86)}\Microsoft SDKs\Azure\PowerShell\ServiceManagement\Azure\Services\Microsoft.IdentityModel.Clients.ActiveDirectory.WindowsForms.dll" [System.Reflection.Assembly]::LoadFrom($adal) [System.Reflection.Assembly]::LoadFrom($adalforms) # Set Azure AD Tenant name $adTenant = "AD TENANT" # Set well-known client ID for AzurePowerShell $clientId = "1950a258-227b-4e31-a9cf-717495945fc2" # Set redirect URI for Azure PowerShell $redirectUri = "urn:ietf:wg:oauth:2.0:oob" # Set Resource URI to Azure Service Management API $resourceAppIdURI = "https://vault.azure.net" # Set Authority to Azure AD Tenant $authority = "https://login.windows.net/$adtenant" # Create Authentication Context tied to Azure AD Tenant $authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority # Acquire token $authResult = $authContext.AcquireToken($resourceAppIdURI, $clientId, $redirectUri, "Auto") # Generate auth header $authHeader = $authResult.CreateAuthorizationHeader() # Set HTTP request headers to include Authorization header $headers = @{'x-ms-version'='2014-08-01';"Authorization" = $authHeader} ############################################################################## # 1. Retrieve wrapped BEK # 2. Make KeyVault REST API call to unwrap the BEK # 3. Convert the Base64Url string returned by KeyVault unwrap to Base64 string # 4. Convert Base64 string to bytes and write to the BEK file ################################################################################### #Get wrapped BEK and place it in JSON object to send to KeyVault REST API $keyVaultSecret = Get-AzureKeyVaultSecret -VaultName $keyVaultName -Name $secretName $wrappedBekSecretBase64 = $keyVaultSecret.SecretValueText $jsonObject = @" { "alg": "RSA-OAEP", "value" : "$wrappedBekSecretBase64" } "@ #Get KEK Url $kekUrl = (Get-AzureKeyVaultKey -VaultName $keyVaultName -Name $kekName).Key.Kid; $unwrapKeyRequestUrl = $kekUrl+ "/unwrapkey?api-version=2015-06-01"; #Call KeyVault REST API to Unwrap $result = Invoke-RestMethod -Method POST -Uri $unwrapKeyRequestUrl -Headers $headers -Body $jsonObject -ContentType "application/json" -Debug #Convert Base64Url string returned by KeyVault unwrap to Base64 string $base64UrlBek = $result.value; $base64Bek = $base64UrlBek.Replace('-', '+'); $base64Bek = $base64Bek.Replace('_', '/'); if($base64Bek.Length %4 -eq 2) { $base64Bek+= '=='; } elseif($base64Bek.Length %4 -eq 3) { $base64Bek+= '='; } #Convert base64 string to bytes and write to BEK file $bekFileBytes = [System.Convert]::FromBase64String($base64Bek); [System.IO.File]::WriteAllBytes($bekFilePath,$bekFileBytes) ################################################################################### |
After the script is finished the output is a file in $bekFilePath. The VM is restored in Azure as a new VM and you can now mount one of the disks to an existing VM. After you have mounted the disk you can unlock the disk with the .BEK file which is the output of the above script.
To mount a restored disk to an existing VM, go to the portal and select the restored VM.
Detach the disk you want to attach to an existing VM.
- Go to the VM
- Go to Disks
- Select the disk and select detach.
Mount the disk to an existing VM:
- Go to the VM
- Go to Disks
- Select Add a data disk
- Select the disk that is available and click save.
Now you can mount the disk in the VM the following command and the .BEK file which is the output of the script above.
manage-bde -unlock F: -rk C:\bek\YOURBEKKEY.BEK’
Apply system updates
Azure Security Center monitors Windows and Linux virtual machines (VMs) on a daily base if computers are missing operating system updates. Security Center retrieves a list of available security and critical updates from Windows Update or Windows Server Update Services (WSUS), depending on which service is configured on a Windows computer. Security Center also checks for the latest updates in Linux systems. If your VM or computer is missing a system update, Security Center will recommend that you apply system updates.
You can also check this recommendation in the Log analytics workspace that is used by Azure Security Center. This can be done by following the following steps:
- Go to the Log analytics in the Azure Portal.
- Go to the solutions pane in the Log analytics.
- Click on the updates solution.
- Click on System Update Assessment.
- In the solution there is an overview of the Windows and Linux VMs that are connected to the Log analytics workspace.
- In the overview you can see the VMs that need updates.
Enable encryption for Azure storage account
Azure Security Center may recommend that you enable Azure Storage Service Encryption for data at rest.
Storage Service Encryption (SSE) works by encrypting the data when it is written to Azure storage and decrypting the data before retrieval. SSE is currently available only for the Azure Blob service and can be used for block blobs, page blobs, and append blobs.
After enabling encryption, only new data is encrypted. Any existing blobs in your storage account remain unencrypted.
To comply to the recommendation execute the following plan:
- Go to the recommendation in Azure Security Center.
- Select the storage account.
- Click on the Enable button and save.
Now the storage account is encrypted.
Enable VM Agent
The Microsoft Monitoring Agent must be installed on virtual machines (VMs) in order to enable data collection. Azure Security Center enables you to see which VMs require the VM Agent and will recommend that you enable the VM Agent on those VMs.
To comply with the recommendation the following plan can be executed:
- Go to the VM(s) that the recommendation is about.
- Go to extensions > Add.
- Select the Microsoft Monitoring Agent.
- After the provisioning of the agent is done, go to the Log Analytics workspace that is used by Azure Security Center.
- Go to WORKSPACE DATA SOURCES > Virtual Machines.
- Click on the virtual machine and select the Connect button.
Endpoint Protection not installed on Azure VMs
Azure Security Center monitors the status of antimalware protection and reports this under the Endpoint protection issues blade. Security Center highlights issues, such as detected threats and insufficient protection, which can make the virtual machines (VMs) and computers vulnerable to antimalware threats. By using the information under Endpoint protection issues, you can identify a plan to address any issues identified.
- Go to the recommendation
- Select the VM(s) and click [Install on X VMs]
- Select the vendor of the Endpoint Protection agent and click install.
Reboot after system updates
This recommendation is very easy, after a system update the VM should be restarted.
Remediate OS vulnerabilities
This recommendations are based on the CCE standard (https://cce.mitre.org/about/), Microsoft gives a clear overview of all the OS vulnerabilities and how they can be resolved. I have created a custom group policy with most of the settings that are recommended.
A couple of settings are unable to set by GPO, so these settings are in the PowerShell script.
The GPO settings can be deployed locally by LGPO.exe or by a computer policy in the domain. The GPO files can be found here.
Download the GPO backup and save the following script. Both the files can be uploaded to a location that is accessible from the VM, for example a blob storage account.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
### Variables $TimeStamp=Get-Date $ScriptDir="C:\Script\" $DownloadDir="C:\Packages\Plugins\Microsoft.Compute.CustomScriptExtension\1.9\Downloads\0\" ### LOG $LogDir="C:\Script\Log" $LogFile="C:\Script\Log\Script.log" ## Create Folder for logging New-Item -Path $LogDir -ItemType Directory Function LogWrite{ Param ([string]$logstring) Add-content $Logfile -value $logstring } LogWrite "$TimeStamp Script started" ### ZIP $ZIPDst= "C:\Script\" $ZIPFile="OS_Vulnerabilities.zip" #$ZIPSrc=$ScriptDir+$ZIPFile $ZIPSrc=$DownloadDir+$ZIPFile ## Create Folder for extraction New-Item -Path $ZIPDst -ItemType Directory $TimeStamp=Get-Date LogWrite "$TimeStamp ZIP Source is:$ZIPSrc" LogWrite "$TimeStamp ZIP Destination is:$ZIPDst" ### UNZIP Add-Type -AssemblyName System.IO.Compression.FileSystem function Unzip{ param([string]$zipfile, [string]$outpath) [System.IO.Compression.ZipFile]::ExtractToDirectory($zipfile, $outpath) } Unzip "$ZIPSrc" "$ZIPDst" ### Get OS Version $OSVersion = (gwmi win32_operatingsystem).caption if ($OSVersion -eq "Microsoft Windows Server 2012 R2 Datacenter") { $TimeStamp=Get-Date Write-Host "$TimeStamp OS Version is Microsoft Windows Server 2012 R2 Datacenter" LogWrite "$TimeStamp OS Version is Microsoft Windows Server 2012 R2 Datacenter" ### MSS Settings - Disable AutoAdminLogon - CCE-ID - CCE-37067-6 $TimeStamp=Get-Date LogWrite "$TimeStamp Set MSS Settings - Disable AutoAdminLogon - CCE-ID - CCE-37067-6" $RegPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\" $RegName = "AutoAdminLogon" $RegValue = "0" New-ItemProperty -Path $RegPath -Name $RegName -Value $RegValue ### MSS Settings - SafeDllSearchMode - CCE-ID - CCE-36351-5 $TimeStamp=Get-Date LogWrite "$TimeStamp Set MSS Settings - SafeDllSearchMode - CCE-ID - CCE-36351-5" $RegPath = "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\" $RegName = "SafeDllSearchMode" $RegValue = "1" New-ItemProperty -Path $RegPath -Name $RegName -Value $RegValue -PropertyType "DWord" ### MSS Settings - Screen Saver GracePeriod - CCE-ID - CCE-37993-3 $TimeStamp=Get-Date LogWrite "$TimeStamp Set MSS Settings - Screen Saver GracePeriod - CCE-ID - CCE-37993-3" $RegPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\" $RegName = "ScreenSaverGracePeriod" $RegValue = "5" New-ItemProperty -Path $RegPath -Name $RegName -Value $RegValue ### MSS Settings - Disable IP SourceRouting (IPv6) - CCE-ID - CCE-36871-2 $TimeStamp=Get-Date LogWrite "$TimeStamp Set MSS Settings - Disable IP SourceRouting (IPv6) - CCE-ID - CCE-36871-2" $RegPath = "HKLM:\System\CurrentControlSet\Services\Tcpip6\Parameters" $RegName = "DisableIPSourceRouting" $RegValue = "2" New-ItemProperty -Path $RegPath -Name $RegName -Value $RegValue -PropertyType "DWord" ### MSS Settings - Disable IP SourceRouting (IPv4) - CCE-ID - CCE-36535-3 $TimeStamp=Get-Date LogWrite "$TimeStamp Set MSS Settings - Disable IP SourceRouting (IPv4) - CCE-ID - CCE-36535-3" $RegPath = "HKLM:\System\CurrentControlSet\Services\Tcpip\Parameters" $RegName = "DisableIPSourceRouting" $RegValue = "2" New-ItemProperty -Path $RegPath -Name $RegName -Value $RegValue -PropertyType "DWord" ### MSS Settings - Eventlog WarningLevel - CCE-ID - CCE-36880-3 $TimeStamp=Get-Date LogWrite "$TimeStamp Set MSS Settings - Eventlog WarningLevel - CCE-ID - CCE-36880-3" $RegPath = "HKLM:\SYSTEM\CurrentControlSet\Services\Eventlog\Security" $RegName = "WarningLevel" $RegValue = "90" New-ItemProperty -Path $RegPath -Name $RegName -Value $RegValue -PropertyType "DWord" ### Restore GPO Backup with OS vulnerability fixes $TimeStamp=Get-Date LogWrite "$TimeStamp Started with GPO Restore" $LGPOCMD = $ScriptDir+"LGPO\LGPO.exe" $GPOPath = $ScriptDir+"GPO_BACKUP\WS-2012R2" $ARG = "/g", $GPOPath Start-Process -FilePath $LGPOCMD -ArgumentList $ARG }elseif ($OSVersion -eq "Microsoft Windows Server 2016 Datacenter"){ $TimeStamp=Get-Date Write-Host "$TimeStamp OS Version is Microsoft Windows Server 2016 Datacenter" LogWrite "$TimeStamp OS Version is Microsoft Windows Server 2016 Datacenter" ### Enable 'Scan removable drives' by setting DisableRemovableDriveScanning to 0 $TimeStamp=Get-Date LogWrite "$TimeStamp Enable 'Scan removable drives' by setting DisableRemovableDriveScanning to 0" Set-MpPreference -DisableRemovableDriveScanning 0 ### Disable SMBv1 $TimeStamp=Get-Date LogWrite "$TimeStamp Disable SMBv10" Set-SmbServerConfiguration -EnableSMB1Protocol $false -Force ### Restore GPO Backup with OS vulnerability fixes $TimeStamp=Get-Date LogWrite "$TimeStamp Started with GPO Restore" $LGPOCMD = $ScriptDir+"LGPO\LGPO.exe" $GPOPath = $ScriptDir+"GPO_BACKUP\WS-2016" $ARG = "/g", $GPOPath Start-Process -FilePath $LGPOCMD -ArgumentList $ARG } |
To deploy the OS Vulnerabilities custom script extension you can use thefollowing PowerShell commands:
Change the FileUri to your location.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Login-AzureRmAccount $Subscription = Get-AzureRmSubscription | Out-GridView -Title "Select Subscription" -OutputMode Single Select-AzureRmSubscription -SubscriptionId $Subscription.Id $VM = Get-AzureRmVM | Out-GridView -Title "Select VM" -OutputMode Single Set-AzureRmVMCustomScriptExtension -ResourceGroupName $VM.ResourceGroupName` -VMName $VM.Name ` -Location $VM.Location ` -FileUri "https://your.blob.core.windows.net/ascosvulnerabilities/OS_Vulnerabilities.ps1","https://your.blob.core.windows.net/ascosvulnerabilities/OS_Vulnerabilities.zip" ` -Run 'OS_Vulnerabilities.ps1' ` -Name OSVulnerabilitiesExtension #### Output log - C:\WindowsAzure\Logs\Plugins\Microsoft.Compute.CustomScriptExtension #### Download dir - C:\Packages\Plugins\Microsoft.Compute.CustomScriptExtension\1.*\Downloads\ |
Update OS version
This recommendation is quite simple, it recommends that you update the operating system (OS) version for your Cloud Service to the most recent version available for your OS family.
Add a vulnerability assessment solution
If Security Center doesn’t find a vulnerability assessment solution installed on your VM, it recommends that you install one. A partner agent, after being deployed, starts reporting vulnerability data to the partner’s management platform. In turn, the partner’s management platform provides vulnerability and health monitoring data back to Security Center. You can quickly identify vulnerable VMs on the Security Center dashboard. Switch to the partner management console directly from Security Center for additional reports and information.
Important notes regarding the vulnerability assessment capability:
- Currently, a vulnerability assessment is available from Qualys. More partners will be added in the future.
- You can install the vulnerability assessment solution on multiple VMs. The VMs must belong to the same subscription.
If you want to comply to this recommendation there is vulnerability assessment solution from Qualys you can implement. If you want to deploy this solution you need a License Code and a Public Key. This data can be found in the management platform from Qualys.
Thank you for reading.
This is the last blog of the Azure Security Center blog series. Keep post for more blogs!