Training module · Draft
Attribute-Driven Migration, Module 5: Build Machine Configuration Packages
Take a server setting from a DSC configuration to a policy definition with Learn’s cmdlets, see what they don’t check, and run the build that cleans the MOF, checks the package and definition against a release registry, and records what shipped
Status — Draft
This page is still being written. Expect gaps and changes; it is not part of the workbook's production guidance. Last reviewed 2026-11-07.
Learning path: Building the Attribute-Driven Migration · Module 5 of 9 · 45 minutes · Guide Gate 5 · Workbook Chapter 07
Watch: the narrated deck for this module: Deck · ▶ Watch (about 14 minutes, captions) · MP4 · Script · Captions.
Also: the chapter’s narrated tour, about 11 minutes: ▶ Watch.
Unit 1: Introduction
5 minutes, with the objectives and prerequisites
Gate 5 sets the conventions the server track builds on: once connected, an Arc server is an Azure resource whose tags are its attributes. The Arc track and the Ansible guide add the rule that each setting has one owning tool. For the settings machine configuration owns, the code is a DSC configuration and Azure Policy delivers it. Learn’s cmdlets build a package and a definition. They don’t make the same bytes twice, and they write your user name and computer name into what the machines download. In this module you follow Learn’s steps, then run the build that checks between them.
The federal requirement. A package is the server baseline in a file, so what was built, from what, and what shipped is the evidence. From Chapter 07’s table:
| Requirement | KSI | Evidence the ISSO keeps |
|---|---|---|
| SR-3; SA-10 Developer Configuration Management: the build uses pinned module versions | KSI-SCR-MIT | The repository: RequiredModules.psd1 |
| SA-11; CM-3(2): the package check refuses build-host data | KSI-CMT-VTD | The check output |
| SA-10; SI-7; CM-2: the same commit gives the same cleaned MOF hash, and the hashes identify what shipped | KSI-SVC-VRI; KSI-CMT-LMC | provenance.json, in the evidence store |
CM-6; SI-7: the definition carries the package’s contentHash, and Arc servers are a stated choice |
KSI-SVC-ACM; KSI-SVC-VRI | The published definition |
| CM-3; CM-6: a version that didn’t rise, a mode in the wrong case and an enforcing definition with no owner filter are refused | KSI-CMT-VTD | The pull request’s check run |
| SI-7: the published blob is the built package, byte for byte (Lab 2) | KSI-SVC-VRI | The change record |
| AC-6; IA-5: machines read the package with their own identity, and no SAS goes in the definition (Lab 2) | KSI-IAM-ELP; KSI-IAM-SNU | The Azure role assignment record |
Key Security Indicators are FedRAMP 20x indicators for cloud service providers. The agency authorizes its own system and lines this evidence up with what its providers report against them; it doesn’t report KSIs itself (Book 00, Appendix B). Which output supports which control is the program’s reading (design guidance).
Learning objectives
In this module, you’ll:
- Describe a package, a policy definition and a guest assignment, and the three assignment types.
- Explain what Learn’s cmdlets leave to you, and why each gap matters in a federal pipeline.
- Explain the release registry, and what MC-POL-06 and MC-POL-08 refuse.
- Run the checked build, and compare two builds’ hashes.
- Explain why
IncludeArcMachineshas to be set for Arc-enabled servers.
Prerequisites
- Module 4, for one owning policy per setting, and Chapter 07’s rule of one owning tool per setting.
- Windows with PowerShell 7.1.3 or later and Pester 5.
Unit 2: Where machine configuration sits
8 minutes
A package says what the machine should look like. A definition points at it by URI and hash. Assigning the definition creates a guest assignment on each machine in scope. The assignment type, Audit, ApplyAndMonitor or ApplyAndAutoCorrect, is case sensitive. Read:
- The guide’s Gate 5, step 7: the server convention.
- Chapter 07: where machine configuration sits.
- Chapter 07: what Learn’s cmdlets leave to you.
Unit 3: The registry, the cleaner and the checks
8 minutes
The release registry is the reviewed record of what is in production: one fixed policy ID per configuration, its mode, the last published version, the identity and the owner tag. The cleaner rewrites the MOF into one canonical form. The package check applies seven rules, the definition check nine. Read:
Unit 4: Exercise: Lab 1 (offline, no subscription)
17 minutes
Every command in this lab, in order, from examples/machine-configuration. Each step below says what it does and what to look for.
# Step 1: Save the pinned modules
Save-Module GuestConfiguration -RequiredVersion 4.12.0 -Path ./mcmods
Save-Module PSDesiredStateConfiguration -RequiredVersion 2.0.7 -Path ./mcmods
Save-Module PSDscResources -RequiredVersion 2.12.0.0 -Path ./mcmods
$env:PSModulePath = (Resolve-Path ./mcmods).Path + ';' + $env:PSModulePath
Import-Module PSDesiredStateConfiguration -RequiredVersion 2.0.7
# Step 2: Follow Learn's steps by hand
. ./configurations/TlsServerProtocols.ps1
TlsServerProtocols -OutputPath ./lab/compiled
New-GuestConfigurationPackage -Name TlsServerProtocols -Configuration ./lab/compiled/localhost.mof -Type Audit -Path ./lab/p1 -Force
New-GuestConfigurationPackage -Name TlsServerProtocols -Configuration ./lab/compiled/localhost.mof -Type Audit -Path ./lab/p2 -Force
Get-FileHash ./lab/p1/TlsServerProtocols.zip, ./lab/p2/TlsServerProtocols.zip
# Step 2: Follow Learn's steps by hand
Select-String -Path ./lab/compiled/localhost.mof -Pattern '@GeneratedBy|@GenerationHost|SourceInfo'
# Step 3: See what the checks say about Learn's output
Import-Module ./powershell/McPipeline.psm1
Test-McPackage -Path ./lab/p1/TlsServerProtocols.zip -Name TlsServerProtocols -ExpectedType Audit
# Step 4: Run Microsoft's pinned sample with the current module
cd ../third_party/guest-configuration-samples/package-samples/.example-project
$env:PSModulePath = "$PWD/Modules;" + $env:PSModulePath
New-GuestConfigurationPackage -Name AdministratorsGroupMembers -Configuration ./AdministratorsGroupMembers.mof -Path $env:TEMP/ms -Force
Get-Command Test-GuestConfigurationPackage -ErrorAction SilentlyContinue # nothing: renamed
cd ../../../../machine-configuration
# Step 4: Run Microsoft's pinned sample with the current module
Test-McMofText -Text (Get-Content ../third_party/guest-configuration-samples/package-samples/.example-project/AdministratorsGroupMembers.mof -Raw)
# Step 5: Run the checked build
pwsh ./powershell/Invoke-McBuild.ps1 -Name TlsServerProtocols -Version 1.0.0 `
-ContentUri https://stcontosomc.blob.core.windows.net/machine-configuration -ModulePath ./mcmods -OutputPath ./out
# Step 5: Run the checked build
Select-String -Path ./out/TlsServerProtocols_1.0.0/policy/TlsServerProtocols_1.0.0_AuditIfNotExists.json -Pattern 'contentHash|IncludeArcMachines'
# Step 5: Run the checked build
pwsh ./powershell/Invoke-McBuild.ps1 -Name TlsServerProtocols -Version 1.0.0 `
-ContentUri https://stcontosomc.blob.core.windows.net/machine-configuration -ModulePath ./mcmods -OutputPath ./out2
foreach ($o in './out', './out2') { $p = Get-Content "$o/TlsServerProtocols_1.0.0/provenance.json" -Raw | ConvertFrom-Json; '{0} mof {1} package {2}' -f $o, $p.mof.sha256, $p.package.sha256 }
# Step 6: Break the registry on purpose
(Get-Content -Raw ./config/releases.json).Replace('"published": "0.0.0"', '"published": "1.0.0"') | Set-Content -NoNewline ./lab/releases.json
pwsh ./powershell/Invoke-McBuild.ps1 -Name TlsServerProtocols -Version 1.0.0 -Registry ./lab/releases.json `
-ContentUri https://stcontosomc.blob.core.windows.net/machine-configuration -ModulePath ./mcmods -OutputPath ./lab/out-registry
# Step 6: Break the registry on purpose
(Get-Content -Raw ./lab/releases.json).Replace('"mode": "Audit"', '"mode": "applyAndAutoCorrect"') | Set-Content -NoNewline ./lab/releases.json
pwsh ./powershell/Invoke-McBuild.ps1 -Name TlsServerProtocols -Version 1.0.0 -Registry ./lab/releases.json `
-ContentUri https://stcontosomc.blob.core.windows.net/machine-configuration -ModulePath ./mcmods -OutputPath ./lab/out-registry
# Step 6: Break the registry on purpose
(Get-Content -Raw ./lab/releases.json).Replace('"mode": "applyAndAutoCorrect"', '"mode": "ApplyAndAutoCorrect"') -replace ',\r?\n\s*"owner_tag": \{[^}]*\}', '' | Set-Content -NoNewline ./lab/releases.json
pwsh ./powershell/Invoke-McBuild.ps1 -Name TlsServerProtocols -Version 1.0.0 -Registry ./lab/releases.json `
-ContentUri https://stcontosomc.blob.core.windows.net/machine-configuration -ModulePath ./mcmods -OutputPath ./lab/out-registry
# Step 7: Run the tests
$env:MC_MODULE_PATH = (Resolve-Path ./mcmods).Path
Invoke-Pester ./testsYou follow Learn’s steps by hand, check what they produce, run the checked build twice, and refuse three bad registry entries. Work in examples/machine-configuration, in PowerShell 7 on Windows. Nothing here signs in, uploads or installs the machine configuration agent; step 1 downloads three pinned modules from the PowerShell Gallery into a folder. These are the steps of Chapter 07, Lab 1, which explains each result. The expected results below are the chapter’s.
Save the pinned modules to a folder rather than installing them:
Save-Module GuestConfiguration -RequiredVersion 4.12.0 -Path ./mcmods Save-Module PSDesiredStateConfiguration -RequiredVersion 2.0.7 -Path ./mcmods Save-Module PSDscResources -RequiredVersion 2.12.0.0 -Path ./mcmods $env:PSModulePath = (Resolve-Path ./mcmods).Path + ';' + $env:PSModulePath Import-Module PSDesiredStateConfiguration -RequiredVersion 2.0.7Follow Learn’s steps by hand. Compile the configuration and package it twice from the same MOF:
. ./configurations/TlsServerProtocols.ps1 TlsServerProtocols -OutputPath ./lab/compiled New-GuestConfigurationPackage -Name TlsServerProtocols -Configuration ./lab/compiled/localhost.mof -Type Audit -Path ./lab/p1 -Force New-GuestConfigurationPackage -Name TlsServerProtocols -Configuration ./lab/compiled/localhost.mof -Type Audit -Path ./lab/p2 -Force Get-FileHash ./lab/p1/TlsServerProtocols.zip, ./lab/p2/TlsServerProtocols.zipExpected: two packages from one MOF, with two different hashes. Now look inside the MOF:
Select-String -Path ./lab/compiled/localhost.mof -Pattern '@GeneratedBy|@GenerationHost|SourceInfo'Expected: your user name and computer name are in the header, and your folder path is in every
SourceInfo.See what the checks say about Learn’s output.
Import-Module ./powershell/McPipeline.psm1 Test-McPackage -Path ./lab/p1/TlsServerProtocols.zip -Name TlsServerProtocols -ExpectedType AuditExpected: three rows, two
MC-PKG-04findings (build-host data, absolute path) and theMC-PKG-07warning (no version in the name).Run Microsoft’s pinned sample with the current module.
cd ../third_party/guest-configuration-samples/package-samples/.example-project $env:PSModulePath = "$PWD/Modules;" + $env:PSModulePath New-GuestConfigurationPackage -Name AdministratorsGroupMembers -Configuration ./AdministratorsGroupMembers.mof -Path $env:TEMP/ms -Force Get-Command Test-GuestConfigurationPackage -ErrorAction SilentlyContinue # nothing: renamed cd ../../../../machine-configurationExpected: the package builds, and
Get-Commandreturns nothing. Now check Microsoft’s published MOF:Test-McMofText -Text (Get-Content ../third_party/guest-configuration-samples/package-samples/.example-project/AdministratorsGroupMembers.mof -Raw)Expected: nothing. Microsoft’s published MOF carries no build-host data, the form the cleaner produces.
Run the checked build.
pwsh ./powershell/Invoke-McBuild.ps1 -Name TlsServerProtocols -Version 1.0.0 ` -ContentUri https://stcontosomc.blob.core.windows.net/machine-configuration -ModulePath ./mcmods -OutputPath ./outExpected: exit 0, with one information row,
MC-POL-09:IncludeArcMachinesdefaults to false. FindcontentHashand that parameter in the definition:Select-String -Path ./out/TlsServerProtocols_1.0.0/policy/TlsServerProtocols_1.0.0_AuditIfNotExists.json -Pattern 'contentHash|IncludeArcMachines'Build again into
./out2, and compare the twoprovenance.jsonfiles:pwsh ./powershell/Invoke-McBuild.ps1 -Name TlsServerProtocols -Version 1.0.0 ` -ContentUri https://stcontosomc.blob.core.windows.net/machine-configuration -ModulePath ./mcmods -OutputPath ./out2 foreach ($o in './out', './out2') { $p = Get-Content "$o/TlsServerProtocols_1.0.0/provenance.json" -Raw | ConvertFrom-Json; '{0} mof {1} package {2}' -f $o, $p.mof.sha256, $p.package.sha256 }Expected: the
mofhashes match (they startecffd795), and the package hashes don’t.Break the registry on purpose. The first command writes a copy of the registry with
publishedraised to1.0.0; the build reads the copy and writes to./lab/out-registry:(Get-Content -Raw ./config/releases.json).Replace('"published": "0.0.0"', '"published": "1.0.0"') | Set-Content -NoNewline ./lab/releases.json pwsh ./powershell/Invoke-McBuild.ps1 -Name TlsServerProtocols -Version 1.0.0 -Registry ./lab/releases.json ` -ContentUri https://stcontosomc.blob.core.windows.net/machine-configuration -ModulePath ./mcmods -OutputPath ./lab/out-registryExpected: exit 2,
MC-POL-06. Now give the mode the wrong case:(Get-Content -Raw ./lab/releases.json).Replace('"mode": "Audit"', '"mode": "applyAndAutoCorrect"') | Set-Content -NoNewline ./lab/releases.json pwsh ./powershell/Invoke-McBuild.ps1 -Name TlsServerProtocols -Version 1.0.0 -Registry ./lab/releases.json ` -ContentUri https://stcontosomc.blob.core.windows.net/machine-configuration -ModulePath ./mcmods -OutputPath ./lab/out-registryExpected: exit 1, because the mode is case sensitive. Fix the case, remove
owner_tag, and build again:(Get-Content -Raw ./lab/releases.json).Replace('"mode": "applyAndAutoCorrect"', '"mode": "ApplyAndAutoCorrect"') -replace ',\r?\n\s*"owner_tag": \{[^}]*\}', '' | Set-Content -NoNewline ./lab/releases.json pwsh ./powershell/Invoke-McBuild.ps1 -Name TlsServerProtocols -Version 1.0.0 -Registry ./lab/releases.json ` -ContentUri https://stcontosomc.blob.core.windows.net/machine-configuration -ModulePath ./mcmods -OutputPath ./lab/out-registryExpected: exit 2,
MC-POL-08: an enforcing definition with no owner filter.Run the tests.
$env:MC_MODULE_PATH = (Resolve-Path ./mcmods).Path Invoke-Pester ./testsExpected: 65 tests passing (49, with 16 skipped, if
MC_MODULE_PATHisn’t set).
Check your work: for any package and definition, you can say whether it was built from the reviewed source, whether it matches what the registry allows, and which hash proves what shipped.
Unit 5: Exercise: Lab 2 (your test subscription)
Not timed. Not run by the authors.
Test the package on a disposable server, publish the exact bytes Lab 1 built to a storage account with anonymous access off, grant the machines read with their own identity, publish the definition under the registry’s policy ID, assign it with IncludeArcMachines set to true, and read each machine’s compliance against the hash in provenance.json. Follow Chapter 07, Lab 2.
- You need: a test subscription and a server you can rebuild, never production. Learn says local testing installs the agent and needs elevation, and remediation changes the machine it runs on.
- Back it out: step 8 deletes the assignment, the definition, the blob and the role assignment, then rebuilds the disposable server.
Unit 6: Knowledge check
5 minutes
- You run
New-GuestConfigurationPackagetwice on the same MOF. What do you get?- Two identical packages.
- Two packages with different hashes, so a rebuilt package won’t match the reviewed definition.
- An error on the second run.
- What does MC-PKG-04 refuse?
- A package over 100 MB.
- A MOF that still carries build-host data, such as the user name, host name or full path.
- A package with no version in its name.
- The registry’s
modeisapplyAndAutoCorrect. What happens?- The build accepts it.
- The build exits 1: the assignment type is case sensitive.
- The build lowers it to
Audit.
- You assign a definition to an Arc-enabled server and leave
IncludeArcMachinesat its default. What happens?- The server is evaluated as usual.
- Nothing happens, and nothing says so; the build’s MC-POL-09 row reminds you.
- The assignment fails.
- b. See what Learn’s cmdlets leave to you and step 2 of the lab.
- b. See check the package and step 3 of the lab.
- b. See where machine configuration sits and step 6 of the lab.
- b. See step 5 of Chapter 07, Lab 1 and step 6 of Lab 2.
Unit 7: Summary
2 minutes
You followed Learn’s procedure, saw where its outputs fall short, and ran the build that makes the same MOF every time, checks the package and definition, and refuses what the registry doesn’t allow.
Evidence an ISSO keeps from this module: both provenance.json files and findings.json in the evidence store, the registry and its pull requests, and in Lab 2 the published blob’s hash, the role assignment, the definition’s activity log entry and each machine’s compliance against the recorded hash. Before shipping, work through the Chapter 07 checklist.