Important Notice: On February 29th, this community was put into read-only mode. All existing posts will remain but customers are unable to add new posts or comment on existing. Please feel to join our Community Discord for any questions and discussions.

chrome extension inventory

Sample PowerShell code for getting the Chrome extensions and putting in WMI for collection with PDQ inventory. This worked in my testing on systems with suspicious web connections to determine what extensions were installed that might be causing the problem. Using the Preferences file seems more reliable than looking in the manifest.json files, reg keys or internet. As with any environment your mileage may vary.

May need to remove the Permissions field if inventorying with SCCM. The data may be too long for SCCM to process.

# PS script to get Chrome Ext and put in WMI
# Some code from Ivanti, rewritten to not browse extension folders or go to web for extension names. Added extra fields. 
# Original idea I saw was from SKissinger for SCCM 2012. Saw Preference file reference in SpiceWorks post for Chrome on unix.

# Now uses Chrome Preferences file.


# Need to run with admin permissions to manipulate WMI.


function Convert-UTCtoLocal {
param(
[parameter(Mandatory=$true)]
[String] $UTCTime
)

    $strCurrentTimeZone = (Get-WmiObject win32_timezone).StandardName
    $TZ = [System.TimeZoneInfo]::FindSystemTimeZoneById($strCurrentTimeZone)
    $LocalTime = [System.TimeZoneInfo]::ConvertTimeFromUtc($UTCTime, $TZ)
    return $LocalTime
}



#setup or clear WMI class
if(Get-WmiObject -List | where { $_.Name -eq "pdqChromeExtension"}){

    #Clear existing WMI entries to re-enumerate extensions
    #Get-WmiObject pdqChromeExtension | Remove-WmiObject
    Remove-CimInstance -Query 'select * from pdqChromeExtension'
}
else{
        $newClass = New-Object System.Management.ManagementClass ("root\cimv2", [String]::Empty, $null); 

        $newClass["__CLASS"] = "pdqChromeExtension"; 

        $newClass.Qualifiers.Add("Static", $true)

        $newClass.Properties.Add("Name", [System.Management.CimType]::String, $false)
        $newClass.Properties["Name"].Qualifiers.Add("Key", $true)

        $newClass.Properties.Add("Version", [System.Management.CimType]::String, $false)
        $newClass.Properties["Version"].Qualifiers.Add("Key", $true)

        $newClass.Properties.Add("EnableState", [System.Management.CimType]::String, $false)
        $newClass.Properties["EnableState"].Qualifiers.Add("Key", $true)

        $newClass.Properties.Add("Description", [System.Management.CimType]::String, $false)
        $newClass.Properties["Description"].Qualifiers.Add("Key", $true)

        $newClass.Properties.Add("Permissions", [System.Management.CimType]::String, $false)
        $newClass.Properties["Permissions"].Qualifiers.Add("Key", $true)

        $newClass.Properties.Add("ID", [System.Management.CimType]::String, $false)
        $newClass.Properties["ID"].Qualifiers.Add("Key", $true)

        $newClass.Properties.Add("DateInstall", [System.Management.CimType]::String, $false)
        $newClass.Properties["DateInstall"].Qualifiers.Add("Key", $true)

        $newClass.Properties.Add("DefaultInstall", [System.Management.CimType]::String, $false)
        $newClass.Properties["DefaultInstall"].Qualifiers.Add("Key", $true)

        $newClass.Properties.Add("OemInstall", [System.Management.CimType]::String, $false)
        $newClass.Properties["OemInstall"].Qualifiers.Add("Key", $true)



        $newClass.Properties.Add("User", [System.Management.CimType]::String, $false)
        $newClass.Properties["User"].Qualifiers.Add("Key", $true)
        
        $newClass.Properties.Add("ChromeVer", [System.Management.CimType]::String, $false)
        $newClass.Properties["ChromeVer"].Qualifiers.Add("Key", $true)
        
        $newClass.Properties.Add("LastScan", [System.Management.CimType]::String, $false)
        $newClass.Properties["LastScan"].Qualifiers.Add("Key", $true)
        
        $newClass.Put()
} # end if else


$parentdir = "C:\Users\"
$users = Get-ChildItem $parentdir
$ObjCollection = @()

Foreach($user in $users){

    # get profile folder
    $targetdir = ""
    $chromedir = $parentdir + $user.ToString() + "\AppData\Local\Google\Chrome\User Data"
    if (test-path "$chromedir\default") {$targetdir = "$chromedir\Default"}
    if (test-path "$chromedir\profile 1") {$targetdir = "$chromedir\Profile 1"}

    # read preferences file in Default folder
    if (test-path "$targetdir\Preferences") {
       $prefs = Get-Content "$targetdir\Preferences"  | ConvertFrom-Json  # Read Prefernces JSON file
    
       #get extension names  
       $extensions = $prefs.extensions.settings | gm -type NoteProperty| ForEach-Object {$_.name}
 
       Foreach($ext in $extensions){
 
            $obj = New-Object System.Object
            $pref = $($prefs.extensions.settings.$($ext))
            $Permissions = ""  # force permissions variable to string

            # view variable contents for testing
            # $pref 
            # $pref.manifest

            $name = $pref.manifest.name
            $state = $pref.state
            $version = $pref.manifest.version
            $description = $pref.manifest.description
            $DefaultInstall = $pref.was_installed_by_default
            $OemInstall = $pref.was_installed_by_oem
            $Ptemp = $pref.manifest.permissions
            foreach ($pt in $Ptemp) {$Permissions = $Permissions + $pt.tostring() + "._."} # convert array to string to store in WMI. Use ._. entry separator.
            $ChromeVer = $prefs.extensions.last_chrome_version

            # install time conversion
            $p = [double]$pref.install_time
            $p = ($p - 11644473600000000 ) / 1000000 #//divide by 1,000,000 because we are going to add seconds on to the base date
            $date = get-date -date "1970-01-01 00:00:00"
            $date = $date.AddSeconds($p)
            $localtimezn = Convert-UTCtoLocal($date)


            #get name and version from json and add to object
            $obj | Add-Member -MemberType NoteProperty -Name Name -Value $name
            $obj | Add-Member -MemberType NoteProperty -Name Version -Value $version
            $obj | Add-Member -MemberType NoteProperty -Name EnableState -Value $state
            $obj | Add-Member -MemberType NoteProperty -Name Description -Value $description
            $obj | Add-Member -MemberType NoteProperty -Name DefaultInstall -Value $DefaultInstall
            $obj | Add-Member -MemberType NoteProperty -Name OemInstall -Value $OemInstall
            $obj | Add-Member -MemberType NoteProperty -Name Permissions -Value $Permissions
            $obj | Add-Member -MemberType NoteProperty -Name ID -Value $ext.ToString()
            $obj | Add-Member -MemberType NoteProperty -Name DateInstall -Value $localtimezn.ToString()

            $obj | Add-Member -MemberType NoteProperty -Name User -Value $user
            $obj | Add-Member -MemberType NoteProperty -Name ChromeVer -Value $ChromeVer
            $obj | Add-Member -MemberType NoteProperty -Name LastScan -Value $(Get-Date)


            # ignore blank names 
            if($name -ne $null){            
                Set-WmiInstance -Class pdqChromeExtension -Puttype CreateOnly -Argument @{Name = $obj.Name; Version = $obj.Version; EnableState = $obj.EnableState; Description = $obj.Description; DefaultInstall = $obj.DefaultInstall; OemInstall = $obj.OemInstall; Permissions = $obj.Permissions; ID = $obj.ID; DateInstall = $obj.DateInstall; User = $obj.User; ChromeVer = $obj.ChromeVer; LastScan = $obj.LastScan} -ErrorAction SilentlyContinue
            

                # items for testing or debug. Not saved in WMI class
                #$obj

                $ObjCollection += $obj  # used for examining the data later for troubleshooting. 
            }
        
       } # end foreach extensions
    } # end if pref file found
} # end foreach users
1

Comments

9 comments
Date Votes
  • Wow, this is amazing. I have this setup as a package to run at regular intervals with the schedule set to run the WMI scan for this specific WMI after the fact. I also have a report setup so management can see the different extensions installed. They are currently evaluating how to setup the GPO for Chrome management and want to see what extensions to allow, block and force install. This has made that task so much easier.

    Thank you!

    0
  • Hi,

    i'm trying your script - but finally with no luck :(

    And i'm not a powershell specialist, but for me it seems that in my case $prefs is not containing all data.

    This is the extensions part of $prefs --> extensions : @{alerts=; chrome_url_overrides=; commands=; corrupted_disable_count=2; install_signature=; last_chrome_version=84.0.4147.45; toolbar=System.Object[]; ui=}

    And this is the part in the preferences --> "extensions":{"alerts":{"initialized":true},"chrome_url_overrides":{"newtab":[{"active":false,"entry":"chrome-extension://chnacmlpiecdhgkdgeoipkmdbekengck/ntp1.html"},{"active":true,"entry":"chrome-extension://ojahbhfflnbaiddhnkgeccgopmedjjen/index.html"}]},"commands":{"windows:Ctrl+D":{"command_name":"_execute_page_action","extension":"gmlllbghnfkpflemihljekbapjopfjik","global":false},"windows:Ctrl+Shift+S":{"command_name":"1-suspend-tab","extension":"klbibkeccnjlkjkiokjodocebajanakg","global":false},"windows:Ctrl+Shift+U":{"command_name":"2-unsuspend-tab","extension":"klbibkeccnjlkjkiokjodocebajanakg","global":false}},"corrupted_disable_count":2,"install_signature":{"expire_date":"2020-09-06","ids":["aapocclcgogkmnckokdopfmhonfmgoek","aohghmighlieiainnegkcijnfilokake","chnacmlpiecdhgkdgeoipkmdbekengck","cjpalhdlnbpafiamejdnhcphjbkeiagm","emgdmhbdljgjleocpgobbfniglfhlhfk","felcaaldnbdncclmgdcncolpebgiejap","fgkboeogiiklpklnjgdiaghaiehcknjo","ghbmnnjooekpmoecnnnilnnbdlolhkhi","ijmndaodmdjamfepoijpolhjddgfgmme","jlhmfgmfgeifomenelglieieghnjghma","kejbdjndbnbjgmefkgdddjlbokphdefk","klbibkeccnjlkjkiokjodocebajanakg","lmjegmlicamnimmfhcmpkclmigmmcbeh","oamembonjndgangicfphlckkdmagpjlg","ojahbhfflnbaiddhnkgeccgopmedjjen","pbfjbhoglggakhkngkbfehgghkaadeba","pkdaplneajgbalnnmaoflooakgijjkhc","pkedcjkdefgpdelpbcmbmeomcjbeemfm"],"invalid_ids":[],"salt":"h87mdBSfMBHa2BYxK5nZMeIYR03k6+kM8Z7I86vG1k0=",

     

    Any idea?

    0
  • I turned Jerry's script into a PowerShell Scanner. Would you mind seeing if it works for you? If it doesn't work, please include the contents of the Output Log.

    https://github.com/pdq/PowerShell-Scanners/tree/master/PowerShell%20Scanners/Google%20Chrome%20Extensions

    1
  • Could this be adapted for the new Edge browser?

    0
  • Lord, Jerry & Bouma, Colby - this script is great.  It worked beautifully when executing on a local machine or via PDQ Deploy.  Thank you!

    Is there a way to integrate the script into Inventory to make viewing network-wide results easier?  I created a new Scan Profile with the Powershell script and added the parameters "| ft Name,User".  When I ran the scan, I ended up with a lot of rows of mostly the same ClassID.

    0
  • When running the script, many of my own extensions did not list in the report. There is a folder within Chrome called "Extensions", should that also be included as a place to look within the script?

     

    0
  • Dan Carp

    "ft" emits an object that is incompatible with the PowerShell Scanner. I recommend using Select-Object if you want to reduce the number of columns returned.

    0
  • Michael Tully

    I have a new version of this scanner that I believe will fix your issue. It hasn't been approved by PDQ yet, but I'm hopeful that it might be within the next few months. Let me know if you want to try it out now, I can walk you through it.

    https://github.com/pdq/PowerShell-Scanners/pull/54

    Michael Edes

    The new scanner I mentioned above scans for Edge extensions too.

    0