System Center Configuration Manager 2007 provides update lists to manage various aspects of the software update management process. An update list is as the name implies just a list of software updates and provides the following benefits:
- The deployment of software updates can be delegated to administrators in child sites. The update list created on the parent site will be replicated down the hierarchy to child sites. Administrators in child sites can then deploy the updates in that list to a collection of clients.
- Software update lists can be used for multiple deployments of the same set of updates.
- Software update lists allow to track the compliance status of update deployments against a defined set of software updates.
Even though software update lists are not absolutely required they are strongly recommended because they simplify the process of deploying software updates.
One aspect I do not particularly like about software update lists is that you need to search and select every single software update you want to add to the update list. If your baseline is, for instance, still Windows XP Service Pack 2, you need to manually select more than 100 software updates. Although you can use appropriate search folders, it is still a too complicated and painful process to initially set up the software update management infrastructure.
Therefore I have written a PowerShell script to make this process easier. The idea is to have a reference client which represents the baseline for a particular client or server type. This reference client needs to have the Configuration Manager client installed and must have successfully completed a software update scan. The script then creates an update list with all software updates reported as missing for this particular client.
The Configuration Manager 2007 Software Development Kit (SDK) describes how to programmatically create an update list by creating an instance of the class SMS_AuthorizationList. The script then queries the class SMS_UpdateComplianceStatus to determine the missing software updates for the client.
param(
$ReferenceClient,
$UpdateListName,
$SiteServer,
[switch] $force,
[switch] $verbose
)
$AppName = "Create-UpdateList"
$manpage = @'
NAME
Create-UpdateList
SYNOPSIS
Creates a Configuration Manager update list.
SYNTAX
Create-UpdateList -ReferenceClient -UpdateListName [-SiteServer ] [-Force] [-Verbose]
DETAILED DESCRIPTION
This script creates an update list based on the inventory data of a reference client. The
reference client should represent the baseline for a specific operating system used within
your organization. This client needs to be present in the Configuration Manager database
with valid inventory data. This script then creates an update list which includes all the
software updates reported as missing from the reference client.
PARAMETERS
-ReferenceClient
Specifies the name of the reference client.
-UpdateListName
Specifies the name of the update list to be created.
-SiteServer
Optional: Specifies the Configuration Manager site server. If not specified, the
local computer is assumed to be the site server.
-Force
Optional: Creates an update list even if an update list with the same display name
already exists.
-Verbose
Optional: Generates detailed information about the script's operations.
'@
if (!$ReferenceClient -or !$UpdateListName) {
Write-Host $manpage
exit
}
if ($args.count -eq 1) {
$siteserver = $args[0]
}
elseif ($args.count -gt 1) {
Write-Host $manpage
exit
}
if (!$siteserver) { $siteserver = $env:computername }
$namespace = "root\sms"
if ($verbose) {
Write-Host "`nReferenceClient: $ReferenceClient"
Write-Host "UpdateListName : $UpdateListName"
Write-Host "SiteServer : $SiteServer`n"
}
$smsContext = New-Object System.Management.ManagementNamedValueCollection
$smsContext.Add("ApplicationName", $AppName)
$smsContext.Add("MachineName", $env:computername)
$smsContext.Add("LocaleID", 1033)
$connOptions = New-Object System.Management.ConnectionOptions
$connOptions.Context = $smsContext
$path = New-Object System.Management.ManagementPath
$path.NamespacePath = "\\$siteserver\" + $namespace
$scope = New-Object System.Management.ManagementScope($path, $connOptions)
$ErrorActionPreference = “silentlycontinue”
$scope.Connect()
if (!$?) {
$ErrorActionPreference = “continue”
$cred = Get-Credential
if (!$cred) {
Write-Host "No credentials supplied." -foregroundcolor Red -backgroundcolor Black
exit
}
# Property "SecurePassword" requires .NET Framework 2.0 SP1 or higher!
$connOptions.Username = $cred.Username
$connOptions.SecurePassword = $cred.Password
$scope.Options = $connOptions
$ErrorActionPreference = “silentlycontinue”
$scope.Connect()
if (!$?) {
Write-Host "Could not connect to site server $siteserver." -foregroundcolor Red -backgroundcolor Black
Write-Host $error[0] -foregroundcolor Red -backgroundcolor Black
exit
}
}
elseif ($verbose) {
Write-Host "Successfully connected to \\$siteserver\$namespace."
}
$ErrorActionPreference = “continue”
$wqlquery = "SELECT * FROM SMS_ProviderLocation"
$query = New-Object System.Management.ObjectQuery($wqlquery)
$searcher = New-Object System.Management.ManagementObjectSearcher($scope, $query)
$providerLoc = $searcher.Get()
if (!$providerLoc) {
Write-Host "Could not get instances from the SMS_ProviderLocation class." -foregroundcolor Red -backgroundcolor Black
exit
}
foreach ($providerInst in $providerLoc) {
if (!$providerInst.ProviderForLocalSite) {
Write-Host "SMS Provider $providerInst.SiteCode not set as local site server." -foregroundcolor Red -backgroundcolor Black
exit
}
else {
$sitecode = $providerInst.SiteCode
}
}
$namespace = "root\sms\site_$sitecode"
$ErrorActionPreference = “silentlycontinue”
$scope.Path = "\\$siteserver\$namespace"
$scope.Connect()
if (!$?) {
Write-Host "Could not connect to site server $siteserver." -foregroundcolor Red -backgroundcolor Black
exit
}
elseif ($verbose) {
Write-Host "Successfully connected to \\$siteserver\$namespace."
}
$ErrorActionPreference = “continue”
# Check if the specified reference client already exists within the ConfigMgr database
$wqlquery = 'SELECT ResourceID FROM SMS_R_System WHERE Name = ' + '"' + "$ReferenceClient" + '"' + ' AND Active = 1'
if ($verbose) {
Write-Host "Verifying if the reference client $ReferenceClient actually exists in the ConfigMgr database."
Write-Host "Running WQL query: $wqlquery."
}
$query = New-Object System.Management.ObjectQuery($wqlquery)
$searcher = New-Object System.Management.ManagementObjectSearcher($scope, $query)
$searcher.Get() | Foreach-Object { $rscID = $_.ResourceID }
if (!$rscID) {
Write-Host "Could not find the reference client $ReferenceClient in the Configuration Manager database." -foregroundcolor Red -backgroundcolor Black
exit
}
if ($verbose) {
Write-Host "Found $ReferenceClient in the database with Resource ID $rscID."
}
# Check if the specified name for the update list is already in use
$wqlquery = "SELECT * FROM SMS_AuthorizationList WHERE LocalizedDisplayName = " + "'"
$wqlquery += $UpdateListName + "'"
if ($verbose) {
Write-Host "Check if the specified name for the update list is already in use."
Write-Host "Running WQL query: $wqlquery."
}
$query = New-Object System.Management.ObjectQuery($wqlquery)
$searcher = New-Object System.Management.ManagementObjectSearcher($scope, $query)
$searcher.Get() | Foreach-Object { $ListID = $_.CI_ID }
if ($ListID) {
if (!$force) {
$msg = "`nAn update list with the name $UpdateListName already exists. If you want the update list to be created, "
$msg += "please specify the -force switch."
Write-Host $msg -foregroundcolor Yellow -backgroundcolor Black
exit
}
}
elseif ($verbose) {
Write-Host "An update list with the name $UpdateListName does not exist."
}
# Get the missing software updates reported for the reference client
$wqlquery = "SELECT css.CI_ID FROM SMS_UpdateComplianceStatus css "
$wqlquery += "JOIN SMS_SoftwareUpdate ui ON css.CI_ID = ui.CI_ID "
$wqlquery += "WHERE css.MachineID = $rscID AND css.Status = 2"
if ($verbose) {
Write-Host "Getting required software updates for $ReferenceClient from the ConfigMgr database."
Write-Host "Running WQL query: $wqlquery."
}
$swupdates = New-Object System.Collections.ArrayList
$query = New-Object System.Management.ObjectQuery($wqlquery)
$searcher = New-Object System.Management.ManagementObjectSearcher($scope, $query)
$searcher.Get() | Foreach-Object { [void] $swupdates.Add($_['CI_ID']) }
if (!$swupdates) {
Write-Host "Reference Client $ReferenceClient has all required software updates installed." -foregroundcolor Yellow -backgroundcolor Black
Write-Host "No Update List will be created." -foregroundcolor Yellow -backgroundcolor Black
exit
}
# Get the LocaleID of the site server installation
$wqlquery = 'SELECT * FROM SMS_Identification'
$query = New-Object System.Management.ObjectQuery($wqlquery)
$searcher = New-Object System.Management.ManagementObjectSearcher($scope, $query)
$searcher.Get() | Foreach-Object { $LocaleID = $_.LocaleID }
if (!$LocaleID) { $LocaleID = 1033 }
if ($verbose) {
Write-Host "Using LocaleID $LocaleID."
}
$options = New-Object System.Management.ObjectGetOptions
$options.Context = $smsContext
$path = New-Object System.Management.ManagementPath("\\$siteserver\$namespace" + ":SMS_CI_LocalizedProperties")
$smsCiLoc = (New-Object System.Management.ManagementClass($scope, $path, $options)).CreateInstance()
# Workaround a PowerShell V1 issue
[void] $smsCiLoc.psbase.Properties
$smsCiLoc.DisplayName = $UpdateListName
$smsCiLoc.LocaleID = $LocaleID
[System.Management.ManagementObject[]] $newDescriptionInfo += $smsCiLoc
$options = New-Object System.Management.ObjectGetOptions
$options.Context = $smsContext
$path = New-Object System.Management.ManagementPath("\\$siteserver\$namespace" + ":SMS_AuthorizationList")
$newUpdateList = (New-Object System.Management.ManagementClass($scope, $path, $options)).CreateInstance()
# Workaround a PowerShell V1 issue
[void] $newUpdateList.psbase.Properties
$newUpdateList.Updates = $swupdates
$newUpdateList.LocalizedInformation = $newDescriptionInfo
$putOptions = New-Object System.Management.PutOptions($smsContext)
$ErrorActionPreference = “silentlycontinue”
[void] $newUpdateList.Put($putOptions)
if (!$?) {
Write-Host "Could not create update list $UpdateListName."
Write-Host $error[0]
}
else {
Write-Host "Successfully created update list $UpdateListName."
}