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 sudo scripts/install-extension-policy.sh
``` ```
No administrator rights on Windows, where it goes into the user's own policy **This only works on an enterprise-managed machine** — domain-joined,
hive; root on Linux, where Chrome only reads policy from `/etc`. **Not Edge** — Entra-joined, or enrolled in Chrome Enterprise Core. No Chromium browser will
an unmanaged Edge refuses to install anything that is not from the Microsoft force-install an off-store extension on an ordinary PC, whatever the registry
Store, so use Chrome or Brave, or run the unpacked copy there. See 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 [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 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 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 Both configure every Chromium-family browser they find, and narrowing that to
one is `-Browser Chrome`. one is `-Browser Chrome`.
**Edge is the exception, and it is not one we can fix.** Unmanaged Edge installs ### The machine has to be managed
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.
On a machine that has to be Edge, load `extension/` unpacked there instead. It This is the constraint the whole approach turns on, and it is worth stating
still picks up releases: an unpacked copy reloads itself from disk when the plainly. On Windows, Chromium force-installs an extension from outside its own
dashboard reports a newer version, which is what the updater has already web store **only** when the instance is joined to an Active Directory domain,
pulled. 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 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 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 The restriction that decides whether any of this works.
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.
Chrome and Brave have no such restriction. So on an unmanaged machine there On Windows, Chromium will only force-install an extension from outside its
is nothing to be gained by writing Edge's key, and a real cost: Edge starts own web store when the instance is joined to an Active Directory domain,
reporting "Managed by your organization" and shows a policy error forever. 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 { function Test-DeviceManaged {
try { 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.' 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 @' Write-Warning @'
Edge is skipped: this device is not managed (not domain-joined, Intune-enrolled This machine is not enterprise managed - not domain-joined, not Entra-joined,
or Entra-joined), and unmanaged Edge only installs extensions from the Microsoft not enrolled in Chrome Enterprise Core - and no Chromium browser will
Store. It would read the policy and blank the value. 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 Three ways forward:
in Edge - an unpacked copy still reloads itself when the dashboard reports a
newer version. -Force writes the key anyway. 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' }) exit 1
if (-not $targets) { exit 1 }
} }
Write-Host "Configuring: $($targets -join ', ')" Write-Host "Configuring: $($targets -join ', ')"