Security Baseline on Azure Arc-Enabled Servers using Machine Configuration

Pratheep Sinnathurai
8 min readJan 28, 2024

--

In this blog post, I will share my recent experience with implementing a Security Baseline on Azure Arc-Enabled Servers. Security Baseline deployment is a critical aspect of maintaining a secure environment, and Microsoft provides various methods to achieve this goal. In this exploration, I examined different approaches, including Group Policy, Azure Automanage Machine Configuration, and the challenges encountered when creating a custom Machine Configuration.

Group Policy

If your Azure Arc-Enabled Servers are still within an Active Directory Domain, utilizing Group Policy to assign Security Policies is a viable option. However, for those looking to move away from Active Directory Domain Services and Group Policy, this solution may not align with expectations. In such cases, exploring alternative methods becomes crucial.

Azure Automanage Machine Configuration

Azure Automanage Machine Configuration (let’s just called it Machine Configuration) is Microsoft’s new way to apply Settings on Azure or Hybrid (Azure Arc-Enabled Machines) Virtual Machines. Machine Configuration is built on PowerShell DSC. Using Azure Policy you can deploy Machine Configuration to a single or multiple Computers.

There are built-in Policies and you can also create your own Machine Configuration. Let’s check the Built-In Policy first.

Built-In Azure Policy

Microsoft provides a list of built-in available Machine Configuration in Azure Policy.

If we are going to the Azure Portal under Azure Policy filter for Definition Type: “Initiative” and Category “Guest Configuration” (Machine Configuration was previously called Guest Configuration, don’t ask me why Microsoft don’t change that yet) we will see a Policy called “[Preview]: Windows machines should meet requirements for the Azure compute security baseline”.

If we look exactly at that Policy, we will see that this Policy only supports Audit Mode. That means we can check the Compliance on Security Settings but we cannot enforce it. Machine Configuration allows us to either Audit Settings (AuditIfNotExists) or deploy it (DeployIfNotExists).

As I am looking on a solution to enforce this Setting on a Windows Server let’s have a look into an another Solution.

Deploy using existing .mof File

I found in a GitHub Repository that Microsoft is already providing a MOF File. A MOF file, short for Management Object Format, is a text file used in the context of PowerShell Desired State Configuration (DSC). It is the Result of our compiled PowerShell DSC File.

Here is the link of the MOF File

Let’s try to compile to MOF File to a Azure Policy.

I received the issue above. Seems like the Module ‘AzureOsBaseline’ doesn’t exist in Public and Microsoft doesn’t plan to make it available Public.

Let’s check another way to deploy Security Baselines.

Create my own Machine Configuration

Microsoft provides the possibility to create a own Machine Configuration. In this Chapter we will have a look on how to create our own Machine Configuration.

As first step we need to install PowerShell 7. When you are using Windows you can install it using Winget.

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

To create Machine Configuration we need to install the Guest Configuration Module. If you have already the Guest Configuration Modules installed before I recommend to uninstall the older Versions and install the latest Version.

#Uninstall all Versions of Guest Configuration
Uninstall-Module GuestConfiguration -AllVersions

#Install the latest Available Version of Guest Configuration
install-Module -Name GuestConfiguration

#Import the Module
import-Module GuestConfiguration

#Check if Module is available
get-module GuestConfiguration

For creating our PowerShell DSC Configuration we need to install the following three Modules.

  • PSDesiredStateConfiguration
  • AuditPolicyDsc
  • SecurityPolicyDSC

You can use the Install-Module cmdlet to install them.

Install-Module -Name PSDesiredStateConfiguration, AuditPolicyDsc, SecurityPolicyDSC

The Modules AuditPolicyDSC and SecurityPolicyDSC are from the DSC Community. They did a great Job providing this Modules.

Now we create our own DSC Configuration. Here’s how the configuration could look like:

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

node localhost
{
SecurityOption SecurityOptions
{
Name = 'SecurityOption'
Accounts_Guest_account_status = 'Disabled'
Accounts_Rename_administrator_account = 'NotAdmin'
}
AccountPolicy AccountPolicies
{
Name = 'PasswordPolicies'
Enforce_password_history = 15
Maximum_Password_Age = 42
Minimum_Password_Age = 1
Minimum_Password_Length = 12
Password_must_meet_complexity_requirements = 'Enabled'
Store_passwords_using_reversible_encryption = 'Disabled'

}
}
}

#Create .MOF File in the defined Output Folder
SecurityBaseline -OutputPath .\SecurityBaseline\MOF\

Explanation of the structure:

  • Configuration: Begins the definition of a DSC configuration block named “SecurityBaseline”.
  • Import-DscResource: Imports DSC resources required for configuring Group Policy settings, auditing policies, and security policies.
  • node localhost: Identifies the target node where the configurations will be applied, in this case, the local machine. As we apply this configuration always on a machine directly always use ‘localhost’
  • SecurityOption: Defines security options such as disabling the Guest account and renaming the administrator acccount.
  • AccountPolicy: Specifies account-related policies such as password policies.
  • SecurityBaseline -OutputPath .\SecurityBaseline\MOF\

Running the Script above as PowerShell will create us a MOF file in the current Folder Structure under “SecurityBaseline” -> “MOF”.

As every MOF will be called “localhost.mof” I recommed to rename it. You can do it by using the following Command:

Rename-Item -Path .\SecurityBaseline\MOF\localhost.mof -NewName SecurityBaseline.mof

Now we can convert our MOF File into a Guest Configuration Package using the following Command.

We will take the SecurityBaseline.mof create the Guest Configuration Package under SecurityBaseline\ConfigurationPackageArtificat.

Using the Type Parameter we can define if we want to have an Audit or an AuditAndSet Package. Audit Packages are for AuditIfNotExists Azure Policies and AuditAndSet are for DeployIfNotExists Azure Policies.

New-GuestConfigurationPackage -Configuration .\SecurityBaseline\MOF\SecurityBaseline.mof -Name SecurityBaseline -Path .\SecurityBaseline\ConfigurationPackageArtificat -Type AuditAndSet 

The Package is now ready to be deployed. The packages must be stored in a location that’s accessible via HTTPS. We could upload the package on a GitHub Repo, Azure Repo or Azure Storage. The preferred location to store a configuration package is Azure Blob Storage.

The package is on default public accessible we can make it private using a SAS Token or service endpoints (Note to myself: idea for another Blog).

Now we need to create a Storage Account for our Guest Configuration Package.

Connect-AzAccount

Set-AzContext -Subscription sub-si-corp-shared-02

$newAccountParams = @{
ResourceGroupname = 'rg-machineconfiguration-mgt-dev-szn-01'
Location = 'Switzerlandnorth'
Name = 'stsimgtdevsznmachineconf'
SkuName = 'Standard_LRS'
AllowBlobPublicAccess = $true
MinimumTlsVersion = 'TLS1_2'
}

On our Storage Account we create a Blob Container to upload the Guest Configuration Package.

$container = New-AzStorageAccount @newAccountParams |
New-AzStorageContainer -Name machineconfiguration -Permission Blob

Upload the Guest Configuration Folder and get the URI for later.

$setParams = @{
Container = 'machineconfiguration'
File = '.\SecurityBaseline\ConfigurationPackageArtificat\SecurityBaseline.zip'
Context = $container.Context
}

$blob = Set-AzStorageBlobContent @setParams
$contentUri = $blob.ICloudBlob.Uri.AbsoluteUri

Create the Azure Policy Definition based on the Guest Configuration Package which we prepared so far. The output of this command is a JSON. We can define three different Modes on a Guest Configuration Policy which would be “Audit”, “ApplyAndMonitor” or “ApplyAndAutoCorrect”.

“Audit” will only audit the Settings, “ApplyAndMonitor” will apply it once and then monitor the Settings and “ApplyAndAutoCorrect” will automatically remediate the Settings when any Configuration drifts are found. As I want to my settings apply at any case I choose “ApplyAndAutoCorrect”.

$PolicyConfig      = @{
PolicyId = (New-Guid)
ContentUri = $contentUri
DisplayName = 'SecurityBaseline'
Description = 'SecurityBaseline'
Path = './SecurityBaseline/policies/deployIfNotExists.json'
Platform = 'Windows'
PolicyVersion = '1.0.0'
Mode = 'ApplyAndAutoCorrect'
}

New-GuestConfigurationPolicy @PolicyConfig

Now we create an Azure Policy using the JSON File from the last output.

  New-AzPolicyDefinition -Name 'SecurityBaselineTest' -Policy SecurityBaseline\policies\deployIfNotExists.json\SecurityBaseline_DeployIfNotExists.json

If we are now in the Azure Portal under Definition and looking for Policies with the type “Custom” we will find our own Machine Configuration.

Click on the Guest Configuration and let us assign it to an Azure Arc-Enabled Server.

I recommend to set the Scope on only a few amounts of servers.

It is important to set “Include Arc connected machines”.

For the remediation we need to have a Managed Identity. I recommend to use a User Assigned Managed Identity. Assign the Managed Identity the permission “Guest Configuration Resource Contributor”.

Now we can deploy it on our Azure-Arc Enabled Server and start the Remediation Task. For existing Resources we need to run the Remediation Task for the first for newly created Azure Arc-Enabled Server the remediation will automatically be applied.

If we check our Server we can now see that as one of the Setting renaming the local Administrator Account was applied and the Guest Account is disabled.

Questions you might have

Isn’t there an easier way to do it? Where is my Console? Where is my UI?

I see that for System Admins which are used to Group Policy this will be a big change but if we are looking in the world of Configuration Management we will see that most Tools are using similar approaches. I personally think it’s more important that we as System Admins are using modern approaches for Configuration.

Is there way to automate it?

Yes, you can use Pipelines to automate the whole Process including testing and signing Guest Configuration Packages.

What does Machine Configuration costs?

The cost for Machine Configuration in Azure Arc-Enabled Servers is approximately $5 per server per month. However, when utilizing Defender for Servers Plan 2 alongside Machine Configuration, the cost increases to $15 per server per month.

Conclusion

In this blog post, I shared my experience implementing a Security Baseline on Azure Arc-Enabled Servers. Security Baseline deployment is important for a secure environment, and Microsoft offers various methods to achieve this.

In conclusion, while challenges exist, Machine Configuration offers a modern approach to configuration management. It costs around $5 per server per month, rising to $15 with Defender for Servers Plan 2. Despite lacking a GUI, automation and modern practices make it valuable for sysadmins.

Embracing modern practices and exploring deployment alternatives streamline Security Baseline implementation on Azure Arc-Enabled Servers, ensuring a secure environment.

Sources:

Azure Automanage Machine Configuration documentation | Microsoft Learn

Select a Defender for Servers plan — Microsoft Defender for Cloud | Microsoft Learn

guest configuration dsc resource — Microsoft Q&A

https://github.com/Azure/azure-policy/issues/969

--

--

Pratheep Sinnathurai

Senior Azure Engineer and Microsoft MVP in Azure Hybrid & Migration