Managing Software Updates on the ConfigMgr Server – Part 4
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>