Automating Intune Security Baselines with Microsoft Graph

Automating Intune security baselines with Microsoft Graph — header banner on SecurityScriptographer

Clicking through a security baseline once is fine. Doing it across a dozen tenants, keeping every one on the current version, and proving that devices actually match the baseline is where the portal runs out of road. This post is about how to automate Intune security baselines with Microsoft Graph and PowerShell — enumerating what is deployed, assigning baselines to groups, catching version drift, and exporting a baseline as code so it is reproducible rather than a thing someone configured by hand eight months ago.

Key Takeaways

  • To automate Intune security baselines you work against the Settings Catalog engine — baselines now live on the deviceManagement/configurationPolicies endpoint, not the retired intents API.
  • You can list every deployed baseline and its version by filtering configuration policies on templateReference/templateFamily eq 'Baseline'.
  • The templateDisplayVersion on each policy is your drift signal: a baseline pinned to an old template version is the automatable "you are behind" check.
  • Exporting a configured baseline to JSON turns it into baseline-as-code you can store in source control and replay into another tenant.
  • Per-setting compliance reporting is the weakest link — the API exposes it through export jobs, and the portal is still the better place to drill into why one device does not match.

Environment

  • Microsoft Intune with the Windows security baselines feature (Security Baseline for Windows, Microsoft Defender for Endpoint, Microsoft Edge, and the Windows 365 Security Baseline for Cloud PCs).
  • Microsoft.Graph.Beta.DeviceManagement PowerShell module — the baseline resources are still beta-only in Graph.
  • An app registration or signed-in account with DeviceManagementConfiguration.ReadWrite.All.
  • Entra ID security groups to target, ideally dynamic groups so assignment scope maintains itself.

The Problem

Intune security baselines used to be built on the "intents" model, and a lot of automation examples still floating around call the deviceManagement/intents endpoint. That path is a dead end for new work: since late March 2025 the intents API no longer accepts creation of Windows endpoint security policies, and baselines released since late 2023 are built on the unified Settings Catalog format instead. Each baseline setting now takes its name and options directly from the configuration service provider it maps to, which is a genuine improvement — but it means the automation surface moved, and anything targeting the old endpoint quietly manages nothing.

The three things the portal makes tedious are the three worth automating. Assigning the same baseline consistently across groups and tenants is copy-paste work that drifts the moment someone forgets a step. Keeping baselines current is manual — Microsoft ships new baseline versions periodically, and a policy pinned to an old version does not update itself; you have to notice. And answering "are we actually compliant with the baseline" from the portal is a per-policy click-through, not something you can drop into a report. Graph does not fix all three cleanly, but it fixes the first two well and gives you a real handle on the third.

The Solution

Step 1 — Connect and find where baselines actually live

Baselines are configuration policies with a template reference whose family is Baseline. That single filter is the key to the whole exercise — it separates baselines from the hundreds of ordinary Settings Catalog policies in a tenant:

Connect-MgGraph -Scopes "DeviceManagementConfiguration.ReadWrite.All"

# Every deployed security baseline, with its template and version
Get-MgBetaDeviceManagementConfigurationPolicy `
    -Filter "templateReference/templateFamily eq 'Baseline'" |
    Select-Object Name,
        @{ N = 'Template';  E = { $_.TemplateReference.TemplateDisplayName } },
        @{ N = 'Version';   E = { $_.TemplateReference.TemplateDisplayVersion } },
        Id |
    Format-Table -AutoSize
PowerShell output automating Intune security baselines: deployed baselines with their template and version from Microsoft Graph

The inventory the portal will not give you on one screen: every deployed baseline with its template and the version it is pinned to.

This is the inventory the portal does not give you on one screen: which baselines exist, which template each came from, and — critically — the version each is pinned to. In a healthy tenant every baseline of a given type sits on the same, current version. Where they do not, you have found drift.

Step 2 — List the available baseline templates and their latest versions

To know whether a deployed baseline is current, you need the catalogue of templates Microsoft publishes. The configuration policy templates endpoint, filtered the same way, gives you every baseline template and its version — the newest version number is the target every deployed policy should be on:

Get-MgBetaDeviceManagementConfigurationPolicyTemplate `
    -Filter "templateFamily eq 'Baseline'" |
    Select-Object DisplayName, DisplayVersion, LifecycleState, Id |
    Sort-Object DisplayName, DisplayVersion |
    Format-Table -AutoSize
Microsoft Graph PowerShell listing Intune baseline templates with DisplayVersion and LifecycleState including a superseded version

The published baseline template catalogue with DisplayVersion and LifecycleState — a superseded state is the authoritative "update due" signal.

The LifecycleState is worth watching — templates move to a superseded or deprecated state when a newer version ships, which is the authoritative signal that a baseline is due for an update rather than your own guess about what "latest" means.

Step 3 — Assign a baseline to groups programmatically

Assignment is the highest-value automation because it is pure repetition and the easiest thing to get subtly wrong by hand. The assign action takes the target groups and replaces the policy's assignment set, so it is idempotent — running it again with the same groups is a no-op:

$policyId = ""
$groupId  = ""

$body = @{
    assignments = @(
        @{
            target = @{
                "@odata.type" = "#microsoft.graph.groupAssignmentTarget"
                groupId       = $groupId
            }
        }
    )
}

Invoke-MgGraphRequest -Method POST `
    -Uri "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies/$policyId/assign" `
    -Body ($body | ConvertTo-Json -Depth 6)

Targeting a dynamic group rather than a static one is what makes this set-and-forget: new devices that match the membership rule inherit the baseline without anyone re-running assignment. Dynamic membership is the same lever I lean on for onboarding automation in automating phishing simulations for new employees in M365 — the pattern of "let the group rule do the work" applies just as well to baselines.

Step 4 — Export a baseline as code

A baseline that exists only in one tenant's portal is not reproducible. Pulling the full policy with its settings gives you a JSON artifact you can commit to source control, review in a pull request, and replay into another tenant. Expand the settings when you read it back:

$policy = Get-MgBetaDeviceManagementConfigurationPolicy `
    -DeviceManagementConfigurationPolicyId $policyId `
    -ExpandProperty "settings"

$policy | ConvertTo-Json -Depth 20 |
    Out-File ".\baseline-windows-$(Get-Date -Format yyyyMMdd).json" -Encoding utf8

To replay it, strip the tenant-specific fields (id, timestamps, assignment IDs), keep templateReference, name, and settings, and POST the object back to configurationPolicies. Treating baselines this way — configure once, export, deploy from the export — is what turns "security baseline management" from a portal chore into something with change history, which is a defensible position when an auditor asks who changed what and when.

Step 5 — Report on baseline compliance honestly

This is the step where I have to be straight about the limits. Settings Catalog and baseline policies do not expose a clean per-setting compliance cmdlet the way you might hope. The device-level assignment status is reachable, but the granular "which setting on which device does not match" data comes out of the Intune reports API through export jobs — you request a report, poll for it, and download a file, rather than getting a tidy object back:

# Kick off a device-status export for a configuration policy
$report = @{
    reportName = "DeviceConfigurationPolicyStatus"
    filter     = "(PolicyId eq '$policyId')"
    format     = "json"
}
Invoke-MgGraphRequest -Method POST `
    -Uri "https://graph.microsoft.com/beta/deviceManagement/reports/exportJobs" `
    -Body ($report | ConvertTo-Json)

For a quick "is this baseline green across the fleet" answer, the export job is enough to feed a dashboard. For the harder question — why one device is not compliant with a specific setting — the portal's per-setting view under the baseline is still the faster tool, and I am not going to pretend otherwise. Automate the assignment and the version tracking; use the portal for the last-mile drill-down. Microsoft's baseline configuration guide documents the reporting surface as it stands.

Frequently Asked Questions

Can I create an Intune security baseline entirely through Microsoft Graph?

Technically yes — you can POST a configuration policy with a baseline templateReference and a full settings array to configurationPolicies. In practice, hand-authoring the settings JSON for an entire baseline is painful and error-prone, so the realistic pattern is to configure one baseline in the portal, export it as code, and deploy that export. Graph is better at replicating and assigning baselines than at authoring them from scratch.

Why does the deviceManagement/intents endpoint not show my baselines?

Because baselines moved to the Settings Catalog format. Baselines released since late 2023 live on deviceManagement/configurationPolicies with a templateFamily of Baseline, and since late March 2025 the old intents API no longer supports creating Windows endpoint security policies at all. Any automation still pointed at intents is managing an empty set.

How do I detect that a security baseline is on an outdated version?

Compare the templateDisplayVersion on each deployed baseline (Step 1) against the newest displayVersion in the template catalogue (Step 2), and watch the template lifecycleState for superseded. A deployed baseline whose template version is behind the latest published version is the drift you want to flag and update.

What license do I need to automate security baselines?

Security baselines require an Intune license and are included with the plans that carry Intune (Microsoft 365 E3/E5, and the standalone Intune plans). The Graph automation itself needs no extra license beyond that — just an identity with DeviceManagementConfiguration.ReadWrite.All. The Windows 365 Security Baseline applies specifically to Cloud PCs.

Does updating a baseline to a new version reset my customizations?

Updating the version brings in new and changed settings from the template, but Intune preserves the customizations you made where the setting still exists. You should still review the change before assigning it broadly — a new version can flip defaults or introduce settings that conflict with an existing policy, which is exactly the kind of thing the export-and-diff approach in Step 4 surfaces.

Conclusion

Automating security baselines is not the same as writing them, and being honest about that split is the whole point. Graph is genuinely good at the repetitive, drift-prone parts: inventorying what is deployed, assigning to dynamic groups, tracking template versions, and exporting a baseline into something you can version-control. Those are the parts that go wrong at scale, and they are the parts you can now handle without touching the portal.

Where it is weaker — per-setting compliance drill-down — the portal is still the better tool, and pretending the reports API is a clean substitute would just cost you an afternoon. Automate assignment and version tracking, keep your baselines as exported code, and reach for the portal for the last-mile "why is this one device red" question. That division of labour is the realistic version of baseline management, and it holds up whether you are running one tenant or ten.

Related Posts

Editorial note: posts on this blog are drafted with AI assistance and then reviewed, edited, and tested against a real environment before publishing. Commands, output, and screenshots come from systems I actually ran the work on.

Endpoint Security Microsoft 365 Security Microsoft Graph Microsoft Intune PowerShell Sysadmin
SecurityScriptographer author

About the author

SecurityScriptographer is written and maintained by one person — a defender who builds and tests the detections, scripts, and Microsoft 365 workflows here before publishing them. More about me · @twi_nox

0 comments:

Post a Comment