Microsoft 365 Device Management

Building Intune Configuration Profiles with the Settings Catalog and ADMX Ingestion

Group Policy was a declarative system that wrote registry keys over a domain channel. Intune configuration is also declarative, but it writes through the Configuration Service Provider (CSP) surface over MDM. The mental model translates, but the tooling does not — and the single biggest mistake teams make is treating the Settings Catalog like a GPMC clone instead of a CSP front-end. This guide builds a hardened baseline the way a platform team should: pick the right profile type, ingest the ADMX you actually need, scope with filters, resolve conflicts deterministically, and version-control the whole thing as JSON through Microsoft Graph.

1. Settings Catalog vs templates vs administrative templates

Intune gives you three overlapping ways to author device configuration, and they are not interchangeable. Choosing wrong means you author a setting that silently never applies, or you split one logical baseline across three profile types nobody can audit.

Profile type What it is When to use
Settings Catalog A flat, searchable list of every CSP-backed setting Intune exposes. No opinionated grouping. Default choice for anything new. Mix Windows, Edge, Office, and OneDrive settings in one profile.
Templates (e.g. Device restrictions, Endpoint protection) Curated, fixed groups of settings with a guided UI. Legacy / familiarity. Microsoft is steering everything toward the catalog; avoid for greenfield.
Administrative Templates (ADMX) A GPO-like tree of ADMX-backed policies, including ingested third-party ADMX. When you need a specific ADMX setting (Chrome, custom LOB app) that is not surfaced in the catalog.

The Settings Catalog and Administrative Templates are both ADMX-aware under the hood for many Windows settings. The practical difference: the catalog is a flat search experience that also covers non-ADMX CSPs, while the Administrative Templates profile mirrors the familiar Computer Configuration / User Configuration tree. For a brand-new baseline, default to the Settings Catalog and only reach for Administrative Templates when a setting genuinely is not in the catalog.

The decision rule I give teams: start in the Settings Catalog. If the setting is not there, check whether the owning vendor ships an ADMX. If it does, ingest it (Section 3). If it does not, you are looking at a custom OMA-URI profile — which is a different article and a maintenance liability you want to minimize.

2. How Settings Catalog maps to Windows CSPs under the hood

Every setting you toggle in the catalog resolves to a CSP node and a value. When the profile is assigned, Intune sends the OMA-DM payload to the device, the MDM stack hands it to the relevant CSP, and the CSP writes the effective configuration (often, but not always, the registry). Understanding this matters because the CSP is the source of truth for behavior, not the friendly name in the portal.

Take a concrete example. In the catalog you might enable, under Administrative Templates > Windows Components > BitLocker Drive Encryption, the OS-drive encryption settings. Those resolve to the BitLocker CSP (./Device/Vendor/MSFT/BitLocker). A setting like Minimum PIN length maps to ./Device/Vendor/MSFT/BitLocker/SystemDrivesMinimumPINLength. If two profiles set that node to different values, the CSP — not the portal — decides what happens, and you get a conflict (Section 5).

A few CSP realities that bite GPO veterans:

When you export a profile to JSON (Section 8), you see the catalog’s internal setting IDs, which look like device_vendor_msft_bitlocker_systemdrivesminimumpinlength. That string is your durable identifier — friendly names get re-labelled, IDs do not.

3. Importing third-party and custom ADMX/ADML for app policy

Chrome, custom LOB apps, and some Edge/Office settings ship as ADMX you must ingest before Intune can author them. Ingestion uploads the ADMX (definitions) plus at least one ADML (language strings) into the tenant, after which the settings appear in the Administrative Templates profile authoring surface. Edge and Office for the most part are already built into the catalog, so you usually only ingest Chrome and in-house ADMX.

In the admin center: Devices > Configuration > Import ADMX. Upload the .admx, then the matching .adml for your locale (e.g. en-US). Status moves from uploadInProgress to available. Only then can you create an Administrative Templates profile that references it.

The Graph entity is groupPolicyUploadedDefinitionFile. Its policyType distinguishes the two ADMX worlds:

Upload the ADMX first, then attach the ADML with the addLanguageFiles action. ADML content is base64-encoded:

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

$admx = [Convert]::ToBase64String([IO.File]::ReadAllBytes("C:\admx\chrome.admx"))
$adml = [Convert]::ToBase64String([IO.File]::ReadAllBytes("C:\admx\en-US\chrome.adml"))

$body = @{
  "@odata.type"        = "#microsoft.graph.groupPolicyUploadedDefinitionFile"
  fileName             = "chrome.admx"
  displayName          = "Google Chrome"
  defaultLanguageCode  = "en-US"
  content              = $admx
  groupPolicyUploadedLanguageFiles = @(
    @{
      "@odata.type" = "#microsoft.graph.groupPolicyUploadedLanguageFile"
      fileName      = "chrome.adml"
      languageCode  = "en-US"
      content       = $adml
    }
  )
} | ConvertTo-Json -Depth 5

Invoke-MgGraphRequest -Method POST `
  -Uri "https://graph.microsoft.com/beta/deviceManagement/groupPolicyUploadedDefinitionFiles" `
  -Body $body -ContentType "application/json"

Ingested ADMX has hard limits. The ADMX file must be under the documented size cap (around 1 MB), it cannot reference settings already delivered by a built-in ADMX, and a small number of complex policy element types are unsupported. If ingestion sticks in uploadFailed, it is almost always an unsupported element or a missing referenced namespace, not a transient error. Validate the ADMX renders cleanly in on-prem GPMC first.

After it reaches available, the Chrome settings appear under Administrative Templates and you author them like any other policy — for example Set the default search provider, or Disable saving browser history.

4. Authoring a hardened baseline and scoping it with filters

Now the actual baseline. Create it under Devices > Configuration > Create > New policy, platform Windows 10 and later, profile type Settings Catalog. Add settings by searching the catalog. A credible Windows hardening starter set, all CSP-backed:

Setting (catalog path) Backing CSP node Value
BitLocker > Require device encryption BitLocker/RequireDeviceEncryption Enabled
BitLocker > System drives minimum PIN length BitLocker/SystemDrivesMinimumPINLength 6
DeviceLock > Min device password length DeviceLock/MinDevicePasswordLength 12
Microsoft Defender > Real-time monitoring Defender/AllowRealtimeMonitoring Allowed
LocalPoliciesSecurityOptions > Smart Screen Browser/AllowSmartScreen Enabled
Administrative Templates > Disable LM hash LanmanWorkstation / security options Enabled

Keep one baseline per platform and per purpose. Do not bury Edge settings, BitLocker, and password policy in three profiles when one catalog profile holds them all — fewer profiles means fewer conflict surfaces.

Scope with filters, not group sprawl. Assign the profile to a broad group (e.g. All devices or All corporate Windows), then narrow with an assignment filter evaluated at assignment time. Filters keep your group model flat and your targeting precise.

Create a filter under Tenant administration > Filters. The rule uses Intune device properties with -eq, -ne, -startsWith, -contains, and version operators. Example: only corporate-owned Windows 11 workstations.

(device.deviceOwnership -eq "Corporate") and (device.osVersion -startsWith "10.0.2")

Provision it via Graph as a deviceAndAppManagementAssignmentFilter:

$filter = @{
  displayName = "Corp Win11 Workstations"
  platform    = "windows10AndLater"
  rule        = '(device.deviceOwnership -eq "Corporate") and (device.osVersion -startsWith "10.0.2")'
  assignmentFilterManagementType = "devices"
} | ConvertTo-Json

Invoke-MgGraphRequest -Method POST `
  -Uri "https://graph.microsoft.com/beta/deviceManagement/assignmentFilters" `
  -Body $filter -ContentType "application/json"

Then on the profile’s Assignments tab, add the group and set the filter to Include (apply only to matches) or Exclude. Filters are evaluated per-assignment, so the same broad group can carry different profiles narrowed to different device shapes. A tenant supports up to 200 filters.

5. Policy conflict resolution: precedence, ‘not configured’, and last-writer behavior

This is where multi-profile estates fall apart. Intune’s conflict model is not “highest priority wins” for most configuration profiles — it is closer to “a real conflict on the same CSP node results in Conflict, and the device gets an undefined winner.” The rules you must internalize:

The operational discipline that prevents 90% of conflicts: maintain a single source-of-truth spreadsheet (or, better, the JSON in git per Section 8) mapping every CSP node you configure to exactly one profile. If two profiles touch the same node, that is a code-review failure, not a runtime surprise. Treat overlapping nodes the way you treat two Terraform resources managing the same cloud object.

When you genuinely need a per-group override (e.g. executives get a looser PIN), do it by scoping, not overlap: profile A excludes the exec filter, profile B includes it. Same node, but never on the same device at the same time.

6. Migrating GPOs with Group Policy analytics and the migration report

Before you reauthor a single GPO by hand, run it through Group Policy analytics. It parses an exported GPO backup, cross-references every setting against the CSP database, and tells you what is MDM-supported, deprecated, or unavailable — then lets you migrate the supported settings straight into a Settings Catalog profile.

Workflow:

  1. On a domain controller or management box, export the GPO: Backup-GPO -Name "Corp Baseline" -Path C:\gpobackup, or GPMC > right-click GPO > Back Up. Analytics consumes the *.xml report inside the backup.
  2. In Intune: Devices > Group Policy analytics > Import, upload the GPO report XML.
  3. Intune produces a migration report with a per-setting MDM support percentage and a breakdown of each setting’s status.

The Graph entities are groupPolicyObjectFiles (the uploaded GPO) and groupPolicyMigrationReports (the analysis result):

# List migration reports and their MDM coverage
$reports = Invoke-MgGraphRequest -Method GET `
  -Uri "https://graph.microsoft.com/beta/deviceManagement/groupPolicyMigrationReports"

$reports.value | ForEach-Object {
  [pscustomobject]@{
    Ou               = $_.ouDistinguishedName
    SupportedSettings = $_.migrationReadiness
    TotalSettings    = $_.totalSettingsCount
    SupportedCount   = $_.supportedSettingsCount
  }
} | Format-Table -AutoSize

The report classifies each setting into one of three buckets that map directly to your migration plan:

Report status Meaning Action
Supported (MDM) Setting has a CSP and Intune surfaces it Migrate to Settings Catalog (analytics can do this for you)
Deprecated Setting exists but Microsoft no longer recommends it Drop it; find the modern equivalent
Not available No CSP / no MDM mapping Re-architect: filter, script, or accept the gap

From the migration report you can click Migrate to generate a Settings Catalog profile pre-populated with the supported settings. Treat that as a draft — review every value, then export to JSON and put it under version control rather than assigning it blind.

7. Per-setting reporting and diagnosing ‘Error’/‘Conflict’ states

A profile that says “85% success” is hiding the 15% that matters. Drill into Devices > Configuration > [profile] > View report, then Per setting status, which gives you a row per setting with Success / Error / Conflict / Not applicable counts.

The three failure modes and how to read them:

For a stuck device, pull the per-device per-setting state from Graph and the on-device MDM diagnostics. The catalog’s deviceStatuses surface the applied state:

$policyId = "<configurationPolicyId>"
Invoke-MgGraphRequest -Method GET `
  -Uri "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies/$policyId/deviceStatuses" |
  Select-Object -ExpandProperty value |
  Where-Object status -ne "compliant" |
  Format-Table id, status, lastReportedDateTime -AutoSize

On the device itself, the authoritative log is the MDM diagnostics. Generate the report and read the CSP results:

# On the affected Windows endpoint
mdmdiagnosticstool.exe -area DeviceProvisioning -cab C:\temp\mdm.cab
# Or pull the live policy area into a readable report:
mdmdiagnosticstool.exe -out C:\temp\MDMReport

The generated MDMDiagReport.html lists every applied policy area with its result code. A 0x80070032 (not supported) or 0x86000020-class result pinpoints exactly which CSP node refused the value — far faster than guessing from the portal’s aggregate counts.

8. Exporting, version-controlling, and reimporting profiles as JSON via Graph

The endgame: your baseline lives in git, not in someone’s portal session. The Settings Catalog profile is just a JSON document at /beta/deviceManagement/configurationPolicies/{id} with an expandable settings collection. Export it, commit it, diff it, and reimport it into another tenant.

Export a profile with its settings expanded:

$id = "<configurationPolicyId>"
$policy = Invoke-MgGraphRequest -Method GET `
  -Uri "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies/$id`?`$expand=settings"

# Strip server-managed fields so it re-imports cleanly
$export = $policy | Select-Object name, description, platforms, technologies, roleScopeTagIds, settings
$export | ConvertTo-Json -Depth 20 | Out-File ".\baselines\win-hardening-v3.json" -Encoding utf8

Commit win-hardening-v3.json. Now you get real diffs in pull requests — a reviewer sees that systemdrivesminimumpinlength went from 6 to 8 before it ever reaches a device. The internal setting IDs (e.g. device_vendor_msft_bitlocker_systemdrivesminimumpinlength) are stable, so diffs are meaningful across exports.

Reimport into a new tenant or restore a known-good version:

$json = Get-Content ".\baselines\win-hardening-v3.json" -Raw | ConvertFrom-Json

$body = @{
  name         = $json.name
  description  = $json.description
  platforms    = $json.platforms        # e.g. "windows10"
  technologies = $json.technologies      # e.g. "mdm"
  roleScopeTagIds = $json.roleScopeTagIds
  settings     = $json.settings
} | ConvertTo-Json -Depth 20

Invoke-MgGraphRequest -Method POST `
  -Uri "https://graph.microsoft.com/beta/deviceManagement/configurationPolicies" `
  -Body $body -ContentType "application/json"

platforms and technologies are required and must match the original (windows10 / mdm for a Windows catalog profile). The most common reimport failure is dropping the settings collection’s nested @odata.type discriminators during a hand-edit — keep -Depth 20 on every ConvertTo-Json so nothing gets truncated. Assignments do not travel with the policy export; reapply groups and filters separately (deliberately, so you never copy a production assignment into a test tenant by accident).

This is the bridge from clicking-in-a-portal to GitOps for endpoint config: PRs, review, environments, and the ability to roll back a baseline by re-POSTing last week’s JSON.

Enterprise scenario

A platform team running a ~22,000-seat tenant was mid-migration off on-prem GPO. They had a single legacy “Workstation Baseline” GPO with ~340 settings and tried to lift-and-shift it by hand into one Settings Catalog profile, then assigned it to All devices. Within a day, the per-setting report lit up with Conflict on the password and BitLocker settings, and a cluster of Error states on a fleet of TPM-less hypervisor-hosted VDI images.

Two root causes. First, the conflict: a separate, older “VDI Security” profile already set DeviceLock/MinDevicePasswordLength to a different value on the same machines — two profiles, one CSP node, differing values, undefined winner. Second, the errors: the BitLocker settings were genuinely Not applicable on the TPM-less VDI but the team had forced RequireDeviceEncryption, so the CSP returned a hard error rather than skipping.

The fix was discipline, not more profiles. They ran the legacy GPO through Group Policy analytics first, which flagged 38 of the 340 settings as Not available (no CSP) — settings they had been about to reauthor pointlessly. They split the baseline by purpose (password, BitLocker, Defender, browser), kept each CSP node owned by exactly one profile, and used assignment filters to carve the VDI estate out of BitLocker entirely instead of letting it error:

(device.deviceModel -ne "Virtual Machine") and (device.osVersion -startsWith "10.0.2")

That filter, set to Include on the BitLocker profile, meant the VDI images were Not applicable by design — clean reporting, no errors. Every profile was exported to JSON and committed, so the next change went through a pull request where the password-length bump was visible as a one-line diff. Conflicts dropped to zero, and the “85% success” number became a real 99.x% because the remaining gaps were intentional scoping, not silent failures.

The lesson the team wrote into their runbook: a Conflict is a design defect introduced at authoring time, and an Error is usually a scoping defect — neither is a device problem. Fix them in the profile model and the filters, never by re-pushing policy at the endpoint.

Verify

Confirm the baseline is actually applying — not just assigned — before you call it done.

Checklist

IntuneSettings CatalogADMXConfiguration ProfilesCSP

Comments

Keep Reading