PowerShell Scripting with SCCM

Until now, I’ve done my SCCM patching with the NO REBOOT option set and servers were rebooted manually.  Because of this, we were getting further and further behind due to servers requiring multiple reboots each month.  In order to resolve this, we decided to go about using the Auto-Reboot option in SCCM and letting SCCM reboot however many times is necessary.  But in order to do this, we had to change the way we patch so that servers were rebooted in the right order.  Typically, our NOC reboots the servers in order with 4 different groups.  Anything not in a group is rebooted on a special schedule or by the application teams themselves.  So we decided to create 5 different SCCM groups for each site.  I created groups 1 through 4, and one for the manual reboots which we called EXCLUDED (meaning excluded from the auto-reboot.)

Now that we had a plan, we had to actually figure out how to do this. And as you know, if anything involves a large amount of work like this, I’m going to automate it!  Now, this is a new area for me.  As you can tell from my posts, most of my PowerShell experience is with PowerCLI CMDLETS, with a smattering of Windows PowerShell CMDLETS mixed in.

We normally create AD groups and link those AD groups to SCCM groups. I used PowerShell to create the groups in AD.  We store these groups in the OU that we create the computer accounts for each site.  I had to do some research to find the SCCM CMDLETS I needed, as well as the AD CMDLETS as well!  This is the script I wrote to create the AD Groups.  I was able to un-comment each ARRAY/SITE as needed to create the respective groups.

################################################
#
# Script to create AD Groups for SCCM
# Created by BLiebowitz on 8/17/18
#
################################################

# Build an array for each of the sites  Run multiple times and comment / uncomment for each.
# $array = "GBLSite1", "GBLSite2","GBLSite3" # Global Sites
# $array = "PRDSITE1", "PRDSITE2", "PRDSITE3" # PROD Sites
$array = "STGSITE1" # Staging Site

# Create a loop for each array to do the work
for($count=0;$count -lt $array.length; $count++)
{
# Sets the type (STG, PRD, or GBL). Again, comment/uncomment as necessary.
# $Type = "STG"
# $Type = "PRD"
$Type = "GBL"
$site = $array[$count]

# Creates the AD Groups
New-ADGroup -Name "SCCM_$($site)_$($type)_Group_1" -GroupScope Universal -GroupCategory Security -path "OU=Servers,OU=$($site),OU=Sites,DC=DOMAIN,DC=COM"
New-ADGroup -Name "SCCM_$($site)_$($type)_Group_2" -GroupScope Universal -GroupCategory Security -path "OU=Servers,OU=$($site),OU=Sites,DC=DOMAIN,DC=COM"
New-ADGroup -Name "SCCM_$($site)_$($type)_Group_3" -GroupScope Universal -GroupCategory Security -path "OU=Servers,OU=$($site),OU=Sites,DC=DOMAIN,DC=COM"
New-ADGroup -Name "SCCM_$($site)_$($type)_Group_4" -GroupScope Universal -GroupCategory Security -path "OU=Servers,OU=$($site),OU=Sites,DC=DOMAIN,DC=COM"
New-ADGroup -Name "SCCM_$($site)_$($type)_EXCLUSIONS" -GroupScope Universal -GroupCategory Security -path "OU=Servers,OU=$($site),OU=Sites,DC=DOMAIN,DC=COM"
}

I built the AD groups via script which pulled from a CSV file.  I used our server list spreadsheet and created a new sheet with multiple columns, one for each AD Group membership. I was then able to run THIS script to populate the groups I created above. I ran this script over and over for each column in the CSV, manually verifying the membership as I went.  I found that servers like Domain Controllers, didn’t populate via the script and had to be added manually.  🙂


##############################################################################
#
# This script will add the computer accounts from the SCCM Reboot Groups
# spreadsheet into the respective AD Groups
# Created by BLiebowitz on 8/17/18
#
##############################################################################

# Assign the spreadsheet to a variable
$csv = import-csv -path e:\ben\SCCM\sccm_PRD_reboot_groups.csv

# Create a loop to populate each group from the CSV
foreach ($svr in $csv) {
$server = $svr.Group1Site1
$DN = get-adcomputer $server | Select distinguishedname
Add-ADGroupMember -identity SCCM_Site1_PRD_Group_1 -members $DN
}

Now that the AD groups were created and populated, I had to turn my attention to SCCM.  The first step was updating the SCCM Discovery methods so it synced these new groups with SCCM.  To do this, open SCCM, go to ADMINISTRATION, and then expand HIERACHY CONFIGURATION, and then click DISCOVERY METHODS.  In the right pane, double click on Active Directory Group Discovery. Click ADD and then GROUPS, enter the name of the new group, then click BROWSE in the middle of the box and locate your new SCCM group.  I thought to script this but couldn’t find a simple script to do it so I decided to do this by hand.

Now that SCCM knows about the new groups, we can automate the creation of the groups!!  HOWEVER, first, we had to enable the SCCM PowerShell module.  To do this, you first need to have launched SCCM as an administrator.  Once you’ve done that, click the blue box with the down arrow in the top left corner of SCCM.

After that, I selected Windows PowerShell ISE, so I could run my whole script at once instead of one line at a time.  YMMV. 🙂

If you choose PowerShell ISE, you’ll see it opens and presents you with a script.  You’ll need to run this script to connect to your SCCM Site.


#
# Press 'F5' to run this script. Running this script will load the ConfigurationManager
# module for Windows PowerShell and will connect to the site.
#
# This script was auto-generated at '11/13/2018 4:39:58 PM'.

# Uncomment the line below if running in an environment where script signing is
# required.
#Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process

# Site configuration
$SiteCode = "SCCM_SITE" # Site code
$ProviderMachineName = "SCCM_Server.DOMAIN.COM" # SMS Provider machine name

# Customizations
$initParams = @{}
#$initParams.Add("Verbose", $true) # Uncomment this line to enable verbose logging
#$initParams.Add("ErrorAction", "Stop") # Uncomment this line to stop the script on any errors

# Do not change anything below this line

# Import the ConfigurationManager.psd1 module
if((Get-Module ConfigurationManager) -eq $null) {
Import-Module "$($ENV:SMS_ADMIN_UI_PATH)\..\ConfigurationManager.psd1" @initParams
}

# Connect to the site's drive if it is not already present
if((Get-PSDrive -Name $SiteCode -PSProvider CMSite -ErrorAction SilentlyContinue) -eq $null) {
New-PSDrive -Name $SiteCode -PSProvider CMSite -Root $ProviderMachineName @initParams
}

# Set the current location to be the site code.
Set-Location "$($SiteCode):\" @initParams

Once the ISE was open, I could paste in my new script and run it.


# Set a schedule to update the membership of each collection.
$Schedule1 = New-CMSchedule -Start "10/17/2018 6:53 PM" -DayOfWeek Monday -RecurCount 1
$Schedule2 = New-CMSchedule -Start "10/17/2018 6:53 PM" -DayOfWeek Tuesday -RecurCount 1
$Schedule3 = New-CMSchedule -Start "10/17/2018 6:53 PM" -DayOfWeek Wednesday -RecurCount 1
$Schedule4 = New-CMSchedule -Start "10/17/2018 6:53 PM" -DayOfWeek Thursday -RecurCount 1
$Schedule5 = New-CMSchedule -Start "10/17/2018 6:53 PM" -DayOfWeek Friday -RecurCount 1
$Schedule6 = New-CMSchedule -Start "10/17/2018 6:53 PM" -DayOfWeek Saturday -RecurCount 1
$Schedule7 = New-CMSchedule -Start "10/17/2018 6:53 PM" -DayOfWeek Sunday -RecurCount 1

# Create the new Device Collection, using ALL SYSTEMS as the limiting collection and the above schedules for the refreash schedule.
New-CMDeviceCollection -Name "SCCM_SITE1_PRD_Group_1" -LimitingCollectionName "All Systems" -RefreshSchedule $Schedule1 -RefreshType Periodic
New-CMDeviceCollection -Name "SCCM_SITE1_PRD_Group_2" -LimitingCollectionName "All Systems" -RefreshSchedule $Schedule2 -RefreshType Periodic
New-CMDeviceCollection -Name "SCCM_SITE1_PRD_Group_3" -LimitingCollectionName "All Systems" -RefreshSchedule $Schedule4 -RefreshType Periodic
New-CMDeviceCollection -Name "SCCM_SITE1_PRD_Group_4" -LimitingCollectionName "All Systems" -RefreshSchedule $Schedule5 -RefreshType Periodic
New-CMDeviceCollection -Name "SCCM_SITE1_PRD_EXCLUSIONS" -LimitingCollectionName "All Systems" -RefreshSchedule $Schedule3 -RefreshType Periodic
New-CMDeviceCollection -Name "SCCM_SITE2_PRD_Group_1" -LimitingCollectionName "All Systems" -RefreshSchedule $Schedule6 -RefreshType Periodic
New-CMDeviceCollection -Name "SCCM_SITE2_PRD_Group_2" -LimitingCollectionName "All Systems" -RefreshSchedule $Schedule7 -RefreshType Periodic
New-CMDeviceCollection -Name "SCCM_SITE2_PRD_Group_3" -LimitingCollectionName "All Systems" -RefreshSchedule $Schedule6 -RefreshType Periodic
New-CMDeviceCollection -Name "SCCM_SITE2_PRD_Group_4" -LimitingCollectionName "All Systems" -RefreshSchedule $Schedule7 -RefreshType Periodic
New-CMDeviceCollection -Name "SCCM_SITE2_PRD_EXCLUSIONS" -LimitingCollectionName "All Systems" -RefreshSchedule $Schedule7 -RefreshType Periodic

# Finally, add a query expression to the above device collections and link to the domain groups previously created.
Add-CMDeviceCollectionQueryMembershipRule -CollectionName "SCCM_Site1_PRD_Group_1" -QueryExpression "select * from SMS_R_System where SMS_R_System.SystemGroupName = 'DOMAIN\\SCCM_Site1_PRD_Group_1'" -RuleName "SCCM_Site1_PRD_Group_1"
Add-CMDeviceCollectionQueryMembershipRule -CollectionName "SCCM_Site1_PRD_Group_2" -QueryExpression "select * from SMS_R_System where SMS_R_System.SystemGroupName = 'DOMAIN\\SCCM_Site1_PRD_Group_2'" -RuleName "SCCM_Site1_PRD_Group_2"
Add-CMDeviceCollectionQueryMembershipRule -CollectionName "SCCM_Site1_PRD_Group_3" -QueryExpression "select * from SMS_R_System where SMS_R_System.SystemGroupName = 'DOMAIN\\SCCM_Site1_PRD_Group_3'" -RuleName "SCCM_Site1_PRD_Group_3"
Add-CMDeviceCollectionQueryMembershipRule -CollectionName "SCCM_Site1_PRD_Group_4" -QueryExpression "select * from SMS_R_System where SMS_R_System.SystemGroupName = 'DOMAIN\\SCCM_Site1_PRD_Group_4'" -RuleName "SCCM_Site1_PRD_Group_4"
Add-CMDeviceCollectionQueryMembershipRule -CollectionName "SCCM_Site1_PRD_EXCLUSIONS" -QueryExpression "select * from SMS_R_System where SMS_R_System.SystemGroupName = 'DOMAIN\\SCCM_Site1_PRD_EXCLUSIONS'" -RuleName "SCCM_Site1_PRD_EXCLUSIONS"
Add-CMDeviceCollectionQueryMembershipRule -CollectionName "SCCM_Site2_PRD_Group_1" -QueryExpression "select * from SMS_R_System where SMS_R_System.SystemGroupName = 'DOMAIN\\SCCM_Site2_PRD_Group_1'" -RuleName "SCCM_Site2_PRD_Group_1"
Add-CMDeviceCollectionQueryMembershipRule -CollectionName "SCCM_Site2_PRD_Group_2" -QueryExpression "select * from SMS_R_System where SMS_R_System.SystemGroupName = 'DOMAIN\\SCCM_Site2_PRD_Group_2'" -RuleName "SCCM_Site2_PRD_Group_2"
Add-CMDeviceCollectionQueryMembershipRule -CollectionName "SCCM_Site2_PRD_Group_3" -QueryExpression "select * from SMS_R_System where SMS_R_System.SystemGroupName = 'DOMAIN\\SCCM_Site2_PRD_Group_3'" -RuleName "SCCM_Site2_PRD_Group_3"
Add-CMDeviceCollectionQueryMembershipRule -CollectionName "SCCM_Site2_PRD_Group_4" -QueryExpression "select * from SMS_R_System where SMS_R_System.SystemGroupName = 'DOMAIN\\SCCM_Site2_PRD_Group_4'" -RuleName "SCCM_Site2_PRD_Group_4"
Add-CMDeviceCollectionQueryMembershipRule -CollectionName "SCCM_Site2_PRD_EXCLUSIONS" -QueryExpression "select * from SMS_R_System where SMS_R_System.SystemGroupName = 'DOMAIN\\SCCM_Site2_PRD_EXCLUSIONS'" -RuleName "SCCM_Site2_PRD_EXCLUSIONS"

The only thing I had to do was UPDATE MEMBERSHIP to verify the SCCM Device Group is in fact linked to the AD Group, and sort the new SCCM Device Groups into the folder I wanted them in in SCCM. 🙂

I hope someone finds this helpful!

Ben Liebowitz, VCP, vExpert
NJ VMUG Leader

Share This:

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.