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.

Usage

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:

118_1

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

118_2

118_3

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:

118_4

If you have any feedback on this script, please let me know. I hope this helps!

Nickolaj Andersen
Principal Consultant and Enterprise Mobility MVP. Nickolaj has been in the IT industry for the past 10 years specializing in Enterprise Mobility and Security, Windows deployments and Automation. In 2015 Nickolaj was awarded as PowerShell Hero by the community for his script and tools contributions. Author of ConfigMgr Prerequisites Tool, ConfigMgr OSD FrontEnd, ConfigMgr WebService and a frequent speaker at user groups.

(2352)

comments
  • thestardawg
    Posted at 02:08 November 21, 2014
    thestardawg
    Reply
    Author

    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,

  • Christopher Field
    Posted at 10:42 December 3, 2014
    Christopher Field
    Reply
    Author

    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.
    Chris

    • Nickolaj
      Posted at 10:52 December 3, 2014
      Nickolaj
      Reply
      Author

      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,
      Nickolaj

  • Rajesh Bhuvanan
    Posted at 10:05 December 10, 2014
    Rajesh Bhuvanan
    Reply
    Author

    Hi,

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

    Regards
    Rajesh

    • Nickolaj
      Posted at 10:17 December 10, 2014
      Nickolaj
      Reply
      Author

      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.

      Regards,
      Nickolaj

  • Nick
    Posted at 00:22 December 11, 2014
    Nick
    Reply
    Author

    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.

    Thanks
    Nick

    • Nickolaj
      Posted at 08:27 December 11, 2014
      Nickolaj
      Reply
      Author

      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 🙂

      Regards,
      Nickolaj

  • Dean Buen
    Posted at 21:29 December 11, 2014
    Dean Buen
    Reply
    Author

    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….

    • Nickolaj
      Posted at 22:10 December 11, 2014
      Nickolaj
      Reply
      Author

      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?

      Regards,
      Nickolaj

  • Andrew Hurst
    Posted at 13:29 December 12, 2014
    Andrew Hurst
    Reply
    Author

    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.
    Andrew

  • Dustin Ogilvie
    Posted at 17:20 December 29, 2014
    Dustin Ogilvie
    Reply
    Author

    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

    • Dustin Ogilvie
      Posted at 17:17 January 2, 2015
      Dustin Ogilvie
      Reply
      Author

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

      • Nickolaj
        Posted at 16:24 January 4, 2015
        Nickolaj
        Reply
        Author

        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.

        Regards,
        Nickolaj

  • Alan Dooley
    Posted at 10:43 February 11, 2015
    Alan Dooley
    Reply
    Author

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

    • Nickolaj
      Posted at 10:51 February 12, 2015
      Nickolaj
      Reply
      Author

      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.

      Regards,
      Nickolaj

  • Euan Cooper
    Posted at 04:16 March 3, 2015
    Euan Cooper
    Reply
    Author

    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?

    -Ec

  • Merlin
    Posted at 14:38 March 10, 2015
    Merlin
    Reply
    Author

    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.

    • Nickolaj
      Posted at 12:44 March 11, 2015
      Nickolaj
      Reply
      Author

      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.

      Regards,
      Nickolaj

      • Merlin
        Posted at 11:57 March 12, 2015
        Merlin
        Reply
        Author

        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
        Merlin

  • James
    Posted at 20:31 March 11, 2015
    James
    Reply
    Author

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

  • Al
    Posted at 10:35 March 18, 2015
    Al
    Reply
    Author

    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?

    thanks,

    Al

  • hk
    Posted at 18:50 March 19, 2015
    hk
    Reply
    Author

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

    • Nickolaj
      Posted at 12:13 March 20, 2015
      Nickolaj
      Reply
      Author

      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?

      Regards,
      Nickolaj

  • Fitzgerald David
    Posted at 16:34 April 20, 2015
    Fitzgerald David
    Reply
    Author

    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?

    • Nickolaj
      Posted at 19:54 April 20, 2015
      Nickolaj
      Reply
      Author

      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.

      Regards,
      Nickolaj

  • Jon Bjerke
    Posted at 19:57 May 19, 2015
    Jon Bjerke
    Reply
    Author

    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.

    • Nickolaj
      Posted at 07:20 May 22, 2015
      Nickolaj
      Reply
      Author

      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) 🙂

      Regards,
      Nickolaj

  • DwightB
    Posted at 19:55 June 17, 2015
    DwightB
    Reply
    Author

    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.

    Thanks,
    DwightB.

  • Kadumata
    Posted at 19:56 July 30, 2015
    Kadumata
    Reply
    Author

    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

  • Adam
    Posted at 21:09 October 28, 2015
    Adam
    Reply
    Author

    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
    }

  • Dillon James
    Posted at 22:27 November 6, 2015
    Dillon James
    Reply
    Author

    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.

  • Craig
    Posted at 05:27 November 11, 2015
    Craig
    Reply
    Author

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

  • nicole
    Posted at 22:35 December 14, 2015
    nicole
    Reply
    Author

    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?
    Thanks!

  • Anthony
    Posted at 17:21 March 3, 2016
    Anthony
    Reply
    Author

    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

    • Nickolaj
      Posted at 10:30 March 4, 2016
      Nickolaj
      Reply
      Author

      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.

      Regards,
      Nickolaj

  • Guguianu Victor
    Posted at 17:35 March 16, 2016
    Guguianu Victor
    Reply
    Author

    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
    add
    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 .

    greetings

    • Nickolaj
      Posted at 17:42 March 16, 2016
      Nickolaj
      Reply
      Author

      It’s not for everyone 🙂

  • Jimmy
    Posted at 15:46 April 18, 2016
    Jimmy
    Reply
    Author

    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

    • Nickolaj
      Posted at 15:57 April 18, 2016
      Nickolaj
      Reply
      Author

      Hi Jimmy,

      What was the command line you used? Where are you running the script?

      Regards,
      Nickolaj

      • Jimmy
        Posted at 17:04 April 18, 2016
        Jimmy
        Reply
        Author

        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.

        Regards,

        Jimmy

  • Pushkar
    Posted at 20:01 November 10, 2016
    Pushkar
    Reply
    Author

    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

    • Nickolaj
      Posted at 08:59 November 25, 2016
      Nickolaj
      Reply
      Author

      You should use PowerShell version 3.0 or above.

      Regards,
      Nickolaj

  • Michael
    Posted at 15:44 February 3, 2017
    Michael
    Reply
    Author

    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

  • Leave a Reply