The managed-machine rule is Chromium's, not Edge's

Chrome blanks an off-store ExtensionInstallForcelist entry on an unmanaged
machine exactly as Edge does - the qualifying states are AD domain join, Entra
join, or Chrome Enterprise Core enrolment, and this box is none of them. The
guard now covers every browser and refuses up front, with the three ways
forward, instead of skipping Edge alone and letting Chrome fail the same way.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Brandon Li
2026-08-31 13:30:04 -05:00
co-authored by Claude Opus 5
parent e42e729189
commit ce6fa6c0e1
3 changed files with 71 additions and 33 deletions
+5 -4
View File
@@ -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
+31 -12
View File
@@ -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]<id>;<url>` 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
+35 -17
View File
@@ -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]<id>;<url>" 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 ', ')"