Skip to content

robgrame/ChromePolicyManager

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

28 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Chrome Policy Manager

Server-side Chrome policy delivery for Entra ID–only (Azure AD joined) devices β€” bypassing the Group Policy dependency that breaks ADMX-based Chrome settings on cloud-managed endpoints.

.NET 9 Azure Intune License: MIT

🎯 The Problem

Chrome policies deployed via Intune Settings Catalog (ADMX-backed) fail silently on Entra ID–only joined devices. This happens because:

  1. GP Notification dependency β€” Chrome's PolicyLoaderWin calls RegisterGPNotification() which requires a domain-joined machine
  2. Domain join gate β€” mdm_utils.cc checks IsEnrolledToDomain() before applying policies
  3. ADMX registry mirroring β€” Intune writes to HKLM:\SOFTWARE\Microsoft\PolicyManager\providers\... but the GP Client Service never mirrors them to HKLM:\SOFTWARE\Policies\Google\Chrome on cloud-only devices

This affects ALL Chrome policies equally on cloud-only joined devices β€” not just specific ones.

πŸ’‘ The Solution

Chrome Policy Manager implements a server-side policy resolution engine that delivers Chrome policies directly to device registries via Intune Proactive Remediation scripts, completely bypassing the broken GP pipeline.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        ARCHITECTURE                              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                  β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
β”‚  β”‚  Admin   │───▢│  REST API │◀───│  Intune Remediation      β”‚  β”‚
β”‚  β”‚  UI      β”‚    β”‚  (.NET 9) β”‚    β”‚  (PowerShell scripts)    β”‚  β”‚
β”‚  β”‚ (Blazor) β”‚    β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜          β”‚                                         β”‚
β”‚                   β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β”                                    β”‚
β”‚                   β”‚ SQL DB  β”‚  ← PolicySets, Versions,           β”‚
β”‚                   β”‚  (S2)   β”‚    Assignments, DeviceState         β”‚
β”‚                   β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜                                    β”‚
β”‚                        β”‚                                         β”‚
β”‚              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”                               β”‚
β”‚              β”‚         β”‚         β”‚                               β”‚
β”‚         β”Œβ”€β”€β”€β”€β”΄β”€β”€β”€β”€β” β”Œβ”€β”€β”΄β”€β”€β”€β” β”Œβ”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                   β”‚
β”‚         β”‚ MS Graphβ”‚ β”‚ Svc  β”‚ β”‚ Graph Change β”‚                   β”‚
β”‚         β”‚ (delta) β”‚ β”‚ Bus  β”‚ β”‚ Webhooks     β”‚                   β”‚
β”‚         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                   β”‚
β”‚                                                                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

✨ Key Features

Feature Description
ADMX Catalog Ingestion Parse Chrome ADMX/ADML templates β†’ browse 700+ policies with descriptions, types, categories
PolicySet Versioning Immutable versions (Draft β†’ Active β†’ Archived) with hash-based change detection
Group-Based Targeting Assign policies to Entra ID security groups with priority-based conflict resolution
Mandatory & Recommended Support both Chrome policy scopes per assignment
Effective Policy Resolution Server resolves device β†’ groups β†’ assignments β†’ merged settings (lower priority wins)
Device Observability Real-time compliance dashboard, offline detection, error tracking
Intune Delivery Proactive Remediation hourly check β†’ detect drift β†’ apply policies via registry
Audit Trail Full audit logging for all policy changes and device interactions

πŸ—οΈ Project Structure

ChromePolicyManager/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ Server/
β”‚   β”‚   β”œβ”€β”€ ChromePolicyManager.Api/        # REST API (.NET 9 Minimal API)
β”‚   β”‚   β”‚   β”œβ”€β”€ Data/                       # EF Core DbContext + models
β”‚   β”‚   β”‚   β”œβ”€β”€ Endpoints/                  # Policy, Assignment, Device, Catalog, Monitoring
β”‚   β”‚   β”‚   β”œβ”€β”€ Models/                     # PolicySet, Version, Assignment, CatalogEntry
β”‚   β”‚   β”‚   └── Services/                   # AdmxParser, EffectivePolicy, Graph, Reporting
β”‚   β”‚   └── ChromePolicyManager.Admin/      # Blazor Server Admin UI (MudBlazor)
β”‚   β”‚       └── Components/Pages/           # Dashboard, Catalog, Policies, Assignments, Devices
β”‚   └── Client/
β”‚       β”œβ”€β”€ Detect-ChromePolicy.ps1         # Intune detection script
β”‚       └── Remediate-ChromePolicy.ps1      # Intune remediation script
β”œβ”€β”€ infra/
β”‚   └── Deploy-Infrastructure.ps1           # One-click Azure deployment
└── tools/                                  # ADMX template downloads (gitignored)

πŸš€ Quick Start

Prerequisites

  • .NET 9 SDK
  • Azure subscription (with Intune license for remediation)
  • az CLI authenticated
  • gh CLI (optional, for repo operations)

1. Deploy Infrastructure

cd infra
.\Deploy-Infrastructure.ps1

This creates: Resource Group, SQL Server (Entra-only auth), App Service Plan (B1), Web Apps (API + Admin), Key Vault, Service Bus, App Configuration.

2. Import Chrome Policy Catalog

Download the Chrome ADMX templates and upload via the Admin UI or API:

# Via API (multipart upload)
curl -X POST https://your-api.azurewebsites.net/api/catalog/import \
  -F "admxZip=@policy_templates.zip" \
  -F "version=136.0"

3. Create Policy Sets

Use the Admin UI at https://your-admin.azurewebsites.net/catalog to:

  1. Browse the catalog β†’ filter by category/type β†’ view descriptions
  2. Select policies and configure values
  3. Create PolicySets (e.g., "Security Baseline", "User Experience")
  4. Add versions with specific settings
  5. Assign to Entra ID groups with priority

4. Deploy Intune Remediation

The deployment script automatically creates a Proactive Remediation in Intune that:

  • Runs hourly on targeted devices
  • Detects drift by comparing local policy hash vs server hash
  • Remediates by writing Chrome registry policies directly to HKLM:\SOFTWARE\Policies\Google\Chrome

πŸ”§ API Endpoints

Policy Catalog

Method Endpoint Description
GET /api/catalog Browse policy catalog (filter: ?category=&search=&dataType=&recommended=)
GET /api/catalog/categories List available categories
GET /api/catalog/stats Import statistics
POST /api/catalog/import Import ADMX zip (multipart/form-data)

Policy Management

Method Endpoint Description
GET /api/policies List all PolicySets with versions
POST /api/policies Create new PolicySet
POST /api/policies/{id}/versions Add version with settings JSON
POST /api/policies/versions/{id}/promote Promote Draft β†’ Active
POST /api/policies/{id}/rollback/{versionId} Rollback to previous version

Assignments

Method Endpoint Description
GET /api/assignments List all assignments
POST /api/assignments Create group assignment (priority + scope)
DELETE /api/assignments/{id} Remove assignment

Device Operations

Method Endpoint Description
GET /api/devices/{id}/effective-policy Resolve effective policy for device
POST /api/devices/{id}/report Device reports compliance status
GET /api/monitoring/dashboard Compliance dashboard data
GET /api/monitoring/offline Offline devices (>N hours)
GET /api/monitoring/errors Devices with errors
GET /health Health check

πŸ“Š How Policy Resolution Works

Client Device β†’ API: "What policies apply to me?" (GET /devices/{id}/effective-policy)
                      β”‚
                      β–Ό
              MS Graph: devices/{id}/memberOf β†’ [Group1, Group2, ...]
                      β”‚
                      β–Ό
              Match groups β†’ Active PolicyAssignments
                      β”‚
                      β–Ό
              Sort by Priority (ascending: lower = higher priority)
                      β”‚
                      β–Ό
              Merge settings (first-writer-wins per key, separated by scope)
                      β”‚
                      β–Ό
              Return: { mandatory: {...}, recommended: {...}, hash: "abc123" }

πŸ”¬ Root Cause Analysis

Why Intune Settings Catalog Fails for Chrome

  1. Intune ADMX ingestion writes to HKLM\SOFTWARE\Microsoft\PolicyManager\providers\{GUID}\...
  2. This relies on GP Client Service to mirror to HKLM\SOFTWARE\Policies\Google\Chrome
  3. On Entra ID–only devices, GP Client mirroring is broken (no RegisterGPNotification, no domain join)
  4. Chrome reads only from HKLM\SOFTWARE\Policies\Google\Chrome β€” policies never arrive

Why Direct Registry Write Works

  • Chrome's PolicyLoaderWin reads HKLM\SOFTWARE\Policies\Google\Chrome unconditionally
  • No domain-join check gates registry policy reading
  • Chrome polls registry every 15 minutes (kReloadInterval = base::Minutes(15))
  • Entra ID–only devices get FULLY_TRUSTED management authority β€” no policy filtering

Source Code Evidence (Chromium)

  • PolicyLoaderWin::InitOnBackgroundThread() β€” requires RegisterGPNotification() success
  • mdm_utils.cc::IsEnrolledToDomain() β€” GP registry path check fails on cloud-only devices
  • WinGPOListProvider β€” depends on Active Directory infrastructure not present on Entra-only devices

πŸ›‘οΈ Security

API Gateway (Azure API Management)

Device-facing endpoints are protected by Azure API Management acting as a security gateway. The backend API never receives unauthenticated device traffic.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     OAuth2 Token     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   Managed Identity   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Device     β”‚ ───────────────────▢ β”‚    APIM      β”‚ ────────────────────▢ β”‚  Backend API β”‚
β”‚ (PowerShell) β”‚                      β”‚   Gateway    β”‚                       β”‚  (.NET 9)    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                            β”‚
                                      β”Œβ”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”
                                      β”‚ Validates  β”‚
                                      β”‚ device JWT β”‚
                                      β”‚ Rate limitsβ”‚
                                      β”‚ Strips     β”‚
                                      β”‚ spoofable  β”‚
                                      β”‚ headers    β”‚
                                      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Security flow:

  1. Device acquires OAuth2 token (client credentials + device certificate) from Entra ID
  2. APIM validates JWT (issuer, audience, expiry, required claims)
  3. APIM rate-limits per device identity (30 calls/hour/device)
  4. APIM strips any client-supplied identity headers (anti-spoofing)
  5. APIM sets trusted X-Forwarded-Device-Id from validated JWT claims
  6. APIM authenticates to backend using its managed identity (no shared secrets)
  7. Backend middleware verifies the request's appid claim matches APIM's identity

Separation of concerns:

Layer Responsibility
APIM Device auth, rate limiting, DDoS protection, request logging
Backend Business logic, policy resolution, database, Graph API
Admin UI Direct Entra ID JWT auth (doesn't go through APIM)

Additional Security Controls

  • Entra ID authentication for Admin UI (interactive login)
  • Managed Identity for all Azure resource access (no stored credentials)
  • Key Vault for secrets (connection strings, Graph client secret)
  • Entra-only SQL auth (no SQL passwords β€” MCAPS compliant)
  • Audit logging for all policy changes and device interactions
  • CORS restricted to Admin UI origin only
  • Service Bus for async device report processing (202 Accepted pattern)
  • Backend restriction: device endpoints reject calls not originating from APIM

πŸ“ˆ Scaling to 100k+ Devices

The solution is designed to handle large-scale enterprise environments (100,000+ devices) with minimal infrastructure cost. Three key optimizations make this possible:

1. ETag / 304 Not Modified

The GET /devices/{id}/effective-policy endpoint returns an ETag header containing the policy hash. On subsequent requests, the client sends If-None-Match with its cached hash:

Client β†’ API: GET /effective-policy  (If-None-Match: "abc123")
API β†’ Client: 304 Not Modified       ← No body, minimal compute

Only when policy actually changes:
Client β†’ API: GET /effective-policy  (If-None-Match: "abc123")
API β†’ Client: 200 OK + full payload  (ETag: "def456")

Impact: At 100k devices/hour with ~90% steady state β†’ only ~10k full responses/hour carry a payload.

2. Graph Change Notifications (Webhooks)

Instead of calling Microsoft Graph for every device check-in (which would hit throttling limits at scale), the API subscribes to real-time webhook notifications for group membership changes:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     Webhook: "Group X changed"     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Microsoft   β”‚ ──────────────────────────────────▢ β”‚  CPM API    β”‚
β”‚ Graph       β”‚                                     β”‚  (marks     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                                     β”‚  group as   β”‚
                                                    β”‚  dirty)     β”‚
                                                    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
                                                           β”‚
Device check-in:                                           β–Ό
  - Device in Group X β†’ Graph call (real-time, fresh data)
  - Device in Group Y (unchanged) β†’ use cached membership

Implementation:

  • GroupChangeNotificationService (BackgroundService) maintains subscriptions for all groups used in policy assignments
  • Subscriptions auto-renew before the 4230-minute Graph limit (~3 days)
  • /api/webhooks/group-change receives notifications and marks affected groups
  • WebhookEndpoints.HasGroupChanged() allows the effective policy resolver to skip Graph calls for unchanged groups

Impact: Reduces Graph API calls from 100,000/hour to ~50-100/hour (only devices in groups that actually changed), while maintaining zero-latency reactivity β€” policy changes propagate within minutes, not hours like Intune.

3. Azure SQL S2 (50 DTU)

Upgraded from Basic (5 DTU) to Standard S2 to handle sustained write throughput:

  • 100k device reports/hour = ~28 writes/sec sustained
  • S2 provides 50 DTU β†’ comfortable headroom for reads + writes + indexes

Scaling Summary

Metric Without optimizations With optimizations
Graph API calls/hour 100,000 (throttled) 50-100
Full policy responses/hour 100,000 ~10,000
Network bandwidth/hour ~500 MB ~50 MB
SQL write pressure 100k full reports 100k lightweight + 10k full
Reactivity N/A (was polling) Real-time (webhook push)

Recommended SKUs for 100k+ Devices

Component SKU Monthly Cost (est.)
App Service S2 or P1v3 €70-140
Azure SQL S2 (50 DTU) €60-150
Service Bus Standard €10
Total ~€150-300/month

πŸ“¦ Technology Stack

Component Technology
API .NET 9, Minimal API, Entity Framework Core
Admin UI Blazor Server, MudBlazor 8
Database Azure SQL S2, 50 DTU (Entra-only auth)
Auth Microsoft Identity Web, MSAL, Device Certificates
Group Resolution Microsoft Graph SDK + Change Notifications
Messaging Azure Service Bus (async device reports)
Config Azure App Configuration (Standard)
Secrets Azure Key Vault
Hosting Azure App Service (B1 β†’ S2 at scale)
Client PowerShell 5.1 (Intune Proactive Remediation)
Policy Catalog Chrome ADMX/ADML parser (700+ policies)

🀝 Contributing

  1. Fork the repo
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes
  4. Push to the branch
  5. Open a Pull Request

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

🏷️ Keywords

chrome-policy, intune, entra-id, azure-ad-joined, admx, group-policy-workaround, chrome-enterprise, mdm, proactive-remediation, browser-management, endpoint-management, registry-policy, blazor, dotnet, azure


Built to solve a real-world enterprise pain point β€” Chrome policy delivery on modern cloud-only managed devices where ADMX-based Settings Catalog fails silently.

About

Chrome Policy Manager - Workaround for Chrome ADMX policies failing on Entra ID-only (Azure AD joined) devices. Server-side policy resolution with Intune Proactive Remediation delivery.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors