Remove expired and superseded updates from a Software Update Group with PowerShell

If you’re working with patch management in ConfigMgr 2012, you’ve most likely scratched your head about why there’s no simple way to remove the updates that have been expired or superseded. To help with that I’ve created a PowerShell script that gives you the possibility to target a Software Update Group that you want to remove expired and superseded updates from and also remove any content that will be obsolete. While I was researching for this script I ran into a script that Trevor Sullivan (PowerShell MVP) created back in 2011 for ConfigMgr 2007. From what I can interpret of Trevor’s excellent script, it should work in a ConfigMgr 2012 environment as well. But I wanted some more dynamic to my own script and wanted it to work with PowerShells advanced functions.

Download information

You can download this script from my TechNet Gallery page.

Script documentation

I’ve built the script so that you can leverage PowerShell’s built in help functionality. You also have access to the common parameters like Verbose, Confirm and WhatIf. In addition to the common advanced functions the script has two switches that can be used. One of them is the ShowProgress switch that will show a progressbar with some extra information about the current operation. The second switch is called RemoveContent and when you specify that in your command, those Software Updates that will be removed from the specified Software Update Group, will also be removed from the Deployment Packages where they’ve been downloaded to. Remember that if you select to remove the content for the updates, if those updates are present in any other Software Update Group you’ll need to clean those groups as well. During the content removal process, for each Deployment Package that gets targeted, a refresh will be initated at the end.


In this demo scenario, I have a Software Update Group called Critical and Security Patches – Windows Clients 2014-10-18 00:29:04. Within this group there’s several expired and superseded Software Updates, as shown in the picture below:
To run the script and perform a clean up of this Software Update Group, follow the instructions below:
1. Download the script from the TechNet Gallery and save it in C:\Scripts.
2. Open an elevated PowerShell console and browse to where you saved the script.
3. Run the following command (remember to change the parameter values to suite your environment):

.\Clean-CMSoftwareUpdateGroup.ps1 -SiteServer CAS01 -SUGName "Critical and Security Patches - Windows Clients 2014-10-18 00:29:04" -ShowProgress -RemoveContent -Verbose

As we can see in the pictures above, it found 18 updates that was matching the criteria of being expired and superseded. Since we gave the script both the ShowProgress and RemoveContent switches, it showed the progress bar and after it had cleaned the 18 eligible updates, it started to remove the content for those updates.
As a result of a successful execution, the Software Update Group should now have a green icon instead of the previous grey:
If you have any feedback on this script, please let me know. I hope this helps!

Nickolaj Andersen

Chief Technical Architect and Enterprise Mobility MVP since 2016. Nickolaj has been in the IT industry for the past 10 years specializing in Enterprise Mobility and Security, Windows devices and deployments including automation. Awarded as PowerShell Hero in 2015 by the community for his script and tools contributions. Creator of ConfigMgr Prerequisites Tool, ConfigMgr OSD FrontEnd, ConfigMgr WebService to name a few. Frequent speaker at conferences such as Microsoft Ignite, NIC Conference and IT/Dev Connections including nordic user groups.


  • Hi Nickolaj,
    I am a fledgling admin and guys like you have made my life and learning curve seamless. Thank you for the script and my SUGs have never looked cleaner. Thanks a million

  • Thanks for this awesome post , When i tried running the script in elevated powershel it says that”You must provide a value expression on the right-hand side of the ‘-‘ operator.”
    I am using this line to run the script “.\Clean-CMSoftwareUpdateGroup.ps1 -SiteServer abc -SUGName “testpatches” -ShowProgress -RemoveContent -Verbose
    ” I have replaced the site server name and Software update name

  • Hello,
    I get the following error
    Unable to determine SiteCode
    At C:\scripts\Clean-CMSoftwareUpdateGroup.ps1:52 char:9
    + Throw “Unable to determine SiteCode”
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : OperationStopped: (Unable t
    + FullyQualifiedErrorId : Unable to determine SiteCod

      • Hi Nickolaj,
        .\Clean-CMSoftwareUpdateGroup.ps1 -SiteServer CAS01 -SUGName “Office 2013 Updates – Automatic Deployment 2015-12-07 19:29:35” -ShowProgress -RemoveContent -Verbose
        I am running it on Powershell as admin.

  • Nice Script , don’t get me wrong, but it’s missing the package cleaning up part and is not in my opinion Worth the effort, you can do better and faster from console:
    Go to
    1.All software updates,
    Then add
    2.criteria : Expired
    3.Criteria : superseeded
    4.clic search
    5. select them all
    6.edit membership and remove them from all software updates
    7.go now to your package ==> show members do 1,2, 3,4 and delete
    8. finally update your package
    you are done . 2 minutes if you ‘re slow .

  • I am very new at Windows Power Shell. I have ran the script verbatim sever times and I getting this error.
    .\Clean-CMSoftwareUpdateGroup.ps1 : The term ‘.\Clean-CMSoftwareUpdateGroup.ps1’ is not recognized as the name of a
    cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify
    that the path is correct and try again.
    At line:1 char:1
    + .\Clean-CMSoftwareUpdateGroup.ps1 -SiteServer 001 -SUGName “Critical and Securit …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : ObjectNotFound: (.\Clean-CMSoftwareUpdateGroup.ps1:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

    • Hi Anthony,
      If you’ve downloaded the script from the internet (doh), you may have to right-click on the script file in Windows Explorer, select Properties and at the bottom select to Unblock the file.

  • Your Shell Script worked like a champ!!
    But I have one question…I was told that you should never delete the expired/ superseded updates. Will this affect my ADRs?

  • I’m getting the same behaviour as Dillon James above, the script runs but ends with “WARNING: Unable to save changes to SUGname”.

  • I am running the script against SCCM 2012 R2 SP1 and it comes up with a warning – Unable to save changes. Looks like it cannot save the changes back to the named software update group.

  • I expanded on this script using the Configuration Manager module. Here’s a version that will run for all software update groups. I set this up as a scheduled task to run every night on my primary site server (a little aggressive, but now the SUGs are all green every day). Make sure to update your installation path, site code, and site server.
    If (Test-Path ‘E:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin’){
    Import-Module ‘E:\Program Files (x86)\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1’
    } ElseIf (Test-Path ‘E:\Program Files\Microsoft Configuration Manager\AdminConsole\bin’){
    Import-Module ‘E:\Program Files\Microsoft Configuration Manager\AdminConsole\bin\ConfigurationManager.psd1’
    } Else {
    Echo ‘Could not find Configuration Manager installation directory. Please install Configuration Manager and try again.’
    CD WV1:
    $SUGs = Get-CMSoftwareUpdateGroup -Name “*” | Select LocalizedDisplayName, CI_ID, ContainsExpiredUpdates, ContainsSupersededUpdates, Updates | Where-Object {$_.ContainsSupersededUpdates}
    Foreach($SUG in $SUGs){
    C:\Scripts\Clean-CMSoftwareUpdateGroup.ps1 -SiteServer vm-sccm -SUGName $SUG.LocalizedDisplayName -ShowProgress -RemoveContent -Verbose

  • I have successfully installed the console extension and it seems to run but it is not removing the expired updates. Instead I get:
    “No changes detected, will not update ‘….updates’ (migrated from software update deployment)” message. The expired update files remain with a red x

  • Nickolaj,
    We have many DPs within our environment located all around the world. Will this need to be ran on each DP to remove the content or just on the primary (we only have a primary, no secondary) and when completed cleaning up the update group will it kick off a content update (to remove) the old content from the remote DPs or will it delete it from each of them and redistribute the content?
    Please let me know.

  • Great work. A minor quibble. Can you add the -Requires statement to indicate what PowerShell version is required? It wouldn’t run for me on version 2. It gave me a generic PowerShell error about providing a value expression on the right hand side of the – operator and consumed time debugging why this script wouldn’t even hit the error trapping.

    • Hi Jon,
      It could be something that I might add in the future, but nearly all of my scripts that I share on this blog requires PowerShell 3.0. If you’re still running 2.0, I’d advise you to update (you’re most likely aware of that) 🙂

  • Hi-
    I am a newbie at Powershell and running scripts. I followed the instructions and got an error about the file not being digitally signed. What should I do?

    • Hi Fitzgerald David,
      Right-click on the downloaded zip-file and select Properties. Click on the Unblock button and extract the ps1 file. You may also have to do so for the ps1 file, I’m not sure about that.

  • Whne i running the script, I’m getting error message Unexpected token -site server any idea

    • Hi HK,
      Could you try to run the script manually with the parameters specified in the CleanSoftwareUpdateGroups.xml file? What output do you get from there?

  • Thanks for this script, another great help like the Adobe slipstream script. Is there a way to run this against all of my update groups at once or is there a reason why this wouldn’t be recommended?

  • Do I have to specifiy a SUGName? I would like it to look at the Software Update Groups and run through each of them.

  • Hi Nickolaj,
    Thanks for your great script! It has formed the basis for what I need done. It’s saved me time and effort and for that, I owe you copious amounts of beer! 😉
    One addition I’ve made is for the script to handle SUG names that contain [square brackets], as they do in this particular environment. For instance, take a group named “[TST] SU Group”. The script can’t find any groups in WMI based on that string, as the WMI filter needs the opening square bracket to be enclosed in square brackets itself. So the WMIfilter variable needs to be modified to read “[[]TST] SU Group”.
    Based on line 73 in your script: if ($SUGName -match “\*”)
    I’ve added an if-section to deal with the square bracket. as follows:
    if ($SUGName -match “\[“) {
    $WmiFilter = $WmiFilter.Replace(“[“,”[[]”)
    Note that I didn’t change the SUGname variable value, since it is passed literally to the filter parameter in the $AuthorizationList declaration on line 79.
    Also, I like how you check the number of SUGresults and stop the script if multiple groups are targeted. However, I did want to run the script for many groups at once. After all, what’s the use of automation if you can’t use in bulk, right?
    I’ve added the ValueFromPipeline attribute to the SUGname variable. Then, I enumerate the groups that have expired updates. (I don’t care about superseded updates at this stage)
    $ExpiredSUGs = Get-WmiObject -Namespace “root\SMS\site_$($SiteCode)” -Class SMS_AuthorizationList -ComputerName $SiteServer -Filter ‘ContainsExpiredUpdates = “true”‘ | select -ExpandProperty LocalizedDisplayName
    I then pipe this variable to the script, et voila! Thanks to your use of begin/process/end-blocks, it works perfectly 🙂
    $ExpiredSUGs | Clean-CMSoftwareUpdateGroup.ps1 -SiteServer servername [-WhatIf]
    I can send you my modified version of your script for reference, if you like.

    • Hi Merlin,
      Surething, if you send me the script I can upload it to this blog post so that other users can download it straight away. Would that be ok with you?
      Really great contributions on your end, this is what the community is all about.

      • Sure thing. Check your mail. I’ve made another few modifications in the mean time and still tweaking it to do more stuff I want it to do.
        Looking forward to collaborate on this further.
        Speak soon

  • I have just started working with SCCM – system was set up by someone who has left little documentation on how anything was set up. Do have 7 SUGs – only one of which has the grey cross Icon. Have tried to run this script bit all I get is Unable to locate a software update group named ‘xxxxxx’ have treble checked the SUG name., have copy/pasted the name from the properties of the group, have re-named a group – all with the same result. Created a new test group and the script ran fine on this group – but as it had only just been created there were no expired/superseded updates for it to find. Any Idea how to determine the “correct” SUG name?

  • Thanks for the script, I was manually reviewing all of our SUGs after each patch Tuesday, now just executing this. Great stuff!

    • Hi Alan,
      You’re welcome, I’m glad it’s working out for you. If you have any suggestions on improvements, I’d gladly hear about them.

  • Great script. This will save me many hours. The script ran great for me except for one little issue. After Successfully cleaned up updates from the group I get a zero objects found error. This appears to happen for every single update that was found during the script. Any help would be great. Error listed below. Thank you.
    Collecting content data for CI_ID: 16843649
    Found ‘0’ objects

    • Figured it out, I had an older group that I ran the script against. Those updates had already been removed.

      • Hi Dustin,
        Excuse me for not getting back to you on your first comment. It’s been a hectic time with the holidays etc. I’m glad that you where able to figure it out. I’d have said that you should check if those files were present on the DP, since that’s the the scripts is checking for.

  • Hi Nickolaj,
    Thank you for the script, it is working well on SCCM 2012 R2 CU2.
    If I was to get a list of all SUG’s and put them in a text file, how could i run your command against that file? I could then schedule this to run every 3 months as an extra clean up job. Hope you can help and thanks again.

  • I’m interested in running this script, however I still have some of my SUG as active deployment. A few articles that I have ran into states that you should remove the deployment of the SUG from your deployed collection and then do the cleanup.
    Can this script run during an active SUG? or will that break its current deployment state since its removing updates from the Software Update Package?
    Not pertinent to this discussion but if I add updates to a SUG while its deployed to collections, I’ve notice that on end users clients when its time to download/install the updates they will get stuck in 0% downloaded state. If I redistribute the package and restart SMS agent on those clients, things start to work again. Wasn’t sure if this emphasizes adding/removing updates from an active SUG….

    • Hi Dean,
      To my knowledge, you should be able to run this script on a SUG that has an enabled deployment. Since the script will only remove the expired and superseded updates from the SUG, it wont affect any updates that you’ve targeted to the clients since those will not get installed anyway (unless you’re dealing with superseded updates manually, it might be another situation).
      Regarding your situation of where the updates get stuck in 0% during the download phase, I’ve not seen that myself actually. What version are you running?

  • This looks amazing.
    Any chance this can be made to work on Windows Server 2008 R2? My SCCM 2012 SP1 server is running on 2008 R2 right now.

    • Hi Nick,
      I’ve not tested it, but I don’t see any reason for why it wouldn’t work. Give it a try and add the -WhatIf switch so that it won’t actually change anything. Keep us posted with the results 🙂

  • Hi,
    Thanks a lot for the script.
    Does delete the expired update files from the hard drive in order to save disk space?

    • Hi Rajesh,
      You’re welcome! As I wrote in the short documentation part of the blog post:
      “The second switch is called RemoveContent and when you specify that in your command, those Software Updates that will be removed from the specified Software Update Group, will also be removed from the Deployment Packages where they’ve been downloaded to.”
      So, yes if you specify the -RemoveContent switch to your command.

  • HI,
    Just ran this on a customer’s site who was having major issues with expired and out of date updates, ranging from Server 2003/XP to 8.1/2012R2, after running this script against all of his updates packages, we have managed to reduce the size consumed by little over 3rd and made the whole process for deployment more efficient. Brilliant work keep it up.

    • Hi Chris,
      Now that’s just outstanding, that was the whole idea with this script. I’m glad that it’s working that well. Thank you for the kind words!
      Best regards,

  • Used this today to clean up 15 Software Update Groups. 2012R2 CU2. Worked really well.
    Only thing I noticed is sometimes while running it appeared to pause. I would hit ctrl+c and it would take off again.
    Thanks again,


Categories use cookies to ensure that we give you the best experience on our website.