diff --git a/README.md b/README.md index 86b46b9..d97e48f 100644 --- a/README.md +++ b/README.md @@ -144,10 +144,11 @@ powershell -NoProfile -ExecutionPolicy Bypass -File scripts\install-extension-po sudo scripts/install-extension-policy.sh ``` -No administrator rights on Windows, where it goes into the user's own policy -hive; root on Linux, where Chrome only reads policy from `/etc`. **Not Edge** — -an unmanaged Edge refuses to install anything that is not from the Microsoft -Store, so use Chrome or Brave, or run the unpacked copy there. See +**This only works on an enterprise-managed machine** — domain-joined, +Entra-joined, or enrolled in Chrome Enterprise Core. No Chromium browser will +force-install an off-store extension on an ordinary PC, whatever the registry +says; the scripts check and refuse rather than leave a dead policy behind. On an +unmanaged box, load `extension/` unpacked and let it reload itself. See [deploy/README.md](deploy/README.md). Either way the extension notices when the dashboard is running newer code than it is: a policy install asks Chrome to fetch the new CRX, an unpacked one reloads itself from disk. Clicking the reload diff --git a/deploy/README.md b/deploy/README.md index eb465b9..8da7f3b 100644 --- a/deploy/README.md +++ b/deploy/README.md @@ -96,19 +96,38 @@ sudo scripts/install-extension-policy.sh Both configure every Chromium-family browser they find, and narrowing that to one is `-Browser Chrome`. -**Edge is the exception, and it is not one we can fix.** Unmanaged Edge installs -extensions from the Microsoft Store and nowhere else. It reads the policy and -then blanks it — `edge://policy` shows `[BLOCKED];` with "This device -isn't managed by your organization" — so a self-hosted CRX cannot be installed -there at all unless the machine is domain-joined, Intune-enrolled or -Entra-joined. Chrome and Brave have no equivalent restriction. The script -detects this and skips Edge rather than leaving a permanent policy error and a -"Managed by your organization" badge behind; `-Force` overrides it. +### The machine has to be managed -On a machine that has to be Edge, load `extension/` unpacked there instead. It -still picks up releases: an unpacked copy reloads itself from disk when the -dashboard reports a newer version, which is what the updater has already -pulled. +This is the constraint the whole approach turns on, and it is worth stating +plainly. On Windows, Chromium force-installs an extension from outside its own +web store **only** when the instance is joined to an Active Directory domain, +joined to Entra ID, or enrolled in Chrome Enterprise Core. Edge says the same of +the Microsoft Store. On an ordinary machine the policy is read and then blanked: + +``` +Value ["[BLOCKED]lkjbblagjjbldlkeaaffhiopdlhniaan;https://ext.juicerroom.com/updates.xml"] +Error Error at ExtensionInstallForcelist[0]: Invalid extension ID. +Warning This computer is not detected as enterprise managed so policy can only + automatically install extensions hosted on the Chrome Webstore. +``` + +The "invalid extension ID" is just `[BLOCKED]` being parsed as the id. Nothing +about the CRX, the update manifest or the hive is wrong — the machine is what +Chrome is refusing. Both scripts check for this and write nothing rather than +leaving a permanent policy error and a "Managed by your organization" badge +behind; `-Force` overrides. + +So the update server is only half a solution on an unmanaged box. The other half +is one of: + +- **Load `extension/` unpacked.** No update channel, but it reloads itself from + disk when the dashboard reports a newer version, and the updater has already + pulled those files. Works today, any browser, nothing to enrol. +- **Enrol Chrome in Chrome Enterprise Core** (free, and it does not require a + domain-joined machine): one `CloudManagementEnrollmentToken` registry value + makes the browser managed, and everything here then works as designed. +- **Publish to a web store.** Store-hosted extensions update themselves with no + policy at all — at the price of a listing and a review on every release. The Windows script writes to the current user's policy hive and needs no elevation; `-Scope Machine` covers every account on the box and does need an diff --git a/scripts/install-extension-policy.ps1 b/scripts/install-extension-policy.ps1 index 9ff58f9..bcc145a 100644 --- a/scripts/install-extension-policy.ps1 +++ b/scripts/install-extension-policy.ps1 @@ -95,15 +95,20 @@ function Test-BrowserInstalled([string]$Name) { } <# - Edge refuses to install an extension it did not get from the Microsoft Store - unless the device is managed — domain-joined, Intune-enrolled or Entra-joined. - The policy is read and then blanked: edge://policy shows the value as - "[BLOCKED]..." with "This device isn't managed by your organization", and an - "Invalid extension ID" error from parsing that prefix. + The restriction that decides whether any of this works. - Chrome and Brave have no such restriction. So on an unmanaged machine there - is nothing to be gained by writing Edge's key, and a real cost: Edge starts - reporting "Managed by your organization" and shows a policy error forever. + On Windows, Chromium will only force-install an extension from outside its + own web store when the instance is joined to an Active Directory domain, + joined to Entra ID, or enrolled in Chrome Enterprise Core (what used to be + called Chrome Browser Cloud Management). Edge says the same thing about the + Microsoft Store. On anything else the policy is read and then blanked: + chrome://policy shows "[BLOCKED];" and an "Invalid extension ID" + error, because the [BLOCKED] prefix is what gets parsed as the id. + + This is not something the update server can work around — it is about the + machine, not the extension, the hive, or the browser. So rather than leave a + permanent policy error and a "Managed by your organization" badge behind for + nothing, this refuses up front and says what the ways round it are. #> function Test-DeviceManaged { try { @@ -136,18 +141,31 @@ if (-not $targets) { throw 'Found no Chrome, Edge or Brave installation. Name one with -Browser to configure it regardless of what this can see.' } -if (-not $Remove -and ($targets -contains 'Edge') -and -not $Force -and -not (Test-DeviceManaged)) { +if (-not $Remove -and -not $Force -and -not (Test-DeviceManaged)) { Write-Warning @' -Edge is skipped: this device is not managed (not domain-joined, Intune-enrolled -or Entra-joined), and unmanaged Edge only installs extensions from the Microsoft -Store. It would read the policy and blank the value. +This machine is not enterprise managed - not domain-joined, not Entra-joined, +not enrolled in Chrome Enterprise Core - and no Chromium browser will +force-install an extension from outside its own web store on such a machine. +The policy would be read and blanked. Nothing was written. - Use Chrome or Brave for the update-server install, or load extension\ unpacked - in Edge - an unpacked copy still reloads itself when the dashboard reports a - newer version. -Force writes the key anyway. +Three ways forward: + + 1. Load extension\ unpacked. It has no update channel, but it reloads itself + from disk whenever the dashboard reports a newer version, and the updater + has already pulled those files. Works today, on any browser. + + 2. Enrol Chrome in Chrome Enterprise Core (free). One registry value - + CloudManagementEnrollmentToken - makes the browser managed, after which + this script works exactly as intended and the update server needs no + changes. + + 3. Publish the extension to a web store. Store-hosted extensions update + themselves with no policy at all. + + -Force writes the policy anyway, if you know this machine is managed and + dsregcmd simply cannot see it. '@ - $targets = @($targets | Where-Object { $_ -ne 'Edge' }) - if (-not $targets) { exit 1 } + exit 1 } Write-Host "Configuring: $($targets -join ', ')"