Overview
Provide a full step by step guide on how to audit members of the local Administrators group (or any other local groups at the same time) via ConfigMgr. This implementation will use a Configuration Item, deployed via a Configuration Baseline to a Collection. The heavy lifting is done with a PowerShell script.
Many thanks to the internet for providing the solution. My implementation is a merge of two ideas, and making it scalable.
To view compatibility, please go to the end of the document.
Script Credits
- https://mickitblog.blogspot.com/2016/11/sccm-local-administrators-reporting.html
- https://sqlbenjamin.wordpress.com/2018/01/27/collecting-members-of-the-local-administrators-group/
- https://blog.ctglobalservices.com/configuration-manager-sccm/kea/using-the-archive_reports-sms-file-to-monitor-inventory-data/
Guide
Prepare a computer with the WMI classes.
1. Get a copy of the
script from here.
https://gist.github.com/rileyz/40ec2e8ca9c15c85ce2de70bd1f5fdea
2. Get a copy of WMI Explorer from here, you'll need this later to
peer into WMI.
https://github.com/vinaypamnani/wmie2
On a target device with the CCM Agent installed, run the Inventory-LocalSecurityGroup.ps1 script with elevated privileges. It should run with no errors, and output data to the console via the verbose stream if enabled.
You want to make sure the the Inventory-LocalSecurityGroup.ps1 is gather the data you require. For example if you need to audit the local 'Users' group, or output the data to file, then enable and test the functions now. Warning: if you enable verbose in the script, don't forget to disable it when your done.
Save a copy of the your modified Inventory-LocalSecurityGroup.ps1, you will need this later, to add to the Configuration Item.
Check that the information has been added to WMI by using WMI
Explorer. If you see data in the Instances panel ( in the red box), then
you are good to go.
SCCM Configuration Item & Configuration Baseline Implementation
1. Extend the CCM
Agent Hardware Inventory by opening the SCCM Console and navigating to
"Administration > Overview > Client Settings". I would
recommend you extend the Hardware Inventory on the "Default Client
Settings" but leave it unchecked, and then enabling them on another
Client Setting to enable testing.
Extend Hardware
Inventory on "Default Client Settings" with the details
below.
Default Settings
Click Hardware Inventory and then Set Classes.
Hardware Inventory Classes
Click Add.
Add Hardware Inventory Class
Connect to the target device where we
ran the Inventory-LocalSecurityGroup.ps1 script. Once connected, find and check
LocalSecurityGroup Class Name. Click
Edit and double check the Class qualifiers.
Class Qualifiers
Ensure the Property PrimaryKey should be checked, like the image below.
Normally the Domain, LocalSecurity, PrimaryKey and SID will be checked by also
default.
Once
checked, click OK to the dialogs to get back to the SCCM console and then
enable the inventory class on the desired Custom Device Setting for testing.
If
this is not the case then you will need to export the MOF file, edit the keys
so at least the PrimaryKey is set as the Key and then import the updated
MOF. This is to ensure there is a unique
identifier in the database for the line item, otherwise you will have missing
data in SCCM.
Mick's
solution worked in SCCM 2016, but something has change in the newer versions of
SCCM. As noted on his blogs comments section, the reporting is not correct.
If
editing the MOF, the edited MOF section should look like the example below once
modified.
Warning: you can break inventory if you do this wrong, so keep a
backup of the MOF.
[ SMS_Report (TRUE), SMS_Group_Name ("LocalSecurityGroupInventory"), SMS_Class_ID ("MICROSOFT|LOCALSECURITYGROUPINVENTORY|1.0"), Namespace ("root\\\\cimv2") ] class LocalSecurityGroupInventory : SMS_Class_Template { [ SMS_Report (TRUE), key ] String Domain; [ SMS_Report (TRUE), key ] String LocalSecurityGroup; [ SMS_Report (TRUE), key ] String PrimaryKey; [ SMS_Report (TRUE), key ] String SID; [ SMS_Report (TRUE) ] String User; };
2. Creating the Configuration Item in SCCM by opening the SCCM Console and navigating to "Assets and Compliance > Overview > Compliance Settings > Configuration Items".
Create a new "Configuration Item" with the details below.
General
Give the Configuration Item a name like "Inventory Local
Administrators into WMI", and select the Windows Desktops and
Servers (custom) radio button.
Supported Platforms
Ensure the "Select all" check box is checked, this will ensure future
Windows versions will be selected as well.
Settings
Click New to add our setting.
A new dialog will open. On the General tab, give the setting a name - in our
case "Compliance Setting". Change the Setting type to Script, and the
Data type to Integer. In the Discovery script section, click Edit Script and
add the PowerShell script Inventory-LocalSecurityGroup.ps1.
In the Compliance Rules tab, add the rule which detects this setting as compliant. In our case, we are just looking for a Boolean value return of true. Click New and name the rule "WMI Class Detected", set the rule type as Value, and the value returned by the specified script equals "0". Once that is complete, click OK to confirm the Setting and Rule we just created. Then OK again to get back to the main wizard.
Settings
We should be back at the main wizard. Click Next.
Compliance Rules
You will see the Compliance Rule we just created before. Click Next.
Summary
Click Next.
Completion
Configuration Item has now been created. Click Close.
3. Creating the Configuration Baseline in SCCM by opening the SCCM Console and navigating to "Assets and Compliance > Overview > Compliance Settings > Configuration Baselines".
Create a new
"Configuration Baselines" with the details below.
Create Configuration Baseline
Give
the Configuration baseline a name, in our case "Local Administrators
Group Membership Audit". Now to add the Configuration Item Click Add
> Configuration Item.
A dialogue should open like the below, add our Configuration Item we create
before from the available list of items. Click OK after the item is added, this
will close the dialog below and take you
back the previous dialog above, Click OK again to close the wizard to bring you
back to the SCCM console.
SCCM Configuration Baselines Deployment
1. Deploy the Configuration Baseline in SCCM by opening the SCCM Console and navigating to "Assets and Compliance > Overview > Compliance Settings > Configuration Baselines".
Deploy the
Configuration Baseline with the details below.
In the SCCM console, find the Configuration Baseline you created
before, right click the item and select Deploy.
Select the Configuration Baseline from the left options list, and Add the item to the right list. Once that is added, select the Collection to target. The schedule can also be adjusted to how often you would like the evaluation to be run. Click OK to deploy the Configuration Baseline - this is no confirmation dialog, it will deployed at this point.
2. Monitoring once it has been deployed. You can monitor the deployment via "Monitoring > Overview > Deployments" and searching for your Configuration Baseline. Your compliant and non-compliant devices can be found here, or you can create a report.
Just to note, non-compliant devices are Local Security Groups which have orphaned SIDs. The orphaned SIDs cause the Get-LocalGroupMember cmdlet to error, a ADSI workaround was implemented to gather the members, the drawback was the SID could not be gathered correctly from cross domain members of the Local Security Group where Foreign Security Principals with the same username exists in the current domain due to the SIDHistory. Ie it would return the Foreign Security Principal SID from the current domain, rather than original cross forest domain. This was our edge case situation, for most people this should not be an issue as you will normally be in a single domain.
System Center Configuration Manager - Resource Explorer
Check Configuration Baselines is working and reporting. Go to your SCCM Collection and view the computer objects in the collection. Right click and start Resource Explorer. If everything is working correctly, you should see the inventory data like below, for your computer object. Having this data in the inventory means you can also report on this data.
SCCM Reporting with Reporting Services
Work in progress, coming soon!
But for now you can use Mick's
query solution. In the SCCM Console and navigate to "Monitoring >
Overview > Queries". Then Create Query, Edit Query Statement,
then Show Query Language. Now paste in the WQL statement below, and OK to
the dialogues to get back to the SCCM console. All done, view
your report!
select SMS_R_System.Name, SMS_G_System_LOCALSECURITYGROUPINVENTORY.Domain, SMS_G_System_LOCALSECURITYGROUPINVENTORY.User, SMS_G_System_LOCALSECURITYGROUPINVENTORY.SID from SMS_R_System inner join SMS_G_System_LOCALSECURITYGROUPINVENTORY on SMS_G_System_LOCALSECURITYGROUPINVENTORY.ResourceID = SMS_R_System.ResourceId order by SMS_R_System.Name, SMS_G_System_LOCALSECURITYGROUPINVENTORY.Domain, SMS_G_System_LOCALSECURITYGROUPINVENTORY.User
Compatibility
SCCM
- Tested on SCCM 1906 (25/03/2020).
Windows
- Windows 10 1903
- Windows Server 2016
- Windows Server 2012 R2
- Windows Server 2008 R2
Edit Log
25/03/2020: Initial release.
25/03/2020: Article re-formatting, copy/paste from OneNote was not ideal
09/04/2020: Updated the query, so it pulls the SID information too.
select
vSMS_R_System.Name0, v_GS_LOCALSECURITYGROUPINVENTORY.Domain0,
v_GS_LOCALSECURITYGROUPINVENTORY.User0,
v_GS_LOCALSECURITYGROUPINVENTORY.SID0
from
vSMS_R_System inner
join
v_GS_LOCALSECURITYGROUPINVENTORY
on
v_GS_LOCALSECURITYGROUPINVENTORY.ResourceID = vSMS_R_System.ItemKey
order by
vSMS_R_System.Name0, v_GS_LOCALSECURITYGROUPINVENTORY.Domain0,
v_GS_LOCALSECURITYGROUPINVENTORY.User0 - Mela 3 years ago
If you replace your code with this, it will also resolve the SID when falling back to the legacy method.
if ($Error.Count -ne 0) {
Write-Verbose 'Using legacy method for backwards compatibility.'
$Group = [ADSI]"WinNT://localhost/$LocalSecuirtyGroup"
$Members = @($group.psbase.Invoke("Members"))
$members | ForEach-Object {
$name = $_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)
$path = $_.GetType().InvokeMember("ADsPath", 'GetProperty', $null, $_, $null)
#substract the proper domain name or set it as BUILTIN
$Domain = $path -replace "WinNT://","" -replace "/$name",""
#find the SID of the user or group
$user = New-Object System.Security.Principal.NTAccount($name)
$sid = $user.Translate([System.Security.Principal.SecurityIdentifier])
$LocalInventory += $i++ | select @{Label='PrimaryKey'; Expression={$i}},
@{Label='SID'; Expression={$sid.Value}},
@{Label='LocalSecurityGroup'; Expression={$LocalSecuirtyGroup}},
@{Label='Domain'; Expression={Switch -Exact ($domain) {
"$env:COMPUTERNAME" {'BUILTIN'}
'WinNT:' {'Unknown'}
default {$domain}}}},
@{Label="User"; Expression={$name}}
}
} - Houdini 2 years ago