Managing Software Updates on the ConfigMgr Server – Part 4

Dec 15th, 2011

This post has to deal with getting rid of the old, expired updates. Microsoft will replace its updates with newer versions and there is no need to continue to replicate, advertise, or even store old versions since the clients will not install them anyway.

The following script will get rid of old, expired updates and their files for you. Just run it on a regular basis to clean up the system.

Save the following as CleanupOldUpdates.wsf:

<job>
<runtime>
<named name="AlsoRemoveUnrequired" type=string required=false helpstring="Removes updates that are not required that are more than n days old (default is 7)" />
</runtime>
<script language=vbscript>
'This script will scan through the updates and remove old items from
'deployments, packages, and update lists where the Expired attribute
'is set to TRUE.

Set SMS = GetObject("winmgmts://./root/sms/site_***") 'Replace *** with your sitecode
Set SO = WScript.StdOut
Set WMIDate = CreateObject("WbemScripting.SWbemDateTime")
Set aNamed = WScript.Arguments.Named
If aNamed.Exists("AlsoRemoveUnrequired") Then
	If aNamed("AlsoRemoveUnrequired") = "" Then
		UnrequiredLimit = 7
	Else
		If IsNumeric(aNamed("AlsoRemoveUnrequired")) Then
			UnrequiredLimit = CLng(aNamed("AlsoRemoveUnrequired"))
		Else
			WScript.Arguments.ShowUsage
			WScript.Quit
		End If
	End If
	SO.WriteLine "Will also consider updates more than " & UnrequiredLimit & " days old that are not required to be" & vbCrLf & "expired"
	UnrequiredDate = DateAdd("d",0-UnrequiredLimit,Now())
	WMIDate.SetVarDate UnrequiredDate
	WMIDate.UTC=False
	UnrequiredDateWMI = WMIDate.Value
	SO.WriteLine UnrequiredDate
	SO.WriteLine UnrequiredDateWMI
End If

'*************************************************************************
' Clean up Deployments
'*************************************************************************

SO.WriteLine "*******************************************************************************"
SO.WriteLine "Cleaning up Deployments"
SO.WriteLine "*******************************************************************************"
SO.WriteLine

'Get the assignments and parse through them, looking for expired updates
Set collAssignments = SMS.Get("SMS_UpdatesAssignment").Instances_
Dim AssignedCIs()

For Each AssignmentItem in collAssignments
	Set Assignment=SMS.Get(Split(AssignmentItem.Path_,":")(1))	'This object has lazy properties that need to be obtained
	SO.WriteLine "ASSIGNMENT: " & Assignment.AssignmentName & " [" & Assignment.AssignmentID & "]"

	'We'll hold the updates assigned to this list in an array
	UpdateCount = 0
	UpdateCountOriginal = 0
	Redim AssignedCIs(UpdateCount)

	'Since enumerating updates is "expensive", we'll only target assignments that are known
	'to have expired updates
	If Assignment.ContainsExpiredUpdates or aNamed.Exists("AlsoRemoveUnrequired") Then
		SO.WriteLine "  Getting expired updates..."
		For I = LBound(Assignment.AssignedCIs) to UBound(Assignment.AssignedCIs)
			If UBound(Assignment.AssignedCIs)<>0 Then SO.Write String(79,Chr(8)) & FormatPercent(I/UBound(Assignment.AssignedCIs))
			Set Update=SMS.Get("SMS_SoftwareUpdate.CI_ID=" & Assignment.AssignedCIs(I))
			If Update.IsExpired Then
				SO.WriteLine String(79,Chr(8)) & "  " & Update.ArticleID & " is expired [" & Update.CI_ID & "]"
			ElseIf aNamed.Exists("AlsoRemoveUnrequired") and Update.NumMissing=0 and Update.NumPresent=0 and Update.DateRevised<UnrequiredDateWMI Then
				SO.WriteLine String(79,Chr(8)) & "  " & Update.ArticleID & " is not required [" & Update.CI_ID & "]"
			Else
				Redim Preserve AssignedCIs(UpdateCount)
				AssignedCIs(UpdateCount) = Update.CI_ID
				UpdateCount = UpdateCount + 1
			End If
			UpdateCountOriginal = UpdateCountOriginal + 1
		Next
		SO.Write String(79,Chr(8))

		'If the array is a different size than the original array, we've removed some
		If UpdateCount < UpdateCountOriginal Then
			SO.WriteLine "    Removed " & UpdateCountOriginal-UpdateCount & " update(s)"

			'Save the changes
			If Join(AssignedCIs,",")="" Then
				SO.WriteLine "!!! All updates need to be removed from this deployment.  Please add new ones or remove the deployment altogether."
				SO.WriteLine
			Else
				Assignment.AssignedCIs = AssignedCIs
				Assignment.Put_
			End If
		End If
	End If
Next

'*************************************************************************
' Clean up Packages
'*************************************************************************

SO.WriteLine
SO.WriteLine
SO.WriteLine "*******************************************************************************"
SO.WriteLine "Cleaning up Packages"
SO.WriteLine "*******************************************************************************"
SO.WriteLine

'Get the deployment packages
Set collPackages = SMS.Get("SMS_SoftwareUpdatesPackage").Instances_

'Find a list of all expired updates that are provisioned
Set collUpdates = SMS.ExecQuery("Select * from SMS_SoftwareUpdate where IsContentProvisioned = 1 and IsExpired = 1")
If aNamed.Exists("AlsoRemoveUnrequired") Then _
	Set collUpdatesUnreq = SMS.ExecQuery("Select * from SMS_SoftwareUpdate where IsContentProvisioned = 1 and NumMissing = 0 and NumPresent = 0 and DateRevised < '" & UnrequiredDate & "'")
Dim ContentIDs()
ContentCount = 0

For Each oUpdate in collUpdates
	SO.WriteLine "Compiling content IDs for expired update " & oUpdate.ArticleID & " [" & oUpdate.CI_ID & "]"
	Set collContent = SMS.ExecQuery("Select * from SMS_CIToContent where CI_ID = " & oUpdate.CI_ID)
	For Each oContent in collContent
		If oContent.ContentDownloaded Then
			SO.WriteLine "    Added content ID " & oContent.ContentID
			Redim Preserve ContentIDs(ContentCount)
			COntentIDs(ContentCount) = oContent.ContentID
			ContentCount = ContentCount + 1
		End If
	Next
Next

If aNamed.Exists("AlsoRemoveUnrequired") Then
	For Each oUpdate in collUpdatesUnreq
		SO.WriteLine "Compiling content IDs for unrequired update " & oUpdate.ArticleID & " [" & oUpdate.CI_ID & "]"
		set collContent = SMS.ExecQuery("Select * from SMS_CItoContent where CI_ID = " & oUpdate.CI_ID)
		For Each oContent in collContent
			if oContent.ContentDownloaded Then
				SO.WriteLine "    Added content ID " & oContent.ContentID
				Redim Preserve ContentIDs(ContentCount)
				ContentIDs(ContentCount) = oContent.ContentID
				ContentCount = ContentCount + 1
			End If
		Next
	Next
End If

SO.WriteLine "Found a total of " & ContentCount & " content item(s) to be removed"

If ContentCount > 0 Then
	On Error Resume Next
	For Each oPackage in collPackages
		SO.WriteLine "Removing from " & oPackage.Name & " [" & oPackage.PackageID & "]"
		oPackage.RemoveContent ContentIDs, True
		If Err.Number = 0 Then SO.WriteLine "    Success"
	Next
	On Error Goto 0
End If

'*************************************************************************
' Clean up Update Lists
'*************************************************************************

SO.WriteLine
SO.WriteLine
SO.WriteLine "*******************************************************************************"
SO.WriteLine "Cleaning up Update Lists"
SO.WriteLine "*******************************************************************************"
SO.WriteLine

'Get the update lists and parse through them, looking for expired updates
Set collLists = SMS.Get("SMS_AuthorizationList").Instances_
Dim Updates()

For Each ListItem in collLists
	'SO.WriteLine Split(ListItem.Path_,":")(1)
	Set List=SMS.Get(Split(ListItem.Path_,":")(1))	'This object has lazy properties that need to be obtained
	SO.WriteLine "UPDATE_LIST: " & List.LocalizedDisplayName & " [" & List.CI_ID & "]"

	'We'll hold the updates assigned to this list in an array of CI_IDs
	UpdateCount = 0
	Redim arrUpdate(UpdateCount)

	'Enumerate the existing updates assigned to the list
	Set collUpdateIDs = SMS.ExecQuery("Select SMS_SoftwareUpdate.* from SMS_CIRelation join SMS_SoftwareUpdate on SMS_CIRelation.ToCIID=SMS_SoftwareUpdate.CI_ID where SMS_CIRelation.FromCIID=" & List.CI_ID)
	'SO.WriteLine collUpdateIDs.Count

	'Check each one and, if expired, don't add it into our array - only good updates go into the array
	If collUpdateIDs.Count>0 Then
		For Each Update in collUpdateIDs
			If Update.IsExpired Then
				SO.WriteLine String(79,Chr(8)) & "  " & Update.ArticleID & " is expired [" & Update.CI_ID & "]"
			ElseIf aNamed.Exists("AlsoRemoveUnrequired") and Update.NumMissing=0 and Update.NumPresent=0 and Update.DateRevised<UnrequiredDateWMI Then
				SO.WriteLine String(79,Chr(8)) & "  " & Update.ArticleID & " is not required [" & Update.CI_ID & "]"
			Else
				Redim Preserve arrUpdate(UpdateCount)
				arrUpdate(UpdateCount) = Update.CI_ID
				UpdateCount = UpdateCount + 1
			End If
		Next
	End If

	'If the array is a different size than the original collection, we've removed some
	If UpdateCount < collUpdateIDs.Count Then
		SO.WriteLine "    Removed " & (collUpdateIDs.Count - UpdateCount) & " update(s)"

		'Save the new update membership
		If Join(arrUpdate,",") <> "" Then
			List.Updates = arrUpdate
			List.Put_
		Else
			SO.WriteLine "!!! All updates need to be removed - please remove this list outright or add current updates before cleaning up old ones."
			SO.WriteLine
		End If
	End If
Next
</script>
</job>
No comments yet.