Skip to main content

CM Inventory per-user browser extensions

At my company, there was recently a need to verify a custom vendor browser extension, specifically for Edge.  I found several methods for gathering Chrome extensions (including a clumsy attempt by myself several years ago), but then stumbled across this method, which could be modified to run as a Configuration Item + Mof Edit.

Source for this routine:  https://github.com/vastlimits/uberAgent-Scripts/blob/main/Get-BrowserExtensionInfo/Get-BrowserExtensionInfo.ps1

It looks like that routine was last updated by Helge Klein; which I suspect is this Helge Klein (but I can't be sure): https://helgeklein.com/  Mr. Klein has done some other work which you may have heard of, like "DelProf2" (Delprof2 deletes inactive user profiles), or "SetACL" (SetACL manages permissions, auditing and ownership information.)  Check him out if you have a need for something like that.

For Browser Extensions, if you want to use this with CM, the steps are...
1) deploy the CI inside a Baseline
2) Import the .mof and enable inventory.

That's the simple and short explanation.  For the nitty-gritty details and background story.  Browser Extensions are recorded in the user context.

What this routine does is multi-layered, and solves some (but not all) of the various issues I've felt "could" be encountered with inventorying per-user information.

First, a script inside the CI, running as system (not the logged in users) creates (if it doesn't already exist), a custom WMI Namespace called "CustomCMClasses".  If you so choose, you can change that if you like.  I've seen other examples using "ITLocal" as the custom namespace.  But for purposes of this blog, we'll assume you won't be modifying that name.  Then, it uses the well-known SIDs for "Everyone" and "Authenticated Users", to open up that namespace to allow those types of logins (aka, everyone and authenticated users) to write entries to classes in that namespace, like, for example... the per-user browser extensions.

Second, a script inside the CI runs under user context.  It will first delete any records ALREADY in that class for that specific user, and then repopulate the class with anything found in the per-user browser extensions, for chrome, edge (chromium based), and firefox.  What's nice about that is that if this is a multi-user device, you will continue to get information for all of the users who log in.  

POTENTIAL drawback is that let's surmise that Bob Smith logged on in July, and entries were created for him in WMI then.  Since then, he has not used this box or has even left the company.  There might be stale entries for Bob being inventoried... potentially for the life of the device.  That means that you will want to create reports where you filter on the 'ScriptRunTime' within the last xx days, so you don't pull stale data into reports.

--> Here <-- is the .zip containing the Configuration Item .cab to be imported into your CM Console (rename it before importing).  If you successfully import the CAB file, you don't have to do anything with the .renameAsPS1 files in the Zip.  Those .RenameAsPS1 files would be *IF* the .cab import fails, you could create your own CI, and add each of those as a Rule in the CI; one where you leave it to run as system, and the other where you carefully check the box for 'run scripts by using the logged on user credentials'.  Also in the .zip is a BrowserExtension-ImportMe.mof file.  Presuming you didn't change the custom class from being called 'CustomCMClasses', you would rename that to just .MOF, and import that into your Console, Administration, Client Settings, Default Client Settings, Hardware Inventory.  

Once you have the CI and a baseline including the CI created, you deploy the Baseline potentially to a small collection of devices. On those few devices, interactively do policy refreshes, and run the Baseline (from the Control Panel applet).  Note that you MIGHT have to run the baseline twice--the first time to create the initial custom class and set permissions.  Once that is done, it'll skip over that next time.  Then run the baseline again.  Using your favorite WMI browser (wmiexplorer?) look at customcmclasses, and the class inside.  See if it contains what you expect it to contain.  If so, hooray!

If you are happy with the results, import the .mof (you'll get a view likely called v_gs_browserextensions0... usually). Enable inventory for that.  Deploy the baseline to the rest of your environment where you want to get browser extensions.  Note, I would NOT have the baseline run frequently.  Perhaps every 4 days?  or every 7 days?  This information isn't mission critical, imo; it's a nice-to-have; for those (hopefully few) times when manager-types want to know about browser extensions.

Sample report to get you started (once you have deployed the CI as a Baseline, tested it, and inventory is enabled). Note that the InstallTime returned by the script is in that "seconds since 1970", so it's a bit annoying to tease out.  And 'default' extensions for chrome/Edge just have an installtime of -11644473600000, which doesn't translate to a 'real time', so in the report I just Null that out.

select 
Case when be.ExtensionInstallTime0 = '-11644473600000' then
 NULL
 else 
   dateadd(ms, cast(be.ExtensionInstallTime0 as BIGINT)%(3600*24*1000), dateadd(day, cast(be.ExtensionInstallTime0 as BIGINT)/(3600*24*1000), '1970-01-01 00:00:00.0') )
end as 'InstallTime'
 ,
be.Browser0 as 'Browser',
be.ExtensionFromWebStore0 as 'ExtensionInstalledFromWebStore',
be.ExtensionID0 as 'ExtensionID',
be.ExtensionInstalledByDefault0 as 'ExtensionInstalledByDefault',
be.ExtensionName0 as 'ExtensionName',
be.ExtensionVersion0 as 'ExtensionVersion',
case when be.ExtensionState0 = 1 then 'Active' else 'Disabled' end as 'ExtensionState',
be.OSUser0 as 'User',
be.ProfileDir0 as 'ProfileDir',
be.ProfileGaiaName0 as 'ProfileGaiaName',
be.ProfileName0 as 'Browser ProfileName (if Browsers have multiple profiles)',
be.ProfileUserName0 as 'Browser Profile UserName',
be.ScriptLastRan0 as 'LastTime This information was updated locally'

from v_GS_BrowserExtensions0 be

===============

Example results (from my mini-lab, for example)

InstallTime Browser ExtensionInstalledFromWebStore ExtensionID ExtensionInstalledByDefault ExtensionName ExtensionVersion ExtensionState User ProfileDir Browser ProfileName (if Browsers have multiple profiles) LastTime This information was updated locally
NULL Edge FALSE jmjflgjpcpepeafmmgdpfkogkghcpiha TRUE Edge relevant text changes 1.1.3 Active smsadmin Default Profile 1 7/6/2023 9:56
NULL Chrome TRUE aapbdbdomjkkjkaonfhkkikfgjllcleb FALSE Google Translate 2.0.13 Active sherry Default Person 1 7/6/2023 8:19
NULL Chrome TRUE nmmhkkegccagdldgiimedpiccmgmieda TRUE Chrome Web Store Payments 1.0.0.6 Active sherry Default Person 1 7/6/2023 8:19
NULL Chrome TRUE ghbmnnjooekpmoecnnnilnnbdlolhkhi TRUE Google Docs Offline 1.63.3 Active sherry Default Person 1 7/6/2023 8:19
NULL Edge FALSE jmjflgjpcpepeafmmgdpfkogkghcpiha TRUE Edge relevant text changes 1.1.3 Active sherry Default Profile 1 7/6/2023 8:19
7/3/2023 13:41 Firefox TRUE customscrollbars@computerwhiz NULL Custom Scrollbars 4.2.2 Active sherry 9fpff8kr.default-release default-release 7/6/2023 8:19

SCCM, ConfigMgr

  • Created on .