How to use PowerShell & Azure Policy to check settings inside a VM

You might have explored Azure Policy to enforce the configuration of Azure resources, but did you know it can also investigate inside virtual machines too? This is really helpful for being alerted of “configuration drift” – when you need visibility of whether someone has changed a setting that was originally configured correctly, when the VM was first deployed.

An example scenario:
Tailwind Traders have a bunch of existing resources in Azure, including some virtual machines. They'd like to turn on auditing of those VMs so they can tell if ‘require a complex password' is turned on or not. As this is a setting inside , it requires a couple of additional components to allow Azure Policy to access it and return the results. This setting should be checked on an ongoing basis, to prove their VMs are still compliant with their business-driven password security requirement.

The process:
1. Using PowerShell, the Tailwind Traders admin will run a script that will only apply to VMs inside resource groups that have a specific word included in their name. While you could run this script against individual VMs, or against all of your resource groups, this selective way of applying a script is useful to know.

2. The script will enable in-guest auditing inside your Azure environment (by registering the provider namespace).

3. Then it will check the to see if the required extension has been turned on, to enable Azure Policy to check inside the VM. If not, it will deploy it.

4. It will also apply a managed identity with the “Contributor” permissions level. This is needed for Azure to be able to add the VM extension if it has not already been added to the VM (or if it has been removed). Click here for more information on Managed Identities.

5. Now it can apply the Azure Policy which will audit whether ‘require a complex password' is turned on or not.

PowerShell to the rescue!
Each of the steps above can be performed manually via the Azure Portal. But at scale, we turn to a PowerShell script. Download the script from Github here.

A few key points (and make it your own):
In the script, lines 10-12 tell it to only execute if a ResourceGroupNameFilter parameter is supplied. This prevents the script from running across every resource group in your environment, if you forget to add this filtering parameter. That filter then feeds through to the Get-AZResourceGroup command on line 16.

Lines 19 & 20 then fetch and store the definitions for the two policies we need – the one that deploys the VM extension if it doesn't exist and the one that audits the password complexity setting. You can customize this by changing the DisplayName of the extension and the policy. Right now, this in-guest configuration checking only supports in-built Azure Policies (ones that are provided by Azure) and not custom policies. You'll also note that the password complexity policy & its associated VM extension are currently in preview. That means they are subject to change and we need to include the [Preview] in the display name.

Line 23 registers the resource provider to ‘turn on' the ability to read into VM guest operating systems.

Lines 28-29 assign both the policies, but there's one special note about the VM extension policy. As a ‘deploy if not exists' policy, it needs to use a a managed identity to give it the rights to make a change to the VM properties. Here we create the managed identity, then we need to allow for a delay in that identity being created, before we can assign a role to that identity (giving it the correct permissions). The ‘sleep' in lines 32-34 give us that pause, which you may need to increase in your environment if you receive an error. Then lines 36 onward fetch and apply the correct role definition to the managed identity for each resource.

Click here for more information on Deploy if not Exists policies. 

Running the script
1. Connect to your Azure environment by running:


2. Because this script isn't digitally signed, you'll need to run the following in PowerShell first (or in the PowerShell Integrated Terminal Console in VS Code): 

Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass

Note this will automatically turn on again once your machine is rebooted, to again prevent unsigned from running.

3. Now run the script name, and add your own Resource Group Name Filter parameter, to execute the script against only those resource groups that match. As an example: 

.policyAuditVMPwdComplex.ps1 -ResourceGroupNameFilter "SVM"

The result
The Azure Policy section of the Azure Portal will now highlight VMs that are not compliant with this policy i.e. they've had the enforce complex passwords setting turn off.

You can use the Azure Policy remediation feature to fix this, or use Azure Monitor to set up automated alerting of non-compliance

Learn more:
MS Learn: Apply and monitor infrastructure standards with Azure Policy 
Understand Azure Policy's Guest Configuration 
Remediate non-compliant resources 

With special thanks to Neil Peterson for his help with this script.


This article was originally published by Microsoft's Azure Blog. You can find the original article here.