Managing Software Updates on the ConfigMgr Server – Part 1

Dec 15th, 2011

Collecting and deploying software updates on the server is a regular task (at least monthly) and somewhat tedious. To assist with this process, I’ve developed a handful of scripts that help to automate some of this work.

In this first article, I want to share a script that I use to actually create the update list. This script will search for updates that have not yet been deployed and that are actually required by at least one client system (so you need to usually wait a day or so for inventory of any new updates to be gathered).

The script will default to creating an update list called Software Updates YYYY-MM for the current month. If one already exists, it appends a lowercase letter to the end to provide unique update lists.

Once the update list is created, you can go into the ConfigMgr console to actually review the list (to ensure that it is or is not deploying the latest Internet Explorer, for example), to accept any EULAs, and to actually download the updates into a deployment package.

This script should be run from the ConfigMgr site server itself. Be sure to replace the *** in the “set SCCM” line to your site code. This script produces output and should be run under CSCRIPT.

For command-line options, run the script with the “/?” switch.

Save the following as UpdateList.wsf:

<job>
<runtime>
<description>
This script will create an update list that contains updates according to the
criteria specified on the command line.
</description>
<named name=Range					helpstring="Range of months back from today to search for updates                           (default 2)" type=string required=false />
<named name=IncludeDeployed 		helpstring="Include updates that have already been deployed" type=simple required=false />
<named name=IncludeUnrequired		helpstring="Include updates that are not required" type=simple required=false />
<named name=Categories 				helpstring="Specifies categories to include separated by commas                             (default SU,CU,UR,DU)" type=string required=false />
<named name=ListName				helpstring="Name of the Update List to create                                               (default Software Updates YYYY-MM)" type=string required=false />
<named name=IncludeProducts			helpstring="Display names of products to include updates for,								regardless of category (default Silverlight)" type=string required=false />
</runtime>
<script language=vbscript>

'Connect to SCCM WMI provider
set SCCM = GetObject("winmgmts://./root/sms/site_***") 'Replace *** with your sitecode!

Set WMIDate = CreateObject("WbemScripting.SWbemDateTime")

'Note any articles that should _never_ be automatically included
ArticlesToNeverInclude=Array("974571")

'Note any products that should _never_ be automatically included
ProductsToNeverInclude=Array("ProductFamily:352f9494-d516-4b40-a21a-cd2416098982", _
		"Product:83a83e29-7d55-44a0-afed-aea164bc35e6", _
		"Product:3cf32f7c-d8ee-43f8-a0da-8b88a6f8af1a", _
		"Product:26bb6be1-37d1-4ca6-baee-ec00b2f7d0f1", _
		"Product:9b135dd5-fc75-4609-a6ae-fb5d22333ef0")	'All versions of Exchange server

'Set variables based on parameters

Range = 2
If WScript.Arguments.Named.Exists("Range") Then
	If IsNumeric(WScript.Arguments.Named("Range")) Then Range = WScript.Arguments.Named("Range")
End If
RangeStart = DateAdd("m",0-Range,Now())

IncludeDeployed = False
If WScript.Arguments.Named.Exists("IncludeDeployed") Then IncludeDeployed = True

IncludeUnrequired = False
If WScript.Arguments.Named.Exists("IncludeUnrequired") Then IncludeUnrequired = True

Categories = "SU,CU,UR,DU"
If WScript.Arguments.Named.Exists("Categories") Then Categories = UCase(WScript.Arguments.Named("Categories"))

IncludeProducts = "Silverlight,PowerShell"
If WScript.Arguments.Named.Exists("IncludeProducts") Then IncludeProducts = WScript.Arguments.Named("IncludeProducts")

ListName = "Software Updates " & Year(Now()) & "-"
If Month(Now())<10 Then ListName = ListName & "0"
ListName = ListName & Month(Now())
If WScript.Arguments.Named.Exists("ListName") Then ListName = WScript.Arguments.Named("ListName")

'Create category filter
If InStr(Categories,",") Then arrCat = Split(Categories,",") Else arrCat = Array(Categories)
WQL = "Select * from SMS_UpdateCategoryInstance where CategoryTypeName='UpdateClassification'"
Set SCCMCats = SCCM.ExecQuery(WQL)
For Each SCCMCat in SCCMCats
	If Instr(SCCMCat.LocalizedCategoryInstanceName," ") Then Words = Split(SCCMCat.LocalizedCategoryInstanceName," ") Else Words=Array(SCCMCat.LocalizedCategoryInstanceName)
	Initials = ""
	For I = LBound(Words) to UBound(Words)
		Initials = Initials & UCase(Left(Words(I),1))
	Next
	For I = LBound(arrCat) to UBound(arrCat)
		If arrCat(I) = Initials Then arrCat(I) = SCCMCat.CategoryInstance_UniqueID
	Next
Next
CategoryFilter = " and ("
For I = LBound(arrCat) to UBound(arrCat)
	CategoryFilter = CategoryFilter & "CategoryInstance_UniqueIDs='" & arrCat(I) & "' or "
Next

'Include product filter
If Instr(IncludeProducts,",") Then arrProducts = Split(IncludeProducts,",") Else arrProducts = Array(IncludeProducts)
WQL = "Select * from SMS_UpdateCategoryInstance where CategoryTypeName='Product'"
Set SCCMProds = SCCM.ExecQuery(WQL)
For Each SCCMProd in SCCMProds
	For I = LBound(arrProducts) to UBound(arrProducts)
		If UCase(SCCMProd.LocalizedCategoryInstanceName) = UCase(arrProducts(I)) Then arrProducts(I) = SCCMProd.CategoryInstance_UniqueID
	Next
Next
For I = LBound(arrProducts) to UBound(arrProducts)
	CategoryFilter = CategoryFilter & "CategoryInstance_UniqueIDs='" & arrProducts(I) & "' or "
Next

CategoryFilter = Left(CategoryFilter,Len(CategoryFilter)-4) & ")"

'Exclude Article Filter
ArticleFilter = " and not ("
For I = LBound(ArticlesToNeverInclude) to UBound(ArticlesToNeverInclude)
	ArticleFilter = ArticleFilter & "ArticleID='" & ArticlesToNeverInclude(I) & "' or "
Next
ArticleFilter = Left(ArticleFilter,Len(ArticleFilter)-4) & ")"

'Exclude Product Filter
ProductFilter = " and not ("
For I = LBound(ProductsToNeverInclude) to UBound(ProductsToNeverInclude)
	ProductFilter = ProductFilter & "CategoryInstance_UniqueIDs='" & ProductsToNeverInclude(I) & "' or "
Next
ProductFilter = Left(ProductFilter,Len(ProductFilter)-4) & ")"

'Locate updates that match our criteria
WScript.Echo "Querying for updates..."
WQL = "Select * from SMS_SoftwareUpdate where IsExpired=0 and DateRevised >= '" & RangeStart & "' and LocalizedDisplayName not like '%for Itanium-based Systems%'"
WQL = WQL & " and LocalizedDisplayName not like '%IA64-based%' and LocalizedDisplayName not like '%Windows Embedded%'"
If Not IncludeDeployed Then WQL = WQL & " and IsDeployed=0"
If Not IncludeUnrequired Then WQL = WQL & " and NumMissing>0"
WQL = WQL & CategoryFilter & ArticleFilter & ProductFilter
'WScript.Echo WQL
Set UpdateCollection=SCCM.ExecQuery(WQL)
WScript.Echo "...Found " & UpdateCollection.Count & " update(s) that match criteria"

Dim UpdateCI_IDs()
Redim UpdateCI_IDs(-1)

For Each Update in UpdateCollection
	'Apply some filters to the updates
	WScript.Echo Update.ArticleID & vbTab & Update.DateRevised & vbTab & Update.LocalizedDisplayName
	Redim Preserve UpdateCI_IDs(UBound(UpdateCI_IDs)+1)
	UpdateCI_IDs(UBound(UpdateCI_IDs)) = Update.CI_ID
Next

WScript.Echo
WScript.Echo  "Press ENTER to create the list"
X = WScript.StdIn.ReadLine

'Ensure that the list name is unique
ListNameToUse=ListName
I = 96
WQL = "Select * from SMS_AuthorizationList where LocalizedDisplayName like '" & ListName & "%'"
Set ExistingLists = SCCM.ExecQuery(WQL)
For Each ListItem in ExistingLists
	If Not IsNumeric(Right(ListItem.LocalizedDisplayName,1)) Then
		C = Asc(Right(ListItem.LocalizedDisplayName,1))
		If C > I Then I = C
	End If
Next
If ExistingLists.Count > 0 Then ListNameToUse = ListName & Chr(I+1)
WScript.Echo "Creating Update List " & ListNameToUse

Set Info = SCCM.Get("SMS_CI_LocalizedProperties").SpawnInstance_
Info.Description="Auto-generated Update List"
Info.DisplayName=ListNameToUse
Info.InformativeURL=""
Info.LocaleID="1033"
Set List = SCCM.Get("SMS_AuthorizationList").SpawnInstance_
List.Updates = UpdateCI_IDs
List.LocalizedInformation = Array(Info)
List.Put_
</script>
</job>