Azure Automation - Getting Started With Desired State Configurations
Azure brings a lot of new tools and capabilities to the IT and Information Security toolbox. In fact, there are so many features that it can be overwhelming and difficult to understand when or how to use them. I believe that the revamp of Desired State Configuration (DSC) within Azure is one of these overlooked tools that can help solve some everyday challenges.
Everyone is moving workloads to the cloud, and the recent trend to work from…everywhere is making it difficult to keep security policies enforced on servers and endpoints. This first part of the Azure Automation series will show the steps to get started with DSC and a few ways to put it to work.
In today's blog, we will lay the groundwork for more advanced tools and use cases by completing the following:
- Create an Automation Account
- Create an Enrollment Script
- Create a DSC File
- Compile and Assign the Configuration
Note: All scripts shown can be found here: https://github.com/philliprowland/AzureAutomationDemo
I will update this repository with more scripts throughout the series.
1. Create an Automation Account
An Automation account is a type of Azure resource that contains various methods to perform automated workflows. While many of these features are focused on DevOps workflows, several of them can be used to enhance the management of the endpoint configuration as well.
In the Azure Portal, when creating an Automation resource, the following form will appear:
The Name must be unique within the Resource Group. The Resource Group selected does NOT limit the devices that can be enrolled. It can, however, contain other Azure resources, so it may be worth creating a new one for the Automation account to keep everything tidy.
2. Create an Enrollment Script
The enrollment script will use a special kind of file in Managed Object Format (MOF). The content of the MOF file includes all of the details and secrets required to link an endpoint into the Automation account. I suggest you keep it safe to prevent strangers from giving you full access to their computers ;). This file is an unusual format, but luckily there is an easy way to generate it with PowerShell.
If you do not already have the Az module installed, or if you are not sure, install that first.
Install-Module Az
Update lines 5 and 6 of this script to match your account, save, and run it.
Import-Module Az Connect-AzAccount # Change these variables for your environment $ResourceGroup = "NetworkWatcherRG" $AutomationAccount = "DSC-Demo" $OutputFolderString = "$env:UserProfile\Desktop\" $Params = @{ ResourceGroupName = $ResourceGroup; AutomationAccountName = $AutomationAccount; ComputerName = @('localhost'); OutputFolder = $OutputFolderString; } Get-AzAutomationDscOnboardingMetaconfig @Params $ByteArray = [System.IO.File] : : ReadAllBytes("OutputFolderString\DscMetaConfigs\localhost.meta.mof") [System.Convert] : : ToBase64String($ByteArray) | Out-File "$OutputFolderString\localhost.meta.mof.base64"
Tip: Use PowerShell ISE to work with scripts if you do not have a preferred code editor.
This script will create a folder called DSCMetaConfigs on the desktop with the MOF file inside. It will then grab that file and convert it to base64 in a file named localhost.meta.mof.base64, so we can push it down to devices inside another script later.
Copy the contents of the localhost.meta.mof.base64 file between the quotes on line 1 in this script.
$Base64 = 'Paste base64 here' $OutputPath = "$env:Temp\DscMetaConfigs" New-Item -Path $OutputPath -ItemType Directory $ByteArray = [System.Convert] : : FromBase64String($Base64) Set-Content -Path "$OutputPath\localhost.meta.mof" -Value $ByteArray - Encoding Byte Start-Service WinRM Set-DscLocalConfigurationManger -Path $OutputPath Stop-Service WinRM
Save this and deploy it using GPO/Intune/Manually or any other method you wish for deploying PowerShell scripts. Once the device is enrolled, it will check in regularly without this script executing, so it should not be set on a recurring schedule.
It does need to be run as an Administrator on the device in order to enroll properly.
Once the device is enrolled, it will start checking in as a node in the Automation account. Its status can be viewed there almost immediately. It will initially report as 'Compliant,' because there is not a configuration assigned.
3. Create a DSC File
This is where the magic begins. First, let's create a DSC file. A DSC file is a PowerShell script that compiles into a MOF file. The MOF file is interpreted by the Local Configuration Manager (LCM) service. I recommend reviewing the Microsoft documentation about DSC Resources as a starting point (https://docs.microsoft.com/en-us/powershell/scripting/dsc/resources/resources?view=powershell-7). It contains a list of built-in resources that work without including external modules.
Tip: Many external modules require that PowerShell execution protection be downgraded to at least RemoteSigned in order to run. I recommend avoiding these if possible.
This sample configuration uses a script resource, which will be discussed in a later post. Script resources also require RemoteSigned execution mode to be configured. The sample includes two (2) ways we can use DSC to manage this setting as well.
Configuration DemoDSC { Import-DscResource -ModuleName PSDcResources Import-DscResouce -ModuleName ComputerManagementDsc Node localhost { #This is the preferred way to manage PS Exec mode since it does not require PS to enforce. Registry 'Registry(POL): HKLM:\Software\Microsoft\PowerShell\1\ShellIds\Microsoft.Powershell' { Ensure = 'Present' ValueName = 'ExcutionPolicy' ValueType = 'String' Key = 'HKLM:\Software\Microsoft\PowerShell\1\ShellIds\Microsoft.Powershell' ValueData = 'RemoteSigned' Force = $true } #This is an external module, it breaks if PS is disabled. Chicken & Egg... PowerShellExecutionPolicy ExecutionPolicy { ExecutionPolicy = 'RemoteSigned' ExecutionPolicyScope = 'LocalMachine' } Service XblAuthManager { Name = 'XblAuthManager' State = 'Stopped' StartupType = 'Disabled' } Registry 'Registry(POL): HKLM:\Software\Microsoft\wcmsvc\wifinetworkmanager\config\AutoConnectAllowedOEM' { Ensure = 'Present' ValueName = 'AutoConnectAllowedOEM' ValueType = 'Dword' Key = 'HKLM:\Software\Microsoft\wcmsvc\wifinetworkmanager\config' ValueData = 0 Force = $true } Script DisableNetbios { SetScript = { $NetbiosRegistryKey = "HKLM:SYSTEM\CurrentControlSet\services\NetBT\Parameters\Interfaces" Get-ChildItem $NetbiosRegistryKey | Get-ItemProperty | Where-Object { $_.NetbiosOptions -ne 2} | Set-ItemProperty -Name "NetbiosOptions" - Value 2 } TestScript = { #Get interfaces out of spec and return them. An empty set will pass. $NetbiosRegistryKey = "HKLM:SYSTEM\CurrentControlSet\services\NetBT\Parameters\Interfaces" (Get-ChildItem $NetbiosRegistryKey | Get-ItemProperty | Where-Object { $_.NetbiosOptions -ne 2 } | Measure-Ojbect).count -eq 0 } GetScript = { $NetbiosRegistryKey = "HKLM:SYSTEM\CurrentControlSet\services\NetBT\Parameters\Interfaces" @{ Result = (Get-ChildItem $NetbiosRegistryKey | Get-ItemProperty | Where-Object { $_.NetbiosOptions -ne 2 })} } } } } DemoDSC -OutputPath 'C:\Output'
This sample script imports required modules, defines a node (localhost), and defines its configured resources. There are a couple of key items to keep in mind when building a configuration.
The very first line contains a configuration name, DemoDSC. This configuration name is what the Automation account will use as the configuration name. It must be unique within the Automation account and cannot contain most symbols. Use a descriptive naming convention to avoid confusion and accidental overlaps if there will be numerous configurations. Azure will overwrite the existing configuration if a new one has the same name. Any assigned nodes will pull the new version of the configuration automatically. Take care with these names!
Chicken and egg problems can arise, such as in the PowerShellExecutionPolicy external resource. I have found that setting the PowerShell execution mode to Restricted, like it is by default in Windows 10, can prevent this module from being imported and run. This prevents the configuration from successfully managing the execution policy (whoopsie). This is why the Registry resource method is my preferred way of managing the execution policy. Another example is that if you accidentally disable the LCM service, which is what processes DSC, you will lose access to the device through DSC. Do not do that, it stinks.
4. Compile and Assign the Configuration
Once you have a DSC file that you would like to deploy, it is time to upload it to Azure, compile it into an MOF file, and assign it to a node. Microsoft has great documentation on these steps here: https://docs.microsoft.com/en-us/azure/automation/automation-dsc-getting-started
The node should now report in as pending. It will stay in this status until it checks in for the first time, applies the configuration, and reports back in.
We can simply wait for it to check in again, or if the device is within reach, the Update-DscConfiguration PowerShell command will trigger an update event.
5. Next Steps
The status page for each node will show detailed information for each type of configuration element. Take some time to implement a few key hardening settings in the configuration manually to get the hang of the format. The next post will walk through using some open source tools to convert a GPO directly into a DSC file. It can be quite daunting to see pages of configuration without first gaining some familiarity. We will also be discussing the Script Resource, which provided a few head-scratching moments for me that hopefully, you can learn from.
References:
Using Azure to Address Endpoint Hygiene Management TrustedSec blog
https://docs.microsoft.com/en-us/azure/automation/automation-dsc-getting-started
https://github.com/philliprowland/AzureAutomationDemo
We want to know what you think of our blog!