Forum Discussion
p0db0t
Sep 08, 2021Copper Contributor
PowerShell command to list all Internet explorer - EDGE add-ons?
Hello, people is there any PowerShell command to list all Internet explorer - EDGE add-ons?
3 Replies
- Did this work for you?
For Microsoft Edge Chromium you can use this, but not all extensions register the same info but it's a start perhaps 😉
foreach ($extension in Get-ChildItem -Path "$env:LOCALAPPDATA\Microsoft\Edge\User Data\Default\Extensions" -Filter manifest.json -Recurse) { Get-Content -Path $extension.Fullname | ConvertFrom-Json | Select-Object Author, Name, Description, Browser_Action }
Outputs something like this:
p0db0t Searched a bit and found this one, it lists it so much better 🙂 It does however also show installed software and you have to trim it a bit to adjust it to your needs. (It's made to report to SCCM)
# The script queries the selected profile paths to find installed browser extensions and write them into a custom WMI class to be picked up by ConfigMgr Hardware Inventory Start-Transcript -Path "$env:SystemRoot\Temp\BrowserInventory.log" ################################################################################ # ***** Configuration Options ***** ################################################################################ $ErrorActionPreference = "SilentlyContinue" # Override the default to hide errors. Comment this line to show standard error messgaes. $boolInventoryChrome = $true $boolInventoryMozilla = $true $boolInventoryEdge = $true $strChromeProfilePathAppend = "AppData\Local\Google\Chrome\User Data\Default\Extensions" $strMozillaProfilePathAppend = "AppData\Roaming\Mozilla\FireFox\Profiles" $strEdgeProfilePath = "$env:ProgramFiles\WindowsApps" $CustomWMIClassName = "cm_BrowserExtensionsV1" $boolExcludeCommonExtensions = $true #optional, if you want to exclude the following common browser extensions (these are typically preinstalled or mass installed) $strCommonGoogleExtensions = 'Google Docs','Google Sheets','Google Slides','Google Drive','YouTube','Gmail','Google Docs Offline','Chrome Web Store Payments','Chrome Media Router' $strCommonMozillaExtensions = '' $strCommonEdgeExtensions = '' $strCommon3rdPartyExtensions = 'Adobe Acrobat' $boolInventoryPreinstalledMicrosoftApps = $false # there are hundreds of preinstalled Apps from Microsoft. If you want to inventory them, set this to $true, otherwise they will be skipped #Combine the excluded extension lists for use later $strCommonExtensions = $strCommonGoogleExtensions + $strCommonMozillaExtensions + $strCommonEdgeExtensions + $strCommon3rdPartyExtensions ################################################################################ # ***** THE MAIN SCRIPT STARTS HERE - NO MODIFICATION SHOULD BE NECESSARY ***** ################################################################################ # ***** Functions ***** # Function to create the custom WMI class # Note that, once the class has been created on a device, it must be manually deleted if you want to add any additional columns of data Function Prepare-Wmi-Class() { Get-WMIObject $CustomWMIClassName -ErrorAction SilentlyContinue -ErrorVariable strWMIClassError | Out-Null # If the GET failed, the class doesn't exist, so create it. If not, it exists so clean it out. If ($strWMIClassError) { Write-Host "WMI Class $CustomWMIClassName does not exist. Try to create it.`n" -ForegroundColor Green Try { $newClass = New-Object System.Management.ManagementClass("root\cimv2", [String]::Empty, $null); $newClass["__CLASS"] = "$CustomWMIClassName"; $newClass.Qualifiers.Add("Static", $true) $newClass.Properties.add("Counter", [System.Management.CimType]::UInt32, $false) $newClass.Properties.add("ProfilePath", [System.Management.CimType]::String, $false) $newClass.Properties.add("FolderDate", [System.Management.CimType]::DateTime, $false) $newClass.Properties.add("FolderName", [System.Management.CimType]::String, $false) $newClass.Properties.add("Browser", [System.Management.CimType]::String, $false) $newClass.Properties.add("Name", [System.Management.CimType]::String, $false) $newClass.Properties.add("Version", [System.Management.CimType]::String, $false) $newClass.Properties.add("ScriptLastRan", [System.Management.CimType]::DateTime, $false) $newClass.Properties["Counter"].Qualifiers.Add("Key", $true) $newClass.Put() } Catch { Write-Host "Could not create WMI class" -ForegroundColor Red Exit 1 } } Else { Write-Host "WMI Class $CustomWMIClassName exists. Clear it out and proceed with logging.`n" -ForegroundColor Green # Remove all existing instances of the WMI class Get-WmiObject $CustomWMIClassName | Remove-WmiObject } } # Extract the Mozilla files Function ExtractMozillaExtensionPackage ($strMozillaExtensionPackagePath, $strBrowserDataToFind) { # If the temp extraction directory already exists, remove it $strTempFolderRoot = "$strMozillaExtensionPackagePath\SLPS_TEMP" If (Test-Path $strTempFolderRoot) {Remove-Item $strTempFolderRoot -Recurse -Force} # Get the list of XPI files $arrExtensionPackages = (Get-ChildItem -Path $strMozillaExtensionPackagePath -Filter "*.xpi" -Recurse).FullName # Bind to each XPI file and unzip it ForEach ($objExtensionPackage in $arrExtensionPackages) { $objArchiveFile = Get-ChildItem -Path $objExtensionPackage $objArchiveName = $objArchiveFile.Name # Open the XPI file $objArchive = [System.IO.Compression.ZipFile]::OpenRead($objArchiveFile) # Try to unzip it Try { # Define the target of the extracted files $strTempFolder = "$strTempFolderRoot\$objArchiveName".Trim(".xpi") + "\Extracted" # If the temp folder doesn't exist, create it If (!(Test-Path $strTempFolder)) {New-Item -ItemType "Directory" $strTempFolder | Out-Null} # Extract the files [System.IO.Compression.ZipFileExtensions]::ExtractToDirectory($objArchive, $strTempFolder) } Catch { Write-Host "Error extracting $objArchiveName $_" -ForegroundColor Red } } # Read the manifest files GetManifestFiles $strTempFolderRoot $strBrowserDataToFind "manifest.json" # Remove the temp directory when complete - this isn't really necessary, as it gets removed the next time the script runs. It's also helpful for troubleshooting to leave it. # Remove-Item $strTempFolderRoot -Recurse -Force } # Inventory the browser extensions Function InventoryExtensions ($strUserProfilePath, $strBrowserDataToFind) { # Browser specific searches # For Chrome, just search the given path If ($strBrowserDataToFind -eq "CHROME") { # Append the Chrome path $strManifestSearchPath = $strUserProfilePath + "\" + $strChromeProfilePathAppend # Read the manifest files GetManifestFiles $strManifestSearchPath $strBrowserDataToFind "manifest.json" } # If it's Mozilla, we have to extract the packages into a temp folder first If ($strBrowserDataToFind -eq "MOZILLA") { If (Test-Path "$strUserProfilePath\$strMozillaProfilePathAppend") { # Set the path to the profiles. $arrMozillaProfiles = Get-ChildItem "$strUserProfilePath\$strMozillaProfilePathAppend" | Where-Object { $_.PSIsContainer} # For each profile folder, we need to extract the XPI files (zipped extension pacakges) into a temp folder, then run the Manifest check ForEach ($objMozillaProfile in $arrMozillaProfiles) { # Run the Extraction routine against the Extension profile folder ExtractMozillaExtensionPackage "$($objMozillaProfile.FullName)\Extensions" $strBrowserDataToFind } } } # For Edge, we'll use PowerShell cmdlets to get the data If ($strBrowserDataToFind -eq "EDGE") { # Set the Edge path $strManifestSearchPath = $strEdgeProfilePath # Get the username from the profile path $strAppxUser = $strUserProfilePath.Replace("$strProfileRoot\","") # Get a list of Apps for this user $objUserAppxPackages = Get-AppxPackage -User $strAppxUser # For each app, run the GetManifest function ForEach ($objUserAppxPackage in $objUserAppxPackages) { # If flag is not set to $true and the App Publisher contains "Microsoft", don't inventory it. Otherwise, proceed with the inventory. If ($boolInventoryPreinstalledMicrosoftApps -eq $true -or ($boolExcludePreinstalledMicrosoftEdgeExtensions -ne $true -and $objUserAppxPackage.Publisher -notlike "*Microsoft*")) { # Clear the variables $global:dtFolderDateToRecord = Get-Date $global:strExtensionFolderNameToRecord = "" # Set the FolderName to the installation folder $global:strExtensionFolderNameToRecord = ($objUserAppxPackage.InstallLocation).Replace("$strEdgeProfilePath\","") # Try to get the date from the installation folder Try { $global:dtFolderDateToRecord = Get-ChildItem $objUserAppxPackage.InstallLocation | Select-Object -Last 1 | ForEach-Object { ($_.lastwritetime.tostring("yyyyMMddhhmmss"))+ '.000000-000' } } Catch { Write-Host "Could not record the date for $($objUserAppxPackage.InstallLocation): $_" $global:dtFolderDateToRecord = "19000101000000" + '.000000-000' } # Read the manifest files ReadAppxManifestXML $objUserAppxPackage.PackageFullName $strAppxUser $strBrowserDataToFind } #Else {Write-Host "$($objUserAppxPackage.PackageFullName) - Skipped"} } } } # Get a list of all manifest files in the given path Function GetManifestFiles ($strManifestSearchPath, $strBrowserDataToFind, $strDefaultManifestFileName) { # Clear the variables $global:dtFolderDateToRecord = "" $global:strExtensionFolderNameToRecord = "" # Search the path If (Test-Path $strManifestSearchPath) { Try { # Get an array of Extension folders from the Manifest File Search Path - this should be a list of folders, each containing the Extension files $arrExtensionFolders = Get-ChildItem $strManifestSearchPath | Where-Object { $_.PSIsContainer} # Go through the array of Extension folders ForEach ($objExtensionFolder in $arrExtensionFolders) { # If this is Chrome, there will be a list of Version folders under the Extension folder so we need to iterate those. For other browsers, we'll fake it. $arrExtensionVersionFolders = Get-ChildItem $objExtensionFolder.FullName | Where-Object { $_.PSIsContainer} # Go through the version folders in the extension folder ForEach ($objExtensionVersionFolder in $arrExtensionVersionFolders) { # Record the Extension Folder Name $global:strExtensionFolderNameToRecord = $objExtensionFolder.Name # Record the Extension Folder Date $global:dtFolderDateToRecord = Get-ChildItem $objExtensionVersionFolder.FullName | Select-Object -Last 1 | ForEach-Object { ($_.lastwritetime.tostring("yyyyMMddhhmmss"))+ '.000000-000' } # Inside each version folder, get the manifest.json file $strManifestFilePath = (Get-ChildItem -Path $objExtensionVersionFolder.FullName -filter $strDefaultManifestFileName -Recurse -ErrorAction SilentlyContinue).FullName # If the manifest file exists, read it If ($strManifestFilePath) { # If the Manifest file is manifest.json, call that function to find the Extension Name and Version If ($strDefaultManifestFileName = "manifest.json") { ReadManifestJSONFile $strManifestFilePath } # Record the info to WMI RecordExtensionsToWMI $strExtensionNameToRecord $strUserProfilePath $dtFolderDateToRecord $strExtensionFolderNameToRecord $strBrowserDataToFind $strVersionToRecord } } } } Catch {Write-Host "Error reading manifest files $_" -ForegroundColor Red} } } # Read the required info from the manifest.json file Function ReadManifestJSONFile ($strManifestFilePath) { # Clear the variables $strVersionLabelFromManifest = "" $strNameLabelFromManifest = "" $strVersionValueFromManifest = "" $strNameValueFromManifest = "" $global:strVersionToRecord = "" $global:strExtensionNameToRecord = "" If (Test-Path $strManifestFilePath) { ForEach ($objManifestFilePath in $strManifestFilePath) { # Read the Manifest file into an object $objManifestJSONFile = Get-Content $strManifestFilePath | ConvertFrom-Json # Inside manifest.json file, read the Version info $strVersionValueFromManifest = $objManifestJSONFile.Version # Record the version info $global:strVersionToRecord = $strVersionValueFromManifest # Inside manifest.json file, read the Name info $strNameValueFromManifest = $objManifestJSONFile.Name # If Name starts with underscores (i.e. "__MSG_APP_NAME__") then look in the messages.json files for a language specific name. Otherwise, use the name from the routine above. If ($strNameValueFromManifest -like "__*") { # Get the name to search for in messages.json $strMessageNameToFind = $strNameValueFromManifest.Trim("_").Trim("MSG_") # If the variables already exist, remove them If ($strMessagesFile1) {Remove-Variable -Name strMessagesFile1} If ($strMessagesFile2) {Remove-Variable -Name strMessagesFile2} # Set the Messages folder variables to the 2 likely English language folders $objManifestFileFolder = (Get-ChildItem $strManifestFilePath).DirectoryName $strMessagesFile1 = "$objManifestFileFolder\_locales\en\messages.json" $strMessagesFile2 = "$objManifestFileFolder\_locales\en_US\messages.json" # Call the function to search the messages.json file If (Test-Path $strMessagesFile1) { ReadMessagesFile $strMessagesFile1 $strMessageNameToFind } # If the name wasn't found in the first file, search the second If ($strExtensionNameToRecord -eq "" -and (Test-Path $strMessagesFile2)) { # Call the function with the second file ReadMessagesFile $strMessagesFile2 $strMessageNameToFind } # If the name wasn't found in the second file, record it as Unknown If ($strExtensionNameToRecord -eq "") {$strExtensionNameToRecord = "Unknown"} } Else {$global:strExtensionNameToRecord = $strNameValueFromManifest} } } } # Read the required info from the AppxManifest.XML Function ReadAppxManifestXML ($strAppxManifestPackageName, $strAppxUser, $strBrowserDataToFind) { # Clear the variables $global:strExtensionNameToRecord = "" $global:strVersionToRecord = "" # Record the package name $global:strExtensionNameToRecord = (Get-AppxPackageManifest -Package $strAppxManifestPackageName -User $strAppxUser).Package.Properties.DisplayName #Write-Host "Name: $strExtensionNameToRecord" # Record the package version $global:strVersionToRecord = (Get-AppxPackageManifest -Package $strAppxManifestPackageName -User $strAppxUser).Package.Identity.Version #Write-Host "Version: $strVersionToRecord" # Record the info to WMI RecordExtensionsToWMI $strExtensionNameToRecord $strUserProfilePath $dtFolderDateToRecord $strExtensionFolderNameToRecord $strBrowserDataToFind $strVersionToRecord } # Read the required info from the messages.json file Function ReadMessagesFile ($strMessagesFilePath, $strMessageNameToFind) { # Clear the variable $global:strExtensionNameToRecord = "" # Search the given folder to find a messages.json file If (Test-Path $strMessagesFilePath) { # Clear the variables $strNameLabelFromMessages = "" $strNameValueFromMessages = "" $intMessagesLineNumber = "" $intMessagesNextLineNumber = "" # Search the messages.json file to find the specified text and get the line number $intMessagesLineNumber = Select-String """$strMessageNameToFind""" $strMessagesFilePath | ForEach-Object {$_.LineNumber} # Get the content of the next line as that one should have the name $intMessagesNextLineNumber = (Get-Content $strMessagesFilePath)[$intMessagesLineNumber] # Trim the Value of any spaces, commas, and double-quotes #Value 6 should be the name $strNameLabelFromMessages,$strNameValueFromMessages = $intMessagesNextLineNumber.Split(':').Trim().Trim([Char]0x002c).Trim([Char]0x0022) # Check to see if Label6 was actually the 'Message' that we were looking for. Sometimes it's the next (or the next) line. If ($strNameLabelFromMessages.Trim() -eq "message") {$global:strExtensionNameToRecord = $strNameValueFromMessages} Else { # If it wasn't right, go to the next line, get the content, and trim it. $intMessagesNextLineNumber = (Get-Content $strMessagesFilePath)[$intMessagesLineNumber+1] $strNameLabelFromMessages,$strNameValueFromMessages = $intMessagesNextLineNumber.Split(':').Trim().Trim([Char]0x002c).Trim([Char]0x0022) # Check it again If ($strNameLabelFromMessages.Trim() -eq "message") {$global:strExtensionNameToRecord = $strNameValueFromMessages} Else { # If it still isn't right, check one more time. Get the content and trim it. $intMessagesNextLineNumber = (Get-Content $strMessagesFilePath)[$intMessagesLineNumber+2] $strNameLabelFromMessages,$strNameValueFromMessages = $intMessagesNextLineNumber.Split(':').Trim().Trim([Char]0x002c).Trim([Char]0x0022) # If it found something that time, record it If ($strNameLabelFromMessages.Trim() -eq "message") {$global:strExtensionNameToRecord = $strNameValueFromMessages } } } # If none of those checks found the right name, record the name as Unknown If ($strExtensionNameToRecord -eq "") {$global:strExtensionNameToRecord = "Unknown"} } } # Write the specified info into WMI Function RecordExtensionsToWMI ($strNameWMI, $strProfilePathWMI, $dtFolderDateWMI, $strFolderNameWMI, $strBrowserWMI, $strVersionWMI) { # Output the Name and Version Write-Host -NoNewline "$strBrowserWMI / $strNameWMI / $strVersionWMI" #Write-Host -NoNewLine " / $strProfilePathWMI / $dtFolderDateWMI / $strFolderNameWMI / $intBrowserExtensionCount" # Additional logging data # Check to see if the Extension name is in the list of Extensions to Include If ($boolExcludeCommonExtensions -eq $true) { If ($strCommonExtensions -match $strExtensionNameToRecord) { # The extension name is on the list so skip it and output Write-Host " - Excluded" -ForegroundColor Red Return } } # Record into WMI (Set-WmiInstance -Path \\.\root\cimv2:$CustomWMIClassName -Arguments @{ Counter=$intBrowserExtensionCount; Name=$strNameWMI; ProfilePath=$strProfilePathWMI; FolderDate=$dtFolderDateWMI; FolderName=$strFolderNameWMI; Browser=$strBrowserWMI; Version=$strVersionWMI; ScriptLastRan=$dtScriptRunTime}) | Out-Null # Increment the counter $global:intBrowserExtensionCount++ Write-Host " - Recorded" -ForegroundColor Green } # Start a ConfigMgr hardware scan Function StartConfigMgrInventory ($boolRunFullInventory) { # Set the type of scan to ConfigMgr Hardware Inventory $strScanType = "{00000000-0000-0000-0000-000000000001}" # If specified, wipe out the current Hardware Inventory to initiate a full refresh. If ($boolRunFullInventory -eq $true) { Get-WmiObject -Namespace "root\ccm\invagt" -Class InventoryActionStatus | where {$_.InventoryActionID -eq "$strScanType"} | Remove-WmiObject } # Try to kick off the inventory Try { Write-Host "Starting ConfigMgr Hardware Inventory..." Invoke-WmiMethod -ComputerName $env:ComputerName -Namespace root\ccm -Class SMS_Client -Name TriggerSchedule -ArgumentList $strScanType -ErrorAction Stop | Out-Null } Catch { Write-Host "Hardware Inventory failed for $env:ComputerName`: $_" -ForegroundColor Red } } # ***** End of Functions ***** # ***** Process Script ***** # ***** Set internal script variables ***** $global:intBrowserExtensionCount = 1 $strProfileListKey = 'Registry::HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\*' $strProfilePathsList = Get-ItemProperty -Path $strProfileListKey | Select-Object -Property ProfileImagePath $strProfileRoot = (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList").ProfilesDirectory # Record the script run time in a WMI formatted variable $dtScriptRunTime = New-Object -ComObject WbemScripting.SWbemDateTime $dtScriptRunTime.SetVarDate($(Get-Date)) $dtScriptRunTime = $dtScriptRunTime.Value # Import .NET 4.5 compression utilities Add-Type -As System.IO.Compression.FileSystem | Out-Null # Echo the start info Write-Host "`n`nStarting Browser Inventory on $env:ComputerName at $(Get-Date -Format g)....`n" # Check/Create/Clear the custom WMI class Prepare-Wmi-Class # Go through the Windows user profiles and find each of the Chrome and Mozilla extensions $strProfilePathsList | Foreach-object { If ($boolInventoryChrome -eq $true) { # Check for Chrome Extensions InventoryExtensions $_.ProfileImagePath 'CHROME' } If ($boolInventoryMozilla -eq $true) { # Check for Mozilla Extensions InventoryExtensions $_.ProfileImagePath 'MOZILLA' } If ($boolInventoryEdge -eq $true) { # Check for Edge Extensions InventoryExtensions $_.ProfileImagePath 'EDGE' } } # Kick off the ConfigMgr Inventory if any of the Browser Inventories was set to run and at least one extension was logged If (($boolInventoryChrome -eq $true -or $boolInventoryMozilla -eq $true -or $boolInventoryEdge -eq $true) -and $intBrowserExtensionCount -gt 1) { Write-Host "`nBrowser Extension Inventory recorded $($intBrowserExtensionCount - 1) extensions. Requesting ConfigMgr Inventory update....`n" StartConfigMgrInventory $false } # Stop the Transcript Stop-Transcript