In System Center 2012 Configuration Manager, Microsoft added the capability to automatically remove software
update content from distribution points when that content is related to expired updates. 
This process helps manage drive space on your distribution points by removing any content you no longer need.
It’s particularly helpful for Endpoint Protection definition updates, given their frequency of release, deployment,
and expiration.  In this blog, I’m going to walk you through exactly how the process works, and I’m also
going to provide a script you can use to clean up source locations related to obsolete updates, as our out-of-box
process does not manage source content.

Expired Updates and Deployments

The first part of the process for managing content related to expired updates is getting expired updates out
of any deployed update groups.  First, we never delete any expired update associated with
an active deployment, as we don’t want to remove anything associated with your deployments.
It could be disconcerting if updates simply disappeared from your deployments and you had no idea why.
The step to make expired updates removable, however, is straightforward, and it should be part of a monthly process.
To remove expired updates from all deployments in only a couple of clicks, do the following.

Note:  When using Auto Deployment Rules to deliver definition updates, where you should be re-using the same
update group each time the rule runs, expired updates are automatically removed from the update group each time the rule runs.

  1. Navigate to the All Software Updates node under Software Library, and search for all expired updates. 
  2. Add the value for expired updates to search, leave the default value as “Yes”, and click search.

The search results will include all of your expired updates, so simply select all updates in the list view (CTRL+A), right-click, and choose Edit Membership.


You will then see a list of all Update Groups where any of the selected updates from the list view are members.
Simply uncheck the selected check boxes.

  1. Click OK to remove all of the expired updates from the selected Update Groups, and they’ll be ready for deletion through the process outlined in the next step.

The Delete-Expired-Updates Process

Next, there are four phases to removing expired updates and its related content, and those are:  the expiration action, tomb-stoning, deletion, and then source cleanup, which is a scripted action.  At a high level, updates that have been expired and that aren’t part of an active deployment will be deleted 7 days after they are expired.  There is no specific or configurable maintenance task to do this—it’s an automated process that crosses multiple ConfigMgr components.   Let’s walk through the process at a detailed level so you understand how it works.

  1. When a full software update point synchronization runs, updates are expired.  A full software update point synchronization is a scheduled synchronization, not one initiated through the console, which is a delta synchronization.  Only full/scheduled synchronizations expire updates.
  2. Full synchronizations expire updates for the following reasons:
    1. Updates expired by the publisher.
    2. Updates superseded by the publisher (and outside of the “keep superseded updates” time that you configured for the software update point).
    3. Updates declined or missing from WSUS.
  3. You can see expiration activity in logs, through wsyncmgr.log.  Look for a line in the log that looks like the following:Removed 106 unreferenced updates     SMS_WSUS_SYNC_MANAGER  3/30/2012 12:06:15 AM
  4. Expired updates still show up in the console, but they are marked as expired, and have the following icon showing this state:  They will remain in the console for 7 days following expiration, where the next process around cleanup starts.
  5. After 7 days, expired updates that are not associated with active deployments, are tomb-stoned.  This means they are no longer visible in the UI.  Again, use wsyncmgr.log to view the removal of these updates:Deleting old expired updates… SMS_WSUS_SYNC_MANAGER  3/30/2012 12:06:16 AM 
    Deleted 80 expired updates        SMS_WSUS_SYNC_MANAGER  3/30/2012 12:06:20 AM 
    Deleted 80 expired updates total             SMS_WSUS_SYNC_MANAGER  3/30/2012 12:06:20 AM
  6. Next, object replication manager is notified of deletions through .CIN notification files dropped into the notification file C:\Program Files\Microsoft Configuration Manager\inboxes\\16824032.CIN     SMS_OBJECT_REPLICATION_MANAGER                3/30/2012 12:06:11 AM 
    Completed processing CIN notification files SMS_OBJECT_REPLICATION_MANAGER                3/30/2012 12:07:31 AM 
    Successfully deleted SoftwareUpdate 3da7abb4-d643-422c-9e6d-a5f838fb71a1                SMS_OBJECT_REPLICATION_MANAGER               3/30/2012 12:07:32 AM
  7. Finally, distribution manager checks every hour, and removes any Distribution Point content associated with update CIs that have been deleted by objreplmgr.
  8. Now that both metadata and distributed content for expired updates has been removed, we need to go ahead and cleanup the source directory, which can be done with the script at the end of this blog.
  9. Simply create a VBS script from what’s provided below, and run it on the top-level site server (CAS or standalone Primary Site).  Because of formatting issues, line breaks were inserted at the locations indicated by [CR]. Please remove them before executing the script.  Some notes on the script:
    1. Make sure you have rights to the content source (from the site server you are running the script on).
    2. The script deletes source content for any update that no longer has an associated CI.
    3. The source directory cleanup will not necessarily map to date-time stamps in the source directory.  Some binaries are chained to other binaries, for which there may still be a CI reference in the database.  In other words, if you cleanup Endpoint Protection definitions, yet the source directory still has files and folders older than 7 days, then those files still have active CIs and are needed (don’t delete them manually).
    4. Finally, the script looks for source content that is definitively not needed by a referencing CI, and it removes just that source content.

Wrapping Up

Expired update and associated content cleanup in Configuration Manager 2012 is a built-in mechanism to help keep your console, database, distribution points and (with the script) source directories as clean as possible.  Hopefully, you better understand the manual steps required to do this, as well as how all of the automated pieces work together to manage expired updates and content.

option explicit
dim CRLF: CRLF=chr(13) & chr(10)
dim WSH: set WSH = CreateObject("WScript.Shell")
dim FSO: set FSO = CreateObject("Scripting.FileSystemObject")
dim WMI: set WMI = GetObject("winmgmts:/root/SMS")
dim Prov: set Prov = nothing
dim I, f
for each I in WMI.ExecQuery("select * from SMS_ProviderLocation where ProviderForLocalSite=TRUE")
    set Prov = I
if Prov is nothing then err.Raise 438,"No provider found for the local site"
dim SiteCode: SiteCode = Prov.SiteCode
dim SiteServer: SiteServer = Prov.Machine
set WMI = GetObject("winmgmts:" & Prov.NamespacePath)
dim Pkg
for each Pkg in WMI.ExecQuery("select * from SMS_SoftwareUpdatesPackage")
	Log "Processing package " & Pkg.PackageID & " - " & Pkg.Name
	' remove orphaned package-to-content relations (not associated with CIs)
for each i in WMI.ExecQuery("select pc.* from SMS_PackageToContent pc left join [CR]
SMS_CIToContent cc on cc.ContentID=pc.ContentID where pc.PackageID=""" & Pkg.PackageID & """ and [CR]
cc.ContentID is null")

	Log "Package source path: " & Pkg.PkgSourcePath
	dim srcFolder: set srcFolder = FSO.GetFolder(Pkg.PkgSourcePath)
    dim foldersToDelete: set foldersToDelete = CreateObject("Scripting.Dictionary")
	foldersToDelete.CompareMode = vbTextCompare
	' collect subfolders currently in pkg source
    for each i in srcFolder.SubFolders
		if i.Attributes=16 and i.Name<>"." and i.Name<>".." then 
            'Log "Existing folder " & i.Name
            foldersToDelete.Add i.Name, nothing
        end if
	' exclude subfolders associated with active content
for each i in WMI.ExecQuery("select pc.* from SMS_PackageToContent pc where [CR]
pc.PackageID=""" & Pkg.PackageID & """")

        'Log "Excluding active folder " & i.ContentSubFolder
if foldersToDelete.Exists(i.ContentSubFolder) then [CR]

	f = vbFalse
	' delete remaining folders
	for each i in srcFolder.SubFolders
        if foldersToDelete.EXists(i.NAme) then
            Log "Deleting orphaned subfolder " &
            f = vbTrue
        end if 
	if f = vbTrue then
        Log "Refreshing package " & pkg.PackageID
	end if
Log "cleanup completed"
'================== logging support ==============
dim logMode
dim logWindow
sub Log(msg)
	if IsEmpty(logWindow) then call LogInit
	select case logMode
		case "console":	WScript.StdOut.WriteLine msg
case "window": if IsObject(logWindow) then [CR]
logWindow.document.all.logLines.innerHtml = logWindow.document.all.logLines.innerHTML & msg & [CR] "
" end select end sub sub LogInit if not WScript.Interactive then logMode = "none" elseif lcase(right(WScript.FullName, 11))="cscript.exe" then logMode = "console" else set logWindow = WScript.CreateObject("InternetExplorer.Application", [CR] "Log_") with logWindow .Navigate("about:blank") .Document.Title = WScript.ScriptName .AddressBar = false .ToolBar = false .StatusBar = false .Resizable = true .Visible = 1 do while .Busy: WScript.Sleep 100: loop .document.body.innerHTML = "
" end with logMode = "window" end if end sub sub LogTerm if IsObject(logWindow) then logWindow.Quit: set logWindow = nothing end sub ' provide a way of terminating the script in windowed mode - closing the log window will [CR] terminate the script sub Log_onQuit WScript.Quit end sub