Testing Azure Machine Configuration Using Policy

In my home lab, I have some automation of the configuration of virtual machines. For that configuration I use Ansible. But all of the virtual machines are Linux. This is the first attempt I have on using machine configuration with guest configuration agent and policy, in Azure to configure a VM. I have tried to configure Windows virtual machines earlier using Ansible. Ansible uses some DSC modules for some configurations, and I also use DSC in this configuration.

Prerequisites

Client machine:

I use a Windows 11 machine with this setup:

Powershell v7 - winget install --id Microsoft.Powershell --source winget

Guest Configuration - install-Module -Name GuestConfiguration

Import module - import-Module GuestConfiguration

Test if the module is available - get-module GuestConfiguration

For this test, I did use those DSC modules PSDesiredStateConfiguration, AuditPolicyDsc, and SecurityPolicyDSC - Install-Module -Name PSDesiredStateConfiguration, AuditPolicyDsc, SecurityPolicyDSC

Azure Virtual Machine:

The virtual machine in Azure needs this (both requirements can be deployed using policy):

  • The Guest Configuration extension (not required for a virtual machine that uses Arc)
  • A system-assigned identity

In the subscription, you need to enable the Microsoft.GuestConfiguration provider.

You also need a storage account with a container to save the artefact to. Make sure that the client you are using and the Azure VM have access to the container since you need to upload the artefact we are creating later and the virtual machine will download the config from that container.

I used Terraform to configure all of the resources in Azure with the exception of the definition policy. I will not share the steps that I used in Terraform, since this post is long enough.

Create the DSC config

I created a config that does two things. The first, disable the Guest account. The second one changes the Enforce password history to 15.

Configuration SecurityBaseline
{
    Import-DscResource -ModuleName AuditPolicyDsc
    Import-DscResource -ModuleName SecurityPolicyDSC

    node localhost
    {
        SecurityOption SecurityOptions
        {
            Name = 'SecurityOption'
            Accounts_Guest_account_status = 'Disabled'
        }
        AccountPolicy AccountPolicies
        {
            Name = 'PasswordPolicies'
            Enforce_password_history = 15
        }
    }
}

SecurityBaseline -OutputPath .\SecurityBaselineTest\MOF\

Save it to a .ps1 file and run it. You should now have a .mof file in the directory that you specified in the last line of the script. The .mof file that was created is called localhost.mof. Use this command to rename the file Rename-Item -Path .\SecurityBaselineTest\MOF\localhost.mof -NewName SecurityBaseline.mof -PassThru.

Create the artifact

The next step is to create the .zip file that contains the configuration, modules and all the files that are needed for Azure Machine Configuration to use this file. To do that, you could create a PowerShell script that contains this config:

$params = @{
    Name          = 'SecurityBaselineTest'
    Configuration = './SecurityBaselineTest/MOF/SecurityBaseline.mof'
    Type          = 'AuditAndSet'
    Force         = $true
}
New-GuestConfigurationPackage @params

The important part here is to choose the correct Type. There are two different variables, AuditAndSet and Audit. Audit is the default. When you run this script, it will create a .zip file.

Create, upload and assign the policy

In this step, we are creating the last script we need, but the first step is to create a folder in the storage account container you created earlier. In my case, I called it machine-configuration.

$PolicyConfig      = @{
    PolicyId      = (New-Guid)
    ContentUri    = 'https://somethingstrange.blob.core.windows.net/machine-configuration/SecurityBaselineTest.zip'
    DisplayName   = 'SecurityBaseline1'
    Description   = 'SecurityBaseline1'
    Path          = './SecurityBaselineTest/policies/SecurityBaselineTest.json'
    Platform      = 'Windows'
    PolicyVersion = '1.0.0'
    Mode          = 'ApplyAndAutoCorrect'
}

New-GuestConfigurationPolicy @PolicyConfig

Run the script and you get a .json file. In Azure, create a definition and give it a logical name. Like SecurityBaseline1. If you don’t want to do this manually in the portal you can use this command to create the definition - New-AzPolicyDefinition -Name 'SecurityBaseline1' -Policy /SecurityBaselineTest/policies/SecurityBaselineTest.json. Now you have created a policy definition, that you can assign to something. In my case, I did assign it to the resource group where my virtual machine is located. During the process where you assign the policy, there are a couple of important tasks. The first on, set the parameter IncludeArcMachines to true if you want the police to work on virtual machines that are using Azure Arc. The second important part is that during the creation of the policy assignment, you need to create an identity and give it a role. I did use a System Managed Identity and gave it the Guest Configuration Resource Contributor.

Test

Go to your virtual machine in Azure, and check out the policy tab. There you will see that the new policy is listed, but with the status Non-compliant. Open the policy and create a Remediation task. This will run the policy (the policy is DeployIfNotExist) and after some time you will see that the policy status changes to Compliant. Next, go into the Machine Configuration tab in your virtual machine in Azure. Here you will see that the machine configuration is there and it says that 100% (2 out of 2) rules are compliant. The type should be ApplyAndAutoCorrect.

If you log into the virtual machine, and check the Local Security Policy, the Enforce Password History is changed to the value I used in the script earlier (15).

GitHub - DSC Community

matrixpost - Guest Configuration Extension on the Client

matrixpost - Guest Configuration Extension on the Client

Microsoft Learn - Azure Machine Configuration documentation

Pratheep Sinnathurai - Security Baseline on Azure Arc-Enabled Servers using Machine Configuration