I want to start off with acknowledging that I know, but don’t have the ability, to manage Google Chrome in an organizational setting through Google Cloud which produces nice reports of extensions installed — and this can even be done with M365 Defender. My dilemma is that for some networks I do not have the ability to do this, so I needed an alternative way. This is what I’m developing.
I found an article on Spiceworks that someone created a PowerShell script already while I was trying to find the original article I got the inspiration from. I learned a little about how Chrome extensions use manifest.json and that “name” key doesn’t always, as of Manifest V2, give the actual extension name.
Reading through Chrome.18n documentation on how the manifest is laid out and internationalization structure, I was able to figure out a way to programmatically look things up.
In manifest.json the following keys are required:
default_locale is required. It can be any value, really, but standards are en_US, en, ru, etc.
name is required. It can be the actual name of the extension, but in Manifest V3, it is the string to translate which is preceded by __MSG_
and ends with __
(two underscores). The value between could be anything, for example __MSG_name__
or __MSG_AppName__
.
So my thought is to look up the name key, check for __MSG_
and also the default_locale if the name key matches. This then lets me find messages.json which I can use the __MSG_name__
to find the actual extension name.
Disclaimer: This is my script so far; my PowerShell sucks. But it works for me. I do know I need to handle and log errors (unavailable computers, etc.) and not rely on
-ErrorAction SilentlyContinue
.
# Define an array to store the extensions
$extensions = @()
Clear-Host
Write-Host "Google Chrome Extensions`n`n"
# Get a list of AD computers
$computers = Get-ADComputer -Filter *
foreach ($computer in $computers) {
$computerPath = "\\$($computer.Name)\c$\users"
Write-Host "******************** $($computer.Name) **********************"
# Get a list of users excluding 'Public' and 'Default'
$users = Get-ChildItem -Path $computerPath -ErrorAction SilentlyContinue | Where-Object { $_.Name -notlike '*Public*' -and $_.Name -notlike '*Default*' }
foreach ($user in $users) {
Write-Host "Checking $($user.Name) : $($computer.Name)`n"
$extensionPath = Join-Path -Path $computerPath -ChildPath "$($user.Name)\appdata\local\google\chrome\user data\default\Extensions"
# Check if the Extensions folder exists
if (Test-Path -Path $extensionPath) {
$manifestFiles = Get-ChildItem -Path $extensionPath -Filter manifest.json -Recurse -Depth 4 -ErrorAction SilentlyContinue
foreach ($manifestFile in $manifestFiles) {
$extDir = $manifestFile.DirectoryName
$extID = $manifestFile.Directory.Parent.Name
$manifestData = Get-Content -Path $manifestFile.FullName | ConvertFrom-Json
if ($manifestData.name -like "*__MSG_*") {
$msgJSON = Join-Path -Path $extDir -ChildPath "_locales\$($manifestData.default_locale)\messages.json"
$extName = $manifestData.name -replace "__MSG_", "" -replace "__", ""
$extFullName = (Get-Content -Encoding UTF8 -Path $msgJSON | ConvertFrom-Json).$extName.message
$extensions += [PSCustomObject]@{
ComputerName = $computer.Name
UserName = $user.Name
ExtensionName = $extFullName
ExtensionID = $extID
}
Write-Host "$extFullName ($extID)"
} else {
$extensions += [PSCustomObject]@{
ComputerName = $computer.Name
UserName = $user.Name
ExtensionName = $manifestData.name
ExtensionID = $extID
}
Write-Host "$($manifestData.name) ($extID)"
}
}
Write-Host "`n"
}
}
}
# Export the extensions to a CSV file
$extensions | Export-Csv -Path "browser_extension_report.csv" -Encoding UTF8 -NoTypeInformation
Once it is done, a report should be created as a CSV. Here’s an example. It lists each extension installed for every user per machine scanned.
Managing Extensions in Google Chrome
What I’m working on now is a way to uninstall these after I review installed extensions and determine which ones I want to remove and block from re-installation. This is still a work in progress, here are some of my notes.
This is for removing an app, not an extension. See: Extension and App Types
"C:\Program Files\Google\Chrome\Application\chrome.exe" --profile-directory=Default --uninstall-app-id=EXTENSION_ID;
I’m also seeing some references to registry locations.
HKEY_USERS\Group Policy Objects\Machine\Software\Policies\Google\Chrome\ExtensionInstallForcelist
HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Google\Chrome\ExtensionInstallForcelist
Looks like I need to add the extension to the ExtensionInstallForceList and then remove it.
Add ForceInstall
New-ItemProperty -Path "HKLM\SOFTWARE\Policies\Google\Chrome\ExtensionInstallForcelist " -Name "1" -Value EXTENSION_ID
Remove ForceInstall
Note: Need to specify the same value that was added in the Add step. This does not actually remove the extension if it is installed.
Remove-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Google\Chrome\ExtensionInstallForcelist" -Name "EXTENSION_NAME"
Remove (uninstall) the Extension
Could it be as simple as deleting the actual extension folder? sigh
Remove-Item -Path "C:\users\username\appdata\local\google\chrome\user data\default\extension\<EXTENSION_ID>" -Recurse
Block Extension
Use *
for the EXTENSION_NAME and value of 1
to block all extension installations.
Note:
-Name
is an integer value being placed in the registry. To add another blocked extension, make sure to increment to the another value.
New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Google\Chrome\ExtensionInstallBlocklist" -Name "1" -Value EXTENSION_ID;
Allow Extension
New-ItemProperty -Path "HKLM:\SOFTWARE\Policies\Google\Chrome\ExtensionInstallAllowlist" -Name "1" -Value EXTENSION_ID;
I’ll end up doing this by Group Policy instead of per-computer in Active Directory environments. I’ll block all, and permit only approved extensions.
Group Policy (Google Policy Templates): Computer Configuration > Administrative Templates > Class Administrative Templates > Google > Chrome > Extensions