Using Powershell to get installed Edge and Chrome extensions

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

#google-chrome, #microsoft-edge, #powershell