So you want to automate against ConfigMgr… do you?

Automation is great. It really is. I’m using Azure Automation Hybrid Runbook workers for just about everything these days which is a post on its own, but wanted to touch base on some key interactions with ConfigMgr.

First, the premise. The automation servers are all running server 2016 core. So… no ConfigMgr console. No first popup of the console to set site assignments with powerShell. Actually, no real good way to get some default goodness with ConfigMgr at all. For the remainder of the post – the examples will all be dealing with my automation around updating OS Image Files.

So clearly I just copy over the powerShell module files with the .dlls in order to work better with the site server remotely. The simple route is to copy over the bin path C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin — to the same location on your core server. This will at least get you access to the powerShell modules.

Simple… but this is really messy in an automated world. Important here is making a new-psdrive and providing your service account credentials for the task at hand. Many of the ConfigMgr cmdlets do not allow for a -credential switch to provide access in line.

Import-Module "C:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1" -Force
$drive = get-psdrive -name $siteCode -ErrorAction SilentlyContinue
If(!$drive){New-psdrive -Name $siteCode -PSProvider "AdminUI.PS.Provider\CMSite" -root $siteServer -Credential $sccmServiceAccount  -Description "SCCM Site"}
$location = $siteCode+":"
Set-location $location
$date = get-date -format d
Set-CMOperatingSystemImage -Id $osID -Description $date -version "I'm a NEW and Shiny'"
Remove-Psdrive $siteCode

Alternatively, you could certainly create a powerShell session with credentials; and then invoke a script block to the session. Sure – this works, but it opens up requiring the allowance of powerShell remoting for your service account. Just something to consider if you’re comfortable putting all your automation in script blocks.

Now… into the rabbit hole. I don’t like having to copy about files from a console. What happens if something changes in the ConfigMgr site version that updates the powerShell cmdlets… and now which servers need this copied where? There are many other reasons to keep this stuff off my core infrastructure, but honestly – I just don’t want to have to think about it. Keep it simple. Keep it to powerShell.

So some other good options. Obviously, Cim/Wmi work well since so much of ConfigMgr is accessible via this channel. They are also going to work with providing credentials! So letting a service account handle a well scoped task isn’t that big of a hurdle. What the hurdle is, is that not all Wmi objects will have the appropriate methods to write data back to your site server.

# Reload the image properties before refreshing the package on the distribution points
$imageProperties = Get-WmiObject -ComputerName $siteServer  -Namespace "root/SMS/site_$sitecode" -Class 'SMS_ImagePackage' -Credential $sccmServiceAccount |where-object -property PackageID -eq $osID

While this was great to get the first task done of updating the files in the system… there was no way to update the description or version via the Wmi object. When you pipe through to get-member – the properties exist, but there is no put() method.

Option five. The bottom of the rabbit hole I went down is to connect directly to the SMS Provider using WMI. This requires creating a SWbemServices object. Say what? No need to explain.. Microsoft Docs Page thank you docsMsft!

I rewrote the VB sample in powerShell a while back, and did a pull request. So side note – if you see something, write something, pull request back!

$siteCode = ''
$siteServer = 'server.domain'

$credentials = Get-Credential
$username = $credentials.UserName

# The connector does not understand a PSCredential. The following command will pull your PSCredential password into a string.
$password = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($credentials.Password))

$NameSpace = "root\sms\site_$siteCode"
$SWbemLocator = New-Object -ComObject "WbemScripting.SWbemLocator"
$SWbemLocator.Security_.AuthenticationLevel = 6
$connection = $SWbemLocator.ConnectServer($siteServer,$Namespace,$username,$password)


With a connector in place, you can do something like below to update description/version and put()it back

$wim = $connection.Get("SMS_ImagePackage.PackageID='$osID'")
$wim.Properties_.Item("Version").value = $version
$wim.Properties_.Item("Description").value = $Description

Here is the short of it. I can still provide credentials to run as what ever service principal I need, and then I get the benefit of being able to do.. well anything I want since its a direct connection against the SMS Provider itself.

There you have it folks. A full repertoire of how to get things done when coding in an automated way against ConfigMgr. Pick you poison wisely, or just mix and match as needed.

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

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