Series · Chrome Policy Manager: from intent to verified policy · Part 1 of 3
Microsoft Intune · Chrome Enterprise · ADMX · Policy Management · Azure
Chrome Policy Manager, part 1: create and assign policies
Work through templates, drafts and activation, including the assignment references you must update before a newly active policy reaches devices.
- Drafted
- Published
- Updated
- Reading time
- 10 min
- Author
- Roberto Gramellini
Source:
Chrome Policy Manager
· revision a3f63874
- Chrome Policy Manager
- PolicySet version lifecycle
- ADMX catalog versions
- Chrome policy authoring
- Intune assignments
Chrome Policy Manager, part 1: create and assign policies
A note before we start. In a Microsoft-centric enterprise, I'd generally start with Microsoft Edge for managed browsing. Many organizations already do. This project is for teams whose primary browser is Google Chrome; it is not a reason to replace Edge.
Even among Chrome teams, the need is fairly specific: some want to work with new Chrome policy definitions before their chosen management surface exposes them. That is the motivation here. Most organizations do not need another policy platform, and importing the newest template certainly does not validate its settings against every Chrome version in the fleet.
Suppose an endpoint team wants to pilot one Chrome setting while leaving everyone else's baseline alone. If the pilot goes badly, they need to explain exactly what changed and put it back. The setting may be simple. Keeping template versions, authored settings, assignments and the browser's registry state in agreement is less simple, especially when different teams own them.
Chrome Policy Manager puts authoring, version tracking and effective-value resolution in a server application. Intune still delivers the PowerShell scripts. Those scripts fetch the resolved settings, reconcile the Windows registry and report back. There is no new browser-management protocol here, and Intune still has a job. What changes is where policy intent is authored and resolved.
This expands the earlier short overview at the same URL. I'll start with the catalog and policy versions. Then part 2 examines resolution, registry representation and drift, and part 3 covers user context, trust boundaries and the optional worker. The source references stay at revision a3f6387445f789cb6fef3409973fd2e2f969abed; later branches may behave differently.
Check whether you need another service
Check the Settings Catalog and Chrome ADMX ingestion before building a service. They may already cover the requirement. This project's concern is the work around imported template versions: overlapping namespaces, references that make old definitions difficult to retire, and assignments that become harder to explain as policies change.
Imagine the browser baseline belongs to one team, Intune to another, and the service desk has the ticket. They need more than an assignment screenshot. Which template was used? Which settings version does the assignment reference? What did the API return? What did Chrome actually consume? These are reasonable questions to ask before the meeting, if possible.
The project models those records separately, which helps if you want explicit intent and behavior you can inspect in source. You also inherit an application, database, Graph permissions, certificates and a client lifecycle. I'd count that operational cost honestly. The ADMX bottleneck may disappear, but its replacement still needs an owner.
From the portal to the registry
Administrators browse the catalog, maintain PolicySets and versions, and assign groups in the admin application. The API stores those objects and resolves requests into settings. On Windows, scripts detect and repair local state. There is also an optional worker for selected privileged cloud actions; leave that aside until part 3.
The endpoint does not interpret the ADMX catalog entry. That entry helps author the policy; the stored PolicySet version holds SettingsJson. The effective-policy service merges eligible assignments and versions, then the client converts the returned values into Chrome's expected registry representation.
Catalog versions can therefore coexist without another template import for each endpoint change. Browser compatibility is still your test to run, including supported values and conflicts with other management sources. The server cannot make an older browser understand a newer setting.
Importing a template does not activate it automatically
The template registry lives in AdmxCatalogService.cs. An import replaces entries for that template version only. It deduplicates by policy name and recommended status, attaches the entries to the template record, then recomputes policy and category counts.
If no template is active, the first new import becomes active. Later imports normally arrive staged unless activation was explicitly requested. Activating one retires the previously active template. You can inspect a staged import without making it the default for new authoring.
Deletion has a few rules worth reading before cleanup. An active template cannot be deleted, and a referencing draft PolicySet version also blocks deletion. Active and archived policy versions carry their own stored settings, so those authored payloads do not depend on retaining the catalog at runtime.
Do not mistake this history for a content-addressed archive. Reimport the same version and its catalog entries are replaced. If audit needs the exact ADMX bytes, retain the original file and checksum outside this workflow. The version label alone cannot prove the file stayed unchanged.
Put the settings in a version
In the model, the name belongs to PolicySet; the settings belong to its PolicySetVersion records. A version contains JSON, its hash, an optional ADMX version and a status of Draft, Active or Archived.
Creating a version records the following fields, as this exact excerpt from PolicyService.cs shows:
SettingsJson = settingsJson, Hash = hash, AdmxVersion = !string.IsNullOrWhiteSpace(admxVersion) ? admxVersion : await GetCatalogTemplateVersionAsync(), Status = PolicyVersionStatus.Draft, CreatedBy = actor
The ADMX stamp tells you the authoring context. Without a caller-supplied value, the service tries the active template and then an available catalog version. Keep it for provenance; do not read it as proof of browser compatibility.
The SHA-256 settings hash covers the supplied JSON text. Formatting changes can therefore change the hash without changing the intended settings. It is also not a digital signature. Both details matter when comparing generated documents.
Drafts can change. AddSettingToDraftAsync takes the latest one, or creates one with an incremented version label, and updates the setting and hash. You get versioned snapshots and an explicit activation step. You do not get database-enforced immutability in every lifecycle state.
An accepted draft still needs review
Before saving a submitted document, the create-version endpoint calls ChromePolicyValidator. It checks JSON parsing and types for a built-in selection of policy names: booleans, integers, strings, string arrays and dictionary-shaped values.
An unknown name produces a warning and continues. The exact behavior is worth seeing:
if (!KnownPolicies.TryGetValue(key, out var expectedType)) { result.Warnings.Add($"Unknown policy '{key}' - will be applied but may not be recognized by Chrome"); continue; }
That leaves room for newly introduced browser settings, at the operator's responsibility. The validator does not fully evaluate the imported ADMX schema. An integer may pass while being an unsupported enum value; a dictionary string may be accepted while describing no useful configuration.
There is another wrinkle: the single-setting draft endpoint does not call that validator. I'd review the complete JSON before activation, including settings added through catalog shortcuts. The checks vary with the authoring path.
Activation does not move an assignment
When PolicyService promotes a version, it archives the other active versions in that PolicySet, activates the selected one and records an audit event. The state change is explicit:
foreach (var active in currentActive) { active.Status = PolicyVersionStatus.Archived; } version.Status = PolicyVersionStatus.Active; await _db.SaveChangesAsync();
Now look at the assignment. It stores PolicySetVersionId, a reference to one version rather than just the set's name. The resolver requires that referenced version to be active. This promotion method does not rewrite those references.
Suppose a pilot assignment points to A. Promote B and A becomes archived, but the assignment still points to A. It stops contributing settings unless you update it or another eligible assignment applies. This is the part I'd put prominently in the rollout checklist. Knowing that B is active does not yet tell you what the pilot device will receive.
Rollback needs the same care. It reactivates the chosen older version and archives the current one, without universally restoring old assignment references or registry state. Check references to the rollback target, inspect the effective output, then arrange the client run or wait for it.
Optional push remediation can notify enabled, push-enabled assignments that reference the selected version. It can speed the process up. It cannot fix the wrong reference or establish that all endpoints have converged.
Mandatory for whom, and written where?
The group, version reference, priority, scope, target and enabled state are recorded in PolicyAssignment. Keep its two choices distinct: Mandatory versus Recommended selects the policy level; Machine versus User selects the registry destination and the identity used to resolve groups.
| Target | Mandatory destination | Recommended destination |
|---|---|---|
| Machine | HKLM\SOFTWARE\Policies\Google\Chrome |
Same path with \Recommended |
| User | HKCU\SOFTWARE\Policies\Google\Chrome |
Same path with \Recommended |
The API and client must carry both choices all the way through. A machine-only run does not automatically apply a user-group assignment. A recommended value is not a mandatory restriction, however strongly an administrator would like the recommendation to be followed.
Make the first change small
For a first change, use a small, clearly named PolicySet and a limited test group. Record the catalog version and tested Chrome builds. Read the draft JSON itself, with current browser documentation alongside it. Passing the type validator is a useful start, not the whole review.
Before activation, list every assignment referencing the current version and decide where each should go. Only one version per PolicySet stays active in this implementation. You cannot keep the old active version as a parallel long-lived ring in that same set. If independent rings need different active settings concurrently, consider separate PolicySets.
Activate, update the intended references, inspect a representative device's effective output, then check both its registry and Chrome policy page. Keep the old settings and assignment map for rollback. Be especially careful with forced deletion: the service can remove referencing assignments when force is requested. That is not routine tidying.
At this point the application can explain the intended change. The endpoint still has to demonstrate it. Part 2 follows the settings from group membership to the registry, including the ordering, conversion and drift checks that decide whether a useful-looking version record becomes a working browser policy.
Source and attribution
Inspect the ChromePolicyManager repository at the reviewed revision before adapting the design. The prose and artwork are original; the short code excerpts above are reproduced from the linked files under the repository's MIT license.
MIT License
Copyright (c) 2026 Roberto Gramellini
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.