├── .markdownlint.json ├── .vscode ├── extensions.json └── settings.json ├── .editorconfig ├── LICENSE ├── PSScriptAnalyzerSettings.psd1 ├── CHANGELOG.md ├── PSWsusSpringClean.psd1 ├── README.md ├── PSWsusSpringClean.xml ├── PSWsusSpringClean.csv └── PSWsusSpringClean.psm1 /.markdownlint.json: -------------------------------------------------------------------------------- 1 | { 2 | "default": true, 3 | "heading-style": { "style": "setext_with_atx" }, 4 | "line-length": false 5 | } 6 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "davidanson.vscode-markdownlint", 4 | "editorconfig.editorconfig", 5 | "ms-vscode.powershell", 6 | "redhat.vscode-xml", 7 | "yzhang.markdown-all-in-one" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig 2 | # https://editorconfig.org 3 | 4 | # Don't search any further up the directory tree 5 | root = true 6 | 7 | # Baseline 8 | [*] 9 | charset = utf-8 10 | indent_style = space 11 | indent_size = 4 12 | trim_trailing_whitespace = true 13 | insert_final_newline = true 14 | 15 | # Markdown 16 | [*.md] 17 | trim_trailing_whitespace = false 18 | 19 | # XML 20 | [*.xml] 21 | insert_final_newline = false 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Samuel Leslie 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PSScriptAnalyzerSettings.psd1: -------------------------------------------------------------------------------- 1 | # PSScriptAnalyzer settings 2 | # 3 | # Last reviewed release: v1.24.0 4 | 5 | @{ 6 | IncludeRules = @('*') 7 | 8 | ExcludeRules = @( 9 | 'PSReviewUnusedParameter' 10 | ) 11 | 12 | Rules = @{ 13 | # Compatibility rules 14 | PSUseCompatibleSyntax = @{ 15 | Enable = $true 16 | # Only major versions from v3.0 are supported 17 | TargetVersions = @('3.0', '4.0', '5.0') 18 | } 19 | 20 | # General rules 21 | PSAlignAssignmentStatement = @{ 22 | Enable = $true 23 | CheckHashtable = $true 24 | } 25 | 26 | PSPlaceCloseBrace = @{ 27 | Enable = $true 28 | IgnoreOneLineBlock = $true 29 | NewLineAfter = $false 30 | NoEmptyLineBefore = $false 31 | } 32 | 33 | PSPlaceOpenBrace = @{ 34 | Enable = $true 35 | IgnoreOneLineBlock = $true 36 | NewLineAfter = $true 37 | OnSameLine = $true 38 | } 39 | 40 | PSProvideCommentHelp = @{ 41 | Enable = $true 42 | BlockComment = $true 43 | ExportedOnly = $true 44 | Placement = 'begin' 45 | VSCodeSnippetCorrection = $false 46 | } 47 | 48 | PSUseConsistentIndentation = @{ 49 | Enable = $true 50 | IndentationSize = 4 51 | Kind = 'space' 52 | PipelineIndentation = 'IncreaseIndentationForFirstPipeline' 53 | } 54 | 55 | PSUseConsistentWhitespace = @{ 56 | Enable = $true 57 | CheckInnerBrace = $true 58 | CheckOpenBrace = $true 59 | CheckOpenParen = $true 60 | CheckOperator = $true 61 | CheckParameter = $true 62 | CheckPipe = $true 63 | CheckPipeForRedundantWhitespace = $true 64 | CheckSeparator = $true 65 | IgnoreAssignmentOperatorInsideHashTable = $true 66 | } 67 | 68 | PSUseCorrectCasing = @{ 69 | Enable = $true 70 | CheckCommands = $true 71 | CheckKeyword = $false 72 | CheckOperator = $true 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | // Important to disable for EditorConfig to apply correctly 3 | // See: https://github.com/editorconfig/editorconfig-vscode/issues/153 4 | "files.trimTrailingWhitespace": false, 5 | 6 | // JSON code formatting 7 | "json.format.keepLines": true, 8 | 9 | // Markdown table of contents generation 10 | "markdown.extension.toc.levels": "2..2", 11 | "markdown.extension.toc.slugifyMode": "github", 12 | 13 | // PowerShell code formatting 14 | "powershell.codeFormatting.addWhitespaceAroundPipe": true, 15 | "powershell.codeFormatting.alignPropertyValuePairs": true, 16 | "powershell.codeFormatting.autoCorrectAliases": true, 17 | "powershell.codeFormatting.avoidSemicolonsAsLineTerminators": true, 18 | "powershell.codeFormatting.ignoreOneLineBlock": true, 19 | "powershell.codeFormatting.newLineAfterCloseBrace": false, // Stylistic preference 20 | "powershell.codeFormatting.newLineAfterOpenBrace": true, 21 | "powershell.codeFormatting.openBraceOnSameLine": true, 22 | "powershell.codeFormatting.pipelineIndentationStyle": "IncreaseIndentationForFirstPipeline", 23 | "powershell.codeFormatting.trimWhitespaceAroundPipe": true, 24 | "powershell.codeFormatting.useConstantStrings": true, 25 | "powershell.codeFormatting.useCorrectCasing": false, // Extremely opinionated and often wrong 26 | "powershell.codeFormatting.whitespaceAfterSeparator": true, 27 | "powershell.codeFormatting.whitespaceAroundOperator": true, 28 | "powershell.codeFormatting.whitespaceBeforeOpenBrace": true, 29 | "powershell.codeFormatting.whitespaceBeforeOpenParen": true, 30 | "powershell.codeFormatting.whitespaceBetweenParameters": true, 31 | "powershell.codeFormatting.whitespaceInsideBrace": true, 32 | 33 | // PowerShell script analysis 34 | "powershell.scriptAnalysis.settingsPath": "PSScriptAnalyzerSettings.psd1", 35 | 36 | // XML code formatting 37 | "xml.format.maxLineWidth": 0, 38 | 39 | "[json][jsonc]": { 40 | "editor.rulers": [79], 41 | 42 | "editor.defaultFormatter": "vscode.json-language-features" 43 | }, 44 | 45 | "[markdown]": { 46 | "editor.defaultFormatter": "yzhang.markdown-all-in-one", 47 | "editor.formatOnSave": true, 48 | 49 | "editor.codeActionsOnSave": { 50 | "source.fixAll.markdownlint": "explicit" 51 | }, 52 | 53 | // Trailing whitespace has special meaning 54 | "files.trimTrailingWhitespace": false 55 | }, 56 | 57 | "[powershell]": { 58 | "editor.rulers": [79], 59 | 60 | "editor.defaultFormatter": "ms-vscode.powershell", 61 | "editor.formatOnSave": true 62 | }, 63 | 64 | "[xml]": { 65 | "editor.rulers": [79], 66 | 67 | "editor.defaultFormatter": "redhat.vscode-xml" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | v0.6.2 5 | ------ 6 | 7 | - Major improvements to language updates detection 8 | - Remove obsolete updates from bundled catalogue 9 | - Performance improvements searching for suspect declines 10 | - Miscellaneous improvements to regex update filters 11 | - Minor code clean-up & developer tooling improvements 12 | 13 | v0.6.1 14 | ------ 15 | 16 | - Ensure `ValidateScript()` functions return `$true` on success 17 | 18 | v0.6.0 19 | ------ 20 | 21 | - Add `Write-Progress` support & remove most `Write-Host` calls 22 | 23 | v0.5.0 24 | ------ 25 | 26 | - Added `-UpdateServer` parameter to support remote servers 27 | - Ensure `Microsoft.UpdateServices.BaseApi` assembly is loaded 28 | - Apply code formatting 29 | 30 | v0.4.6 31 | ------ 32 | 33 | - Syntax fixes for older PowerShell versions 34 | - Performance optimisations around array use 35 | 36 | v0.4.5 37 | ------ 38 | 39 | - Remove unneeded files from published package 40 | - Minor documentation updates & miscellaneous fixes 41 | 42 | v0.4.4 43 | ------ 44 | 45 | - Added new `DeclineWindowsNextUpdates` parameter 46 | - Added & removed various updates to local catalogue 47 | 48 | v0.4.3 49 | ------ 50 | 51 | - Added 33 updates to the CSV 52 | 53 | v0.4.2 54 | ------ 55 | 56 | - Enabled Strict Mode set to version 2.0 (latest at time of writing) 57 | - Add PSScriptAnalyzer linting configuration 58 | 59 | v0.4.1 60 | ------ 61 | 62 | - Major bug fix in retrieval of selected architecture(s) metadata 63 | 64 | v0.4.0 65 | ------ 66 | 67 | - Added new `DeclineArchitectures` parameter & removed `DeclineItaniumUpdates` 68 | - Added new `DeclineLanguagesExclude` & `DeclineLanguagesInclude` parameters 69 | - Removed 3,597(!) updates from the CSV as now handled by RegEx matching 70 | - Major performance improvements, minor fixes & documentation updates 71 | 72 | v0.3.1 73 | ------ 74 | 75 | - Added 427 updates to the CSV (language updates) 76 | 77 | v0.3.0 78 | ------ 79 | 80 | - Added new `RunCommonTasks` parameter 81 | - Renamed `DeclineSecurityOnlyQualityUpdates` to `DeclineSecurityOnlyUpdates` 82 | - Removed superfluous `DeclineUnneededUpdates` parameter 83 | - Switch to using RegEx patterns to locate pre-release updates 84 | - Tweaks to existing RegEx patterns & other minor changes 85 | 86 | v0.2.0 87 | ------ 88 | 89 | - Added new `DeclinePrereleaseUpdates` parameter 90 | - Added new `DeclineSecurityOnlyQualityUpdates` parameter 91 | - Added 441 updates to the CSV (almost entirely language packs) 92 | - Added new attribute in catalogue to denote pre-release updates 93 | - Major clean-up of the code & substantial performance improvements 94 | 95 | v0.1.2 96 | ------ 97 | 98 | - Added 2,171(!) updates to the CSV (almost entirely language packs) 99 | 100 | v0.1.1 101 | ------ 102 | 103 | - Added 449 updates to the CSV (almost entirely language packs) 104 | - Fixed links to license & changelog in the module manifest 105 | 106 | v0.1 107 | ---- 108 | 109 | - Initial stable release 110 | -------------------------------------------------------------------------------- /PSWsusSpringClean.psd1: -------------------------------------------------------------------------------- 1 | # 2 | # Module manifest for module 'PSWsusSpringClean' 3 | # 4 | 5 | @{ 6 | 7 | # Script module or binary module file associated with this manifest. 8 | RootModule = 'PSWsusSpringClean.psm1' 9 | 10 | # Version number of this module. 11 | ModuleVersion = '0.6.2' 12 | 13 | # Supported PSEditions 14 | # CompatiblePSEditions = @() 15 | 16 | # ID used to uniquely identify this module 17 | GUID = '9d236d1d-cdd1-4b30-9d91-f7df7acdbf63' 18 | 19 | # Author of this module 20 | Author = 'Samuel Leslie' 21 | 22 | # Company or vendor of this module 23 | # CompanyName = '' 24 | 25 | # Copyright statement for this module 26 | Copyright = '(c) Samuel Leslie. All rights reserved.' 27 | 28 | # Description of the functionality provided by this module 29 | Description = 'Give your WSUS server a thorough spring cleaning' 30 | 31 | # Minimum version of the PowerShell engine required by this module 32 | PowerShellVersion = '3.0' 33 | 34 | # Name of the PowerShell host required by this module 35 | # PowerShellHostName = '' 36 | 37 | # Minimum version of the PowerShell host required by this module 38 | # PowerShellHostVersion = '' 39 | 40 | # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 41 | # DotNetFrameworkVersion = '' 42 | 43 | # Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. 44 | # ClrVersion = '' 45 | 46 | # Processor architecture (None, X86, Amd64) required by this module 47 | # ProcessorArchitecture = '' 48 | 49 | # Modules that must be imported into the global environment prior to importing this module 50 | RequiredModules = @('UpdateServices') 51 | 52 | # Assemblies that must be loaded prior to importing this module 53 | RequiredAssemblies = @('Microsoft.UpdateServices.BaseApi') 54 | 55 | # Script files (.ps1) that are run in the caller's environment prior to importing this module. 56 | # ScriptsToProcess = @() 57 | 58 | # Type files (.ps1xml) to be loaded when importing this module 59 | # TypesToProcess = @() 60 | 61 | # Format files (.ps1xml) to be loaded when importing this module 62 | # FormatsToProcess = @() 63 | 64 | # Modules to import as nested modules of the module specified in RootModule/ModuleToProcess 65 | # NestedModules = @() 66 | 67 | # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. 68 | FunctionsToExport = @('ConvertTo-WsusSpringCleanCatalogue', 'Invoke-WsusSpringClean', 'Test-WsusSpringCleanCatalogue') 69 | 70 | # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. 71 | CmdletsToExport = @() 72 | 73 | # Variables to export from this module 74 | VariablesToExport = '*' 75 | 76 | # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. 77 | AliasesToExport = @() 78 | 79 | # DSC resources to export from this module 80 | # DscResourcesToExport = @() 81 | 82 | # List of all modules packaged with this module 83 | # ModuleList = @() 84 | 85 | # List of all files packaged with this module 86 | # FileList = @() 87 | 88 | # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. 89 | PrivateData = @{ 90 | 91 | PSData = @{ 92 | 93 | # Tags applied to this module. These help with module discovery in online galleries. 94 | Tags = @( 95 | 'wsus' 96 | 'Windows', 97 | 'PSEdition_Desktop' 98 | ) 99 | 100 | # A URL to the license for this module. 101 | LicenseUri = 'https://github.com/ralish/PSWsusSpringClean/blob/stable/LICENSE' 102 | 103 | # A URL to the main website for this project. 104 | ProjectUri = 'https://github.com/ralish/PSWsusSpringClean' 105 | 106 | # A URL to an icon representing this module. 107 | # IconUri = '' 108 | 109 | # ReleaseNotes of this module 110 | ReleaseNotes = 'https://github.com/ralish/PSWsusSpringClean/blob/stable/CHANGELOG.md' 111 | 112 | # Prerelease string of this module 113 | # Prerelease = '' 114 | 115 | # Flag to indicate whether the module requires explicit user acceptance for install/update/save 116 | # RequireLicenseAcceptance = $false 117 | 118 | # External dependent modules of this module 119 | ExternalModuleDependencies = @('UpdateServices') 120 | 121 | } # End of PSData hashtable 122 | 123 | } # End of PrivateData hashtable 124 | 125 | # HelpInfo URI of this module 126 | # HelpInfoURI = '' 127 | 128 | # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. 129 | # DefaultCommandPrefix = '' 130 | 131 | } 132 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PSWsusSpringClean 2 | ================= 3 | 4 | [![pwsh ver](https://img.shields.io/powershellgallery/v/PSWsusSpringClean)](https://www.powershellgallery.com/packages/PSWsusSpringClean) 5 | [![pwsh dl](https://img.shields.io/powershellgallery/dt/PSWsusSpringClean)](https://www.powershellgallery.com/packages/PSWsusSpringClean) 6 | [![license](https://img.shields.io/github/license/ralish/PSWsusSpringClean)](https://choosealicense.com/licenses/mit/) 7 | 8 | A PowerShell module to assist with cleaning-up superfluous updates in Windows Server Update Services (WSUS). 9 | 10 | - [The problem](#the-problem) 11 | - [The solution](#the-solution) 12 | - [Requirements](#requirements) 13 | - [Installing](#installing) 14 | - [Sample usage](#sample-usage) 15 | - [License](#license) 16 | 17 | The problem 18 | ----------- 19 | 20 | For the cleanliness obsessed among us, maintaining a pristine WSUS catalogue of approved updates can be a tedious and time-consuming affair. While WSUS itself provides tools to help manage this process, via the graphical *Server Cleanup Wizard* and its PowerShell equivalent `Invoke-WsusServerCleanup`, these tools can only decline or delete updates which WSUS itself is already aware are obsolete via update metadata. 21 | 22 | Unfortunately, many updates are obsolete but lack the metadata to indicate as such, or are still current but may be unwanted (e.g. Itanium architecture updates). Manually maintaining an ever-increasing catalogue of updates while removing these unwanted updates rapidly becomes a timesink. 23 | 24 | The solution 25 | ------------ 26 | 27 | The `PSWsusSpringClean` module provides several additional options for cleaning your WSUS server: 28 | 29 | - Runs the default set of generally safe clean-up tasks (`-RunDefaultTasks`) 30 | This consists of all the `Invoke-WsusServerCleanup` tasks and all parameters of this cmdlet prefixed with `-Decline`. 31 | - Decline failover clustering updates (`-DeclineClusterUpdates`) 32 | Updates which only apply to **SQL Server 2000/2005** installations in a *failover clustering* configuration. 33 | - Decline farm server & deployment updates (`-DeclineFarmUpdates`) 34 | Updates which only apply to *Farm Server* products or product installations in a *farm-deployment* configuration. 35 | - Decline pre-release updates (`-DeclinePrereleaseUpdates`) 36 | Updates which only apply to pre-release products (e.g. release candidates). 37 | - Decline *Security Only Quality Updates* (`-DeclineSecurityOnlyUpdates`) 38 | Microsoft's new non-cumulative security only updates. The *Security Monthly Quality Rollups* contain everything in these updates and more. 39 | - Decline architecture specific updates (`-DeclineArchitectures`) 40 | Updates which are targeted to specific CPU architectures (e.g. Itanium or ARM64). 41 | - Decline language or region specific updates (`-DeclineLanguagesExclude` & `-DeclineLanguagesInclude`) 42 | Updates which are targeted to specific or regions (e.g. *Setswana* (`tn-ZA`)). 43 | - All parameters of `Invoke-WsusServerCleanup` for wrapping its functionality 44 | Consult the help of `Invoke-WsusServerCleanup` for a description of these tasks. 45 | 46 | Several additional parameters not related to declining updates are also provided: 47 | 48 | - Synchronise the WSUS server catalogue (`-SynchroniseServer`) 49 | A synchronisation will be performed before any requested clean-up actions. 50 | - Flag for review updates which may be incorrectly declined (`-FindSuspectDeclines`) 51 | Lists updates which may be incorrectly declined. See the [Suspect Declines](#suspect-declines) section for more details. 52 | 53 | ### Unneeded Updates 54 | 55 | There are many updates which are likely unwanted in WSUS installations but have no obvious indicator in the metadata which can be used to detect them. To handle these updates a CSV file of categorised potentially unneeded updates is included with this module and can be used to selectively decline listed updates based on their associated category. The CSV file can be easily imported into a spreadsheet application of your choice to review the provided categories and associated updates or make changes. 56 | 57 | Two parameters are provided to indicate to the module which unneeded updates should be declined: 58 | 59 | - Decline only the updates in the listed categories (`-DeclineCategoriesInclude`) \[**Default**\] 60 | An array of strings corresponding to the categories of unneeded updates to be declined. If an empty array is provided (default) then *no* updates listed in the CSV will be declined. 61 | - Decline all unneeded updates except those in the listed categories (`-DeclineCategoriesExclude`) 62 | An array of strings corresponding to the categories of unneeded updates to exclude from declining. If an empty array is provided then **all** updates listed in the CSV will be declined! 63 | 64 | The `-DeclineCategoriesExclude` parameter should be used with caution as it could easily decline updates you did not intend to! 65 | 66 | ### Suspect Declines 67 | 68 | The module also provides a function to identify updates which may have been inadvertently declined via the `-FindSuspectDeclines` parameter. This will identify all declined updates which meet all of the following criteria: 69 | 70 | - Are not superseded 71 | - Are not expired 72 | - Would not have been declined by this module based on the provided parameters 73 | 74 | This can also be used to assist in reverting declines that were unintentionally made via an earlier invocation of this module with incorrect parameters. 75 | 76 | Requirements 77 | ------------ 78 | 79 | - PowerShell 3.0 (or later) 80 | - `UpdateServices` module (included with WSUS) 81 | 82 | Installing 83 | ---------- 84 | 85 | ### PowerShellGet (included with PowerShell 5.0) 86 | 87 | The module is published to the [PowerShell Gallery](https://www.powershellgallery.com/packages/PSWsusSpringClean): 88 | 89 | ```posh 90 | Install-Module -Name PSWsusSpringClean 91 | ``` 92 | 93 | ### ZIP File 94 | 95 | Download the [ZIP file](https://github.com/ralish/PSWsusSpringClean/archive/stable.zip) of the latest release and unpack it to one of the following locations: 96 | 97 | - Current user: `C:\Users\\Documents\WindowsPowerShell\Modules\PSWsusSpringClean` 98 | - All users: `C:\Program Files\WindowsPowerShell\Modules\PSWsusSpringClean` 99 | 100 | ### Git Clone 101 | 102 | You can also clone the repository into one of the above locations if you'd like the ability to easily update it via Git. 103 | 104 | ### Did it work? 105 | 106 | You can check that PowerShell is able to locate the module by running the following at a PowerShell prompt: 107 | 108 | ```posh 109 | Get-Module PSWsusSpringClean -ListAvailable 110 | ``` 111 | 112 | Sample usage 113 | ------------ 114 | 115 | ```posh 116 | # Runs the default clean-up tasks & checks for declined updates that may not be intentional 117 | $SuspectDeclines = Invoke-WsusSpringClean -RunDefaultTasks -FindSuspectDeclines 118 | 119 | # Declines all unneeded updates in the "Region - US" & "Superseded" categories 120 | Invoke-WsusSpringClean -DeclineCategoriesInclude @('Region - US', 'Superseded') 121 | 122 | # Declines all language specific updates excluding those for English (Australia) 123 | Invoke-WsusSpringClean -DeclineLanguagesExclude @('en-AU') 124 | 125 | # Declines all architecture specific updates for ARM64 & IA64 (Itanium) systems 126 | Invoke-WsusSpringClean -DeclineArchitectures @('arm64', 'ia64') 127 | ``` 128 | 129 | License 130 | ------- 131 | 132 | All content is licensed under the terms of [The MIT License](LICENSE). 133 | -------------------------------------------------------------------------------- /PSWsusSpringClean.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /PSWsusSpringClean.csv: -------------------------------------------------------------------------------- 1 | Category,Title,Products 2 | Superseded,810649: Critical Update,Windows 2000 3 | Superseded,810833: Security Update (Windows 2000),Windows 2000 4 | Superseded,811630: Critical Update (Windows 2000),Windows 2000 5 | Superseded,814033: Critical Update,Windows 2000 6 | Superseded,"814078: Security Update (Microsoft Jscript version 5.5, Windows 2000)",Windows 2000 7 | Superseded,Critical Update for Office XP on Windows XP Service Pack 2 (KB885884),Windows XP 8 | Superseded,Critical Update for Windows XP (KB885523),Windows XP 9 | Superseded,Critical Update for Windows XP (KB885626),Windows XP 10 | Superseded,Critical Update for Windows XP (KB888162),Windows XP 11 | Superseded,Critical Update for Windows XP Media Center Edition 2004 (KB830786),Windows XP 12 | Superseded,Cumulative Security Update for Internet Explorer 5.01 Service Pack 4 (KB867282),Windows 2000 13 | Superseded,Cumulative Security Update for Internet Explorer 6 Service Pack 1 (KB867282),"Windows 2000, Windows XP" 14 | Superseded,Cumulative Security Update for Internet Explorer 6 Service Pack 1 (KB896688),"Windows XP, Windows 2000" 15 | Language Packs,Internet Explorer 8 for Windows XP with Language Interface Pack,Windows XP 16 | Language Packs,Internet Explorer 8 Language Packs for Windows Server 2008,Windows Server 2008 17 | Language Packs,Internet Explorer 8 Language Packs for Windows Server 2008 x64 Edition,Windows Server 2008 18 | Language Packs,Internet Explorer 8 Language Packs for Windows Vista,Windows Vista 19 | Language Packs,Internet Explorer 8 Language Packs for Windows Vista for x64-based Systems,Windows Vista 20 | Superseded,Microsoft .NET Framework 2.0 Security Update for Windows Vista (KB974468),Windows Vista 21 | Superseded,Microsoft .NET Framework 2.0 Security Update for Windows Vista for x64-based Systems (KB974468),Windows Vista 22 | Superseded,Microsoft .NET Framework 2.0 Service Pack 1 Security Update for Windows 2000 (KB971110),Windows 2000 23 | Superseded,Microsoft .NET Framework 2.0 Service Pack 1 Security Update for Windows Vista (KB974292),Windows Vista 24 | Superseded,Microsoft .NET Framework 2.0 Service Pack 1 Security Update for Windows Vista for x64-based Systems (KB974292),Windows Vista 25 | Superseded,Microsoft .NET Framework 2.0 Service Pack 2 Security Update for Windows Vista (KB974467),Windows Vista 26 | Superseded,Microsoft .NET Framework 2.0 Service Pack 2 Security Update for Windows Vista for x64-based Systems (KB974467),Windows Vista 27 | Superseded,Microsoft .NET Framework 3.5 Security Update for Windows Server 2003 and Windows XP x86 (KB982865),"Windows Server 2003, Windows XP, Windows Server 2003, Datacenter Edition" 28 | Superseded,Microsoft .NET Framework 3.5 SP1 Security Update for Windows Vista SP1 and Windows Server 2008 for x64-based Systems (KB979911),"Windows Server 2008, Windows Vista" 29 | Superseded,Microsoft .NET Framework 3.5 SP1 Security Update for Windows Vista SP1 and Windows Server 2008 x86 (KB979911),"Windows Server 2008, Windows Vista" 30 | Superseded,Microsoft .NET Framework 3.5 SP1 Update for Windows Vista SP1 and Windows Server 2008 for x64-based Systems (KB956250),"Windows Server 2008, Windows Vista" 31 | Superseded,Microsoft .NET Framework 3.5 SP1 Update for Windows Vista SP1 and Windows Server 2008 for x64-based Systems (KB982532),"Windows Server 2008, Windows Vista" 32 | Superseded,Microsoft .NET Framework 3.5 SP1 Update for Windows Vista SP1 and Windows Server 2008 for x64-based Systems (KB982535),"Windows Server 2008, Windows Vista" 33 | Superseded,Microsoft .NET Framework 3.5 SP1 Update for Windows Vista SP1 and Windows Server 2008 x86 (KB956250),"Windows Server 2008, Windows Vista" 34 | Superseded,Microsoft .NET Framework 3.5 SP1 Update for Windows Vista SP1 and Windows Server 2008 x86 (KB982532),"Windows Server 2008, Windows Vista" 35 | Superseded,Microsoft .NET Framework 3.5 SP1 Update for Windows Vista SP1 and Windows Server 2008 x86 (KB982535),"Windows Server 2008, Windows Vista" 36 | Superseded,"Microsoft .NET Framework 3.5, Windows Vista SP1, and Windows Server 2008 Security Update for x64-based Systems (KB979913)","Windows Server 2008, Windows Vista" 37 | Superseded,"Microsoft .NET Framework 3.5, Windows Vista SP1, and Windows Server 2008 Security Update x86 (KB979913)","Windows Server 2008, Windows Vista" 38 | Superseded,Microsoft .NET Framework 4.5.2 for Windows 8.1 (KB2934520),Windows 8.1 39 | Superseded,Microsoft .NET Framework 4.5.2 for Windows 8.1 and Windows Server 2012 R2 for x64-based Systems (KB2934520),"Windows Server 2012 R2, Windows 8.1" 40 | Language Packs,Microsoft .NET Framework 4.5.2 Language Packs (KB2901983),"Windows Server 2008, Windows 7, Windows Server 2008 R2, Windows Vista" 41 | Language Packs,Microsoft .NET Framework 4.5.2 Language Packs for Windows 8 (KB2938103),Windows 8 42 | Language Packs,Microsoft .NET Framework 4.5.2 Language Packs for Windows 8 and Windows Server 2012 for x64-based Systems (KB2938103),"Windows 8, Windows Server 2012" 43 | Language Packs,Microsoft .NET Framework 4.5.2 Language Packs for Windows 8.1 (KB2938104),Windows 8.1 44 | Language Packs,Microsoft .NET Framework 4.5.2 Language Packs for Windows 8.1 and Windows Server 2012 R2 for x64-based Systems (KB2938104),"Windows Server 2012 R2, Windows 8.1" 45 | Language Packs,Microsoft .NET Framework 4.5.2 Upgrade Language Packs (KB2901983),"Windows Server 2008, Windows 7, Windows Server 2008 R2, Windows Vista" 46 | Superseded,Microsoft .NET Framework 4.6.1 for Windows Server 2012 R2 for x64 (KB3102467),Windows Server 2012 R2 47 | Superseded,Microsoft .NET Framework 4.6.1 for Windows Server 2012 R2 for x64 (KB3102467),Windows Server 2012 R2 48 | Language Packs,Microsoft .NET Framework 4.6.1 Language Packs (KB3102433),Windows 7 49 | Language Packs,Microsoft .NET Framework 4.6.1 Language Packs for Windows Server 2012 R2 for x64 (KB3102521),Windows Server 2012 R2 50 | Language Packs,Microsoft .NET Framework 4.6.1 Upgrade Language Packs (KB3102433),Windows 7 51 | Superseded,Microsoft .NET Framework 4.7 for Windows 10 Version 1607 (KB3186568),Windows 10 52 | Superseded,Microsoft .NET Framework 4.7 for Windows 10 Version 1607 for x64 (KB3186568),Windows 10 53 | Superseded,Microsoft .NET Framework 4.7 for Windows 8.1 (KB3186539),Windows 8.1 54 | Superseded,Microsoft .NET Framework 4.7 for Windows 8.1 and Windows Server 2012 R2 for x64 (KB3186539),"Windows Server 2012 R2, Windows 8.1" 55 | Language Packs,Microsoft .NET Framework 4.7 Language Packs for Windows 10 Version 1607 (KB3186607),Windows 10 56 | Language Packs,Microsoft .NET Framework 4.7 Language Packs for Windows 10 Version 1607 for x64 (KB3186607),Windows 10 57 | Language Packs,Microsoft .NET Framework 4.7 Language Packs for Windows 8.1 (KB3186606),Windows 8.1 58 | Language Packs,Microsoft .NET Framework 4.7 Language Packs for Windows 8.1 and Windows Server 2012 R2 for x64 (KB3186606),"Windows 8.1, Windows Server 2012 R2" 59 | Superseded,Microsoft .NET Framework 4.7.1 for Windows 10 Version 1607 (KB4033393),Windows 10 60 | Superseded,Microsoft .NET Framework 4.7.1 for Windows 10 Version 1607 for x64 (KB4033393),Windows 10 61 | Superseded,Microsoft .NET Framework 4.7.1 for Windows 8.1 (KB4033369),Windows 8.1 62 | Superseded,Microsoft .NET Framework 4.7.1 for Windows 8.1 and Windows Server 2012 R2 for x64 (KB4033369),"Windows Server 2012 R2, Windows 8.1" 63 | Language Packs,Microsoft .NET Framework 4.7.1 Language Packs for Windows 10 Version 1607 (KB4033418),Windows 10 64 | Language Packs,Microsoft .NET Framework 4.7.1 Language Packs for Windows 10 Version 1607 for x64 (KB4033418),Windows 10 65 | Language Packs,Microsoft .NET Framework 4.7.1 Language Packs for Windows 8.1 (KB4033417),Windows 8.1 66 | Language Packs,Microsoft .NET Framework 4.7.1 Language Packs for Windows 8.1 and Windows Server 2012 R2 for x64 (KB4033417),"Windows Server 2012 R2, Windows 8.1" 67 | Language Packs,Microsoft .NET Framework 4.7.2 Language Packs for Windows 10 Version 1607 (KB4054535),Windows 10 68 | Language Packs,Microsoft .NET Framework 4.7.2 Language Packs for Windows 10 Version 1607 for x64 (KB4054535),Windows 10 69 | Language Packs,Microsoft .NET Framework 4.7.2 Language Packs for Windows 8.1 (KB4054534),Windows 8.1 70 | Language Packs,Microsoft .NET Framework 4.7.2 Language Packs for Windows 8.1 for x64 (KB4054534),Windows 8.1 71 | Language Packs,Microsoft .NET Framework 4.7.2 Language Packs for Windows Server 2012 R2 for x64 (KB4054534),Windows Server 2012 R2 72 | Region - GB,Microsoft Office Accounting 2008 UK Service Pack 1 (KB949426),Office 2007 73 | Region - US,Microsoft Office Accounting 2008 US Service Pack 1 (KB949426),Office 2007 74 | Region - GB/US,Microsoft Office Accounting 2009 Service Pack 3 (KB2006634),Office 2007 75 | Superseded,Microsoft SQL Server 2014 RTM Cumulative Update (CU) 14 KB3158271,Microsoft SQL Server 2014 76 | Superseded,Network Diagnostic Tool (KB914440),Windows XP 77 | Language Packs,Office 2003 Service Pack 3 (SP3) for Multilingual User Interface Pack,Office 2003 78 | Region - GB/US,Office Accounting 2007 Service Pack 1,Office 2007 79 | Language Packs,Office XP Input Method Editor Update: KB835727 for Multilingual User Interface Pack,Office 2002/XP 80 | Language Packs,Office XP Service Pack 3 for Multilingual User Interface Pack,Office 2002/XP 81 | Language Packs,Project 2002 Service Pack 1 for Multilingual User Interface Pack,Office 2002/XP 82 | Language Packs,Project 2003 Service Pack 3 (SP3) for Multilingual User Interface Pack,Office 2003 83 | Superseded,Q320206: Security Update,Windows 2000 84 | Superseded,Q326830: Security Update (Windows 2000),Windows 2000 85 | Superseded,Q329115: Security Update (Windows 2000),Windows 2000 86 | Superseded,Q329414: Security Update (MDAC 2.5),Windows 2000 87 | Superseded,Q329414: Security Update (MDAC 2.6),Windows 2000 88 | Superseded,Q329553: Critical Update (Windows 2000),Windows 2000 89 | Superseded,Q811114: Security Update (Windows 2000 with Service Pack 2 or Service Pack 3),Windows 2000 90 | Superseded,Q815487: Critical update for Windows XP Media Center Edition,Windows XP 91 | Language Packs,Remote Desktop Connection (Terminal Services Client 6.0) MUI Language Pack for Windows XP (KB925877),Windows XP 92 | Superseded,Security Update (326886),Windows 2000 93 | Superseded,Security Update for .NET Framework 3.5 on Windows Server 2003 and Windows XP x86 (KB2478656),"Windows Server 2003, Windows XP, Windows Server 2003, Datacenter Edition" 94 | Superseded,Security Update for .NET Framework 3.5 on Windows Server 2003 and Windows XP x86 (KB2530095),"Windows Server 2003, Windows XP, Windows Server 2003, Datacenter Edition" 95 | Superseded,Security Update for .NET Framework 3.5 on Windows Vista SP1 and Windows Server 2008 for x64-based Systems (KB983587),"Windows Server 2008, Windows Vista" 96 | Superseded,Security Update for .NET Framework 3.5 on Windows Vista SP1 and Windows Server 2008 x86 (KB983587),"Windows Server 2008, Windows Vista" 97 | Superseded,Security Update for .NET Framework 3.5 SP1 on Windows Vista SP1 and Windows Server 2008 for x64-based Systems (KB2478659),"Windows Server 2008, Windows Vista" 98 | Superseded,Security Update for .NET Framework 3.5 SP1 on Windows Vista SP1 and Windows Server 2008 for x64-based Systems (KB2518865),"Windows Server 2008, Windows Vista" 99 | Superseded,Security Update for .NET Framework 3.5 SP1 on Windows Vista SP1 and Windows Server 2008 x86 (KB2478659),"Windows Server 2008, Windows Vista" 100 | Superseded,Security Update for .NET Framework 3.5 SP1 on Windows Vista SP1 and Windows Server 2008 x86 (KB2518865),"Windows Server 2008, Windows Vista" 101 | Superseded,"Security Update for .NET Framework 3.5, Windows Vista SP1, and Windows Server 2008 for x64-based Systems (KB2478657)","Windows Server 2008, Windows Vista" 102 | Superseded,"Security Update for .NET Framework 3.5, Windows Vista SP1, and Windows Server 2008 for x64-based Systems (KB2518863)","Windows Server 2008, Windows Vista" 103 | Superseded,"Security Update for .NET Framework 3.5, Windows Vista SP1, and Windows Server 2008 x86 (KB2478657)","Windows Server 2008, Windows Vista" 104 | Superseded,"Security Update for .NET Framework 3.5, Windows Vista SP1, and Windows Server 2008 x86 (KB2518863)","Windows Server 2008, Windows Vista" 105 | Superseded,Security Update for DirectX 8 for Windows 2000 (KB971633),Windows 2000 106 | Superseded,Security Update for DirectX 8.0 (KB839643),Windows 2000 107 | Superseded,Security Update for DirectX 8.1 (KB839643),Windows 2000 108 | Superseded,Security Update for DirectX 9 for Windows XP (KB904706),Windows XP 109 | Superseded,Security Update for Exchange Server 2003 (KB894549),Exchange Server 2003 110 | Superseded,Security Update for Exchange Server 2003 Service Pack 1 (KB894549),Exchange Server 2003 111 | Superseded,Security Update for Exchange Server 2003 Service Pack 1 (KB931832) - Cluster,Exchange Server 2003 112 | Superseded,Security Update for Exchange Server 2003 Service Pack 1 (KB931832) - Non-cluster,Exchange Server 2003 113 | Superseded,Security Update For Exchange Server 2013 CU1 (KB2874216),Exchange Server 2013 114 | Superseded,Security Update For Exchange Server 2013 CU10 (KB3124557),Exchange Server 2013 115 | Superseded,Security Update For Exchange Server 2013 CU11 (KB3150501),Exchange Server 2013 116 | Superseded,Security Update For Exchange Server 2013 CU12 (KB3184736),Exchange Server 2013 117 | Superseded,Security Update For Exchange Server 2013 CU2 (KB2880833),Exchange Server 2013 118 | Superseded,Security Update For Exchange Server 2013 CU3 (KB2880833),Exchange Server 2013 119 | Superseded,Security Update For Exchange Server 2013 CU6 (KB3011140),Exchange Server 2013 120 | Superseded,Security Update For Exchange Server 2013 CU7 (KB3040856),Exchange Server 2013 121 | Superseded,Security Update For Exchange Server 2013 CU8 (KB3087126),Exchange Server 2013 122 | Superseded,Security Update For Exchange Server 2013 CU9 (KB3087126),Exchange Server 2013 123 | Superseded,Security Update For Exchange Server 2016 CU1 (KB3184736),Exchange Server 2016 124 | Superseded,Security Update for Internet Explorer 6 Service Pack 1 (KB925486),Windows XP 125 | Superseded,Security Update for Microsoft .NET Framework 2.0 (KB947746),Windows 2000 126 | Superseded,Security Update for Microsoft .NET Framework 2.0 Service Pack 1 (KB972591),Windows Vista 127 | Superseded,Security Update for Microsoft .NET Framework 2.0 Service Pack 2 (KB972592),Windows Vista 128 | Superseded,Security Update for Microsoft .NET Framework 3.5 on Windows Server 2003 and Windows XP x86 (KB2416468),"Windows Server 2003, Windows XP, Windows Server 2003, Datacenter Edition" 129 | Superseded,"Security Update for Microsoft .NET Framework 3.5 on Windows XP, Windows Server 2003, Windows Vista, Windows Server 2008 for x64-based Systems (KB2418240)","Windows Server 2003, Windows Server 2003, Datacenter Edition, Windows XP x64 Edition, Windows Server 2008, Windows Vista" 130 | Superseded,"Security Update for Microsoft .NET Framework 3.5 on Windows XP, Windows Server 2003, Windows Vista, Windows Server 2008 x86 (KB2418240)","Windows Server 2003, Windows XP, Windows Server 2003, Datacenter Edition, Windows Server 2008, Windows Vista" 131 | Superseded,Security Update for Microsoft .NET Framework 3.5 SP1 on Windows Vista SP1 and Windows Server 2008 for x64-based Systems (KB2416474),"Windows Server 2008, Windows Vista" 132 | Superseded,Security Update for Microsoft .NET Framework 3.5 SP1 on Windows Vista SP1 and Windows Server 2008 x86 (KB2416474),"Windows Server 2008, Windows Vista" 133 | Superseded,"Security Update for Microsoft .NET Framework 3.5, Windows Vista SP1, and Windows Server 2008 for x64-based Systems (KB2416469)","Windows Server 2008, Windows Vista" 134 | Superseded,"Security Update for Microsoft .NET Framework 3.5, Windows Vista SP1, and Windows Server 2008 x86 (KB2416469)","Windows Server 2008, Windows Vista" 135 | Superseded,Security Update for Microsoft .NET Framework 3.5.1 on Windows 7 and Windows Server 2008 R2 for x64-based Systems (KB2604114),"Windows 7, Windows Server 2008 R2" 136 | Superseded,Security Update for Microsoft .NET Framework 3.5.1 on Windows 7 and Windows Server 2008 R2 for x64-based Systems (KB2656355),"Windows 7, Windows Server 2008 R2" 137 | Superseded,Security Update for Microsoft .NET Framework 3.5.1 on Windows 7 and Windows Server 2008 R2 for x64-based Systems (KB2656410),"Windows 7, Windows Server 2008 R2" 138 | Superseded,Security Update for Microsoft .NET Framework 3.5.1 on Windows 7 and Windows Server 2008 R2 for x64-based Systems (KB2729451),"Windows 7, Windows Server 2008 R2" 139 | Superseded,Security Update for Microsoft .NET Framework 3.5.1 on Windows 7 and Windows Server 2008 R2 for x64-based Systems (KB2736418),"Windows 7, Windows Server 2008 R2" 140 | Superseded,Security Update for Microsoft .NET Framework 3.5.1 on Windows 7 and Windows Server 2008 R2 for x64-based Systems (KB2742598),"Windows 7, Windows Server 2008 R2" 141 | Superseded,Security Update for Microsoft .NET Framework 3.5.1 on Windows 7 and Windows Server 2008 R2 for x64-based Systems (KB2756920),"Windows 7, Windows Server 2008 R2" 142 | Superseded,Security Update for Microsoft .NET Framework 3.5.1 on Windows 7 and Windows Server 2008 R2 for x64-based Systems (KB2789644),"Windows 7, Windows Server 2008 R2, Windows Embedded Standard 7" 143 | Superseded,Security Update for Microsoft .NET Framework 3.5.1 on Windows 7 x86 (KB2604114),Windows 7 144 | Superseded,Security Update for Microsoft .NET Framework 3.5.1 on Windows 7 x86 (KB2656355),Windows 7 145 | Superseded,Security Update for Microsoft .NET Framework 3.5.1 on Windows 7 x86 (KB2656410),Windows 7 146 | Superseded,Security Update for Microsoft .NET Framework 3.5.1 on Windows 7 x86 (KB2729451),Windows 7 147 | Superseded,Security Update for Microsoft .NET Framework 3.5.1 on Windows 7 x86 (KB2736418),Windows 7 148 | Superseded,Security Update for Microsoft .NET Framework 3.5.1 on Windows 7 x86 (KB2742598),Windows 7 149 | Superseded,Security Update for Microsoft .NET Framework 3.5.1 on Windows 7 x86 (KB2756920),Windows 7 150 | Superseded,Security Update for Microsoft .NET Framework 3.5.1 on Windows 7 x86 (KB2789644),"Windows 7, Windows Embedded Standard 7" 151 | Superseded,Security Update for Microsoft .NET Framework 4 on Windows Vista SP2 and Windows Server 2008 SP2 x64 (KB3099866),"Windows Server 2008, Windows Vista" 152 | Superseded,Security Update for Microsoft .NET Framework 4.5 on Vista and Server 2008 for x64 (KB2861193),"Windows Server 2008, Windows Vista" 153 | Superseded,Security Update for Microsoft .NET Framework 4.5 on Windows 8 (KB2737084),Windows 8 154 | Superseded,Security Update for Microsoft .NET Framework 4.5 on Windows 8 (KB2840632),Windows 8 155 | Superseded,Security Update for Microsoft .NET Framework 4.5 on Windows 8 and Windows Server 2012 for x64 (KB2742614),"Windows 8, Windows Server 2012" 156 | Superseded,Security Update for Microsoft .NET Framework 4.5 on Windows 8 and Windows Server 2012 for x64-based Systems (KB2737084),"Windows 8, Windows Server 2012" 157 | Superseded,Security Update for Microsoft .NET Framework 4.5 on Windows 8 and Windows Server 2012 for x64-based Systems (KB2840632),"Windows 8, Windows Server 2012" 158 | Superseded,Security Update for Microsoft .NET Framework 4.5 on Windows 8 and Windows Server 2012 for x64-based Systems (KB2861702),"Windows 8, Windows Server 2012" 159 | Superseded,Security Update for Microsoft .NET Framework 4.5 on Windows 8 x86 (KB2742614),Windows 8 160 | Superseded,Security Update for Microsoft .NET Framework 4.5 on Windows 8 x86 (KB2861702),Windows 8 161 | Superseded,Security Update for Microsoft .NET Framework 4.5 on Windows Vista and Windows Server 2008 x86 (KB2861193),"Windows Server 2008, Windows Vista" 162 | Superseded,Security Update for Microsoft .NET Framework 4.5.1 and 4.5.2 on Windows 8.1 (KB3074228),Windows 8.1 163 | Superseded,Security Update for Microsoft .NET Framework 4.5.1 and 4.5.2 on Windows 8.1 and Windows Server 2012 R2 x64-based Systems (KB3074228),"Windows 8.1, Windows Server 2012 R2" 164 | Superseded,Security Update for Microsoft .NET Framework 4.5.1 on Windows 8 (KB2898870),Windows 8 165 | Superseded,Security Update for Microsoft .NET Framework 4.5.1 on Windows 8 and Windows Server 2012 for x64-based Systems (KB2898870),"Windows 8, Windows Server 2012" 166 | Superseded,Security Update for Microsoft .NET Framework 4.5.1 on Windows 8.1 (KB2894856),Windows 8.1 167 | Superseded,Security Update for Microsoft .NET Framework 4.5.1 on Windows 8.1 and Windows Server 2012 R2 for x64-based Systems (KB2894856),"Windows 8.1, Windows Server 2012 R2" 168 | Superseded,Security Update for Microsoft .NET Framework 4.6 on Windows 8.1 (KB3098000),Windows 8.1 169 | Superseded,Security Update for Microsoft .NET Framework 4.6 on Windows 8.1 and Windows Server 2012 R2 x64-based Systems (KB3098000),"Windows 8.1, Windows Server 2012 R2" 170 | Superseded,"Security Update for Microsoft .NET Framework, Version 2.0 (KB929916)",Windows Vista 171 | Superseded,Security Update for Microsoft Visual C++ 2010 Redistributable Package (KB2467173),Visual Studio 2010 172 | Superseded,Security Update for Microsoft Visual Studio 2005 (KB925674),Visual Studio 2005 173 | Superseded,Security Update for Microsoft Visual Studio 2005 (KB937060),Visual Studio 2005 174 | Superseded,Security Update for Microsoft Visual Studio 2008 (KB971091),Visual Studio 2008 175 | Superseded,Security Update for Microsoft Visual Studio 2008 (KB972221),Visual Studio 2008 176 | Superseded,Security Update for Microsoft Visual Studio 2008 ATL for Smart Devices (KB973674),Visual Studio 2008 177 | Superseded,Security Update for Microsoft Visual Studio 2010 (KB2542054),Visual Studio 2010 178 | Superseded,Security Update for Microsoft Visual Studio 2010 (KB2644980),Visual Studio 2010 179 | Language Packs,Security Update for Office XP Multilingual User Interface Pack (KB905649),Office 2002/XP 180 | Superseded,Security Update for Report Viewer Redistributable 2008 (KB971118),Report Viewer 2008 181 | Superseded,Security Update for SQL Server 2008 R2 (KB2494086),SQL Server 2008 R2 182 | Superseded,Security Update for SQL Server 2008 R2 (KB2494088),SQL Server 2008 R2 183 | Superseded,Security Update for SQL Server 2008 R2 Service Pack 1 (KB2716439),SQL Server 2008 R2 184 | Superseded,Security Update for SQL Server 2008 R2 Service Pack 1 (KB2716440),SQL Server 2008 R2 185 | Superseded,Security Update for SQL Server 2008 R2 Service Pack 2 (KB3045312),SQL Server 2008 R2 186 | Superseded,Security Update for SQL Server 2008 R2 Service Pack 2 (KB3045313),SQL Server 2008 R2 187 | Superseded,Security Update for SQL Server 2008 Service Pack 2 (KB2716433),SQL Server 2008 188 | Superseded,Security Update for SQL Server 2008 Service Pack 2 (KB2716434),SQL Server 2008 189 | Superseded,Security Update for SQL Server 2008 Service Pack 3 (KB3045303),SQL Server 2008 190 | Superseded,Security Update for SQL Server 2008 Service Pack 3 (KB3045305),SQL Server 2008 191 | Superseded,Security Update for SQL Server 2012 RTM (KB2716441),Microsoft SQL Server 2012 192 | Superseded,Security Update for SQL Server 2012 RTM (KB2716442),Microsoft SQL Server 2012 193 | Superseded,Security Update for SQL Server 2012 Service Pack 1 (KB3045317),Microsoft SQL Server 2012 194 | Superseded,Security Update for SQL Server 2012 Service Pack 1 (KB3045318),Microsoft SQL Server 2012 195 | Superseded,Security Update for SQL Server 2012 Service Pack 2 (KB3045319),Microsoft SQL Server 2012 196 | Superseded,Security Update for SQL Server 2012 Service Pack 2 (KB3045321),Microsoft SQL Server 2012 197 | Superseded,Security Update for SQL Server 2012 Service Pack 2 GDR (KB3194719),Microsoft SQL Server 2012 198 | Superseded,Security Update for SQL Server 2012 Service Pack 3 CU (KB4057121),Microsoft SQL Server 2012 199 | Superseded,Security Update for SQL Server 2012 Service Pack 3 GDR (KB4057115),Microsoft SQL Server 2012 200 | Superseded,Security Update for SQL Server 2014 (KB3045323),Microsoft SQL Server 2014 201 | Superseded,Security Update for SQL Server 2014 (KB3045324),Microsoft SQL Server 2014 202 | Superseded,Security Update for SQL Server 2014 Service Pack 1 CU (KB4032542),Microsoft SQL Server 2014 203 | Superseded,Security Update for SQL Server 2014 Service Pack 1 GDR (KB4019091),Microsoft SQL Server 2014 204 | Superseded,Security Update for SQL Server 2016 RTM CU (KB4058559),Microsoft SQL Server 2016 205 | Superseded,Security Update for SQL Server 2016 RTM GDR (KB4058560),Microsoft SQL Server 2016 206 | Superseded,Security Update for Windows 2000 (329834),Windows 2000 207 | Superseded,Security Update for Windows 2000 (819696),Windows 2000 208 | Superseded,Security Update for Windows 8.1 (KB2961899) without KB2919355,Windows 8.1 209 | Superseded,Security Update for Windows 8.1 (KB2973906) without KB2919355,Windows 8.1 210 | Superseded,Security Update for Windows 8.1 for x64-based Systems (KB2961899) without KB2919355,Windows 8.1 211 | Superseded,Security Update for Windows 8.1 for x64-based Systems (KB2973906) without KB2919355,Windows 8.1 212 | Superseded,"Security Update for Windows Media Format Runtime 9, 9.5 & 11 for Windows XP SP 2 (KB954155)",Windows XP 213 | Superseded,"Security Update for Windows Media Format Runtime 9, 9.5 & 11 for Windows XP SP2 (KB978695)",Windows XP 214 | Superseded,Security Update for Windows Media Player 9 for Windows XP SP2 (KB979402),Windows XP 215 | Superseded,Security Update for Windows Media Services (KB832359),Windows 2000 216 | Superseded,Security Update for Windows Messenger (KB887472),Windows XP 217 | Superseded,Security Update for Windows Server 2012 R2 (KB2961899) without KB2919355,Windows Server 2012 R2 218 | Superseded,Security Update for Windows Server 2012 R2 (KB2973906) without KB2919355,Windows Server 2012 R2 219 | Superseded,Security Update for Windows Vista Service Pack 1 (KB979688),Windows Vista 220 | Superseded,Security Update for Windows Vista Service Pack 1 for x64-based Systems (KB979688),Windows Vista 221 | Superseded,Security Update for Windows XP Service Pack 2 (KB952069),Windows XP 222 | Superseded,Security Update for Windows XP Service Pack 2 (KB972187),Windows XP 223 | Superseded,Security Update for Windows XP Service Pack 2 (KB973540),Windows XP 224 | Superseded,"Security Update, February 13, 2002 (MSXML 4.0)",Windows XP 225 | Superseded,"Security Update, February 14, 2002 (Internet Explorer 5.01)",Windows 2000 226 | Language Packs,Service Pack 1 for Microsoft Office 2013 Language Interface Pack (KB2817428) 32-Bit Edition,Office 2013 227 | Language Packs,Service Pack 1 for Microsoft Office 2013 Language Interface Pack (KB2817428) 64-Bit Edition,Office 2013 228 | Language Packs,Service Pack 1 for Microsoft Office ScreenTip Language 2013 (KB2817436) 32-Bit Edition,Office 2013 229 | Language Packs,Service Pack 1 for Microsoft Office ScreenTip Language 2013 (KB2817436) 64-Bit Edition,Office 2013 230 | Language Packs,Service Pack 2 for Microsoft Office 2010 Language Pack (KB2687449) 32-Bit Edition,Office 2010 231 | Language Packs,Service Pack 2 for Microsoft Office 2010 Language Pack (KB2687449) 64-Bit Edition,Office 2010 232 | Language Packs,Service Pack 2 for Microsoft Office Language Interface Pack 2010 (KB2687450) 32-Bit Edition,Office 2010 233 | Language Packs,Service Pack 2 for Microsoft Office Language Interface Pack 2010 (KB2687450) 64-Bit Edition,Office 2010 234 | Superseded,SQL Server 2005 Books Online (September 2007),SQL Server Feature Pack 235 | Superseded,SQL Server 2008 Books Online (August 2008),SQL Server Feature Pack 236 | Superseded,SQL Server 2012 Service Pack 1 Setup Update (KB2674319),SQL Server 2012 Product Updates for Setup 237 | Superseded,SQL Server 2012 Service Pack 2 Cumulative Update (CU) 15 KB3205416,Microsoft SQL Server 2012 238 | Superseded,SQL Server 2012 Service Pack 2 Cumulative Update (CU) 16 KB3205054,Microsoft SQL Server 2012 239 | Superseded,SQL Server 2014 Service Pack 1 Cumulative Update (CU) 13 KB4019099,Microsoft SQL Server 2014 240 | Superseded,Update for Exchange Server 2003 (KB888619),Exchange Server 2003 241 | Superseded,Update for Exchange Server 2003 Service Pack 1 (KB931978) - Cluster,Exchange Server 2003 242 | Superseded,Update for Exchange Server 2003 Service Pack 1 (KB931978) - Non-cluster,Exchange Server 2003 243 | Superseded,Update for Internet Explorer 6 for Windows XP (KB946627),Windows XP 244 | Language Packs,Update for LIP SP2 MUI Resource Loading (KB913808),Windows XP 245 | Superseded,Update for Microsoft .NET Framework 3.0 (KB932394),"Windows Server 2003, Windows Server 2003, Datacenter Edition, Windows XP, Windows XP x64 Edition" 246 | Language Packs,Update for Microsoft .NET Framework 4.5 Language Packs for x64-based Systems (KB2770446),"Windows Server 2008, Windows 7, Windows Server 2008 R2, Windows Vista" 247 | Language Packs,Update for Microsoft .NET Framework 4.5 Language Packs x86 (KB2770446),"Windows Server 2008, Windows 7, Windows Vista" 248 | Region - GB/US,Update for Microsoft Office Accounting 2007 (KB946690),Office 2007 249 | Superseded,Update for Microsoft Office Outlook 2007 Junk Email Filter (KB2825642),Office 2007 250 | Superseded,Update for Microsoft Office Outlook 2007 Junk Email Filter (KB2880505),Office 2007 251 | Superseded,Update for Microsoft SkyDrive Pro (KB2837652) 64-Bit Edition,Office 2013 252 | Superseded,Update for Microsoft Visual Studio 2012 (KB2781514),Visual Studio 2012 253 | Superseded,Update for Microsoft Visual Studio 2013 Update 1 (KB2932965),Visual Studio 2013 254 | Superseded,Update for Microsoft Visual Studio Team Foundation Server 2013 (KB2989279),Visual Studio 2013 255 | Superseded,Update for Office SharePoint Foundation 2010 (KB2553014),Office 2010 256 | Superseded,Update Rollup 10 for Exchange Server 2007 Service Pack 1 (KB981407),Exchange Server 2007 257 | Superseded,Update Rollup 5 for Exchange Server 2007 Service Pack 2 (KB2407132),Exchange Server 2007 258 | Superseded,Update Rollup 5 for Exchange Server 2010 (KB2407113),Exchange Server 2010 259 | Superseded,Update Rollup 7 for Exchange Server 2007 (KB953469),Exchange Server 2007 260 | Superseded,Update Rollup 7-v2 for Exchange Server 2010 Service Pack 1 (KB2756496),Exchange Server 2010 261 | Superseded,Update Rollup 8 for Exchange Server 2010 Service Pack 1 (KB2787763),Exchange Server 2010 262 | Superseded,Update Rollup 8 for Exchange Server 2010 Service Pack 2 (KB2903903),Exchange Server 2010 263 | Superseded,Update Rollup 8-v2 for Exchange Server 2007 Service Pack 3 (KB2756497),Exchange Server 2007 264 | Language Packs,Visio 2003 Service Pack 3 (SP3) for Multilingual User Interface Pack,Office 2003 265 | Language Packs,Windows Internet Explorer 7 MUI Pack for Windows Server 2003 (x64) and Windows XP 64-bit Edition Version 2003,"Windows XP x64 Edition, Windows Server 2003, Datacenter Edition, Windows Server 2003" 266 | Language Packs,Windows Internet Explorer 7 MUI Pack for Windows XP and Windows Server 2003 (x86),"Windows Server 2003, Windows XP" 267 | Language Packs,Windows Internet Explorer 9 Language Pack for Windows 7,Windows 7 268 | Language Packs,Windows Internet Explorer 9 Language Pack for Windows 7 for x64-based Systems,Windows 7 269 | Language Packs,Windows Internet Explorer 9 Language Pack for Windows Server 2008,Windows Server 2008 270 | Language Packs,Windows Internet Explorer 9 Language Pack for Windows Server 2008 for x64-based systems,Windows Server 2008 271 | Language Packs,Windows Internet Explorer 9 Language Pack for Windows Server 2008 R2 for x64-based Systems,Windows Server 2008 R2 272 | Language Packs,Windows Internet Explorer 9 Language Pack for Windows Vista,Windows Vista 273 | Language Packs,Windows Internet Explorer 9 Language Pack for Windows Vista for x64-based systems,Windows Vista 274 | Superseded,Windows Malicious Software Removal Tool x64 - January 2008 (KB890830),Windows Vista 275 | Service Packs - All Languages,Windows Server 2008 Service Pack 2 Standalone (KB948465) - All Languages,Windows Server 2008 276 | Service Packs - All Languages,Windows Server 2008 Service Pack 2 Standalone x64-based Systems (KB948465) - All Languages,Windows Server 2008 277 | Service Packs - All Languages,Windows Vista Service Pack 1 Standalone (KB936330) - All Languages,Windows Vista 278 | Service Packs - All Languages,Windows Vista Service Pack 1 Standalone for x64-based Systems (KB936330) - All Languages,Windows Vista 279 | Service Packs - All Languages,Windows Vista Service Pack 2 Standalone (KB948465) - All Languages,Windows Vista 280 | Service Packs - All Languages,Windows Vista Service Pack 2 Standalone for x64-based Systems (KB948465) - All Languages,Windows Vista 281 | -------------------------------------------------------------------------------- /PSWsusSpringClean.psm1: -------------------------------------------------------------------------------- 1 | # See the help for Set-StrictMode for what this enables 2 | Set-StrictMode -Version 3.0 3 | 4 | # Regular expressions for declining certain types of updates 5 | $RegExClusterUpdates = '\bFailover Clustering\b' 6 | $RegExFarmUpdates = '\bfarm-deployment\b' 7 | $RegExPrereleaseUpdates = '\b(Beta|Pre-release|Preview|RC1|Release Candidate)\b' 8 | $RegExSecurityOnlyUpdates = '\bSecurity Only (Quality )?Update\b' 9 | $RegExWindowsNextUpdates = '\b(Server|Version) Next\b' 10 | 11 | # Match updates which appear to be language related 12 | $RegExLanguageUpdates = '(Language( Interface)?|LIP) Pack|_LI?P' 13 | # Matches BCP 47 language ranges (RFC 5646) 14 | $RegExBcp47 = '\b[a-z]{2,3}(-[a-z]{2,4})?-[a-z]{2,}\b' 15 | 16 | Function Invoke-WsusSpringClean { 17 | <# 18 | .SYNOPSIS 19 | Performs additional WSUS server clean-up beyond the capabilities of the built-in tools 20 | 21 | .DESCRIPTION 22 | Adds the ability to decline numerous additional commonly unneeded updates as well as discover potentially incorrectly declined updates. 23 | 24 | .PARAMETER CleanupObsoleteComputers 25 | Specifies that the cmdlet deletes obsolete computers from the database. 26 | 27 | .PARAMETER CleanupObsoleteUpdates 28 | Specifies that the cmdlet deletes obsolete updates from the database. 29 | 30 | .PARAMETER CleanupUnneededContentFiles 31 | Specifies that the cmdlet deletes unneeded update files. 32 | 33 | .PARAMETER CompressUpdates 34 | Specifies that the cmdlet deletes obsolete revisions to updates from the database. 35 | 36 | .PARAMETER DeclineArchitectures 37 | Array of update architectures to decline. 38 | 39 | Valid options are: x64, ia64, arm64 40 | 41 | We don't support declining x86 updates as there's no mechanism to determine which updates are x86 specific versus multi-architecture. 42 | 43 | .PARAMETER DeclineCategoriesExclude 44 | Array of update categories in the bundled updates catalogue to not decline. 45 | 46 | .PARAMETER DeclineCategoriesInclude 47 | Array of update categories in the bundled updates catalogue to decline. 48 | 49 | .PARAMETER DeclineClusterUpdates 50 | Decline any updates which are exclusively for failover clustering installations. 51 | 52 | .PARAMETER DeclineExpiredUpdates 53 | Specifies that the cmdlet declines expired updates. 54 | 55 | .PARAMETER DeclineFarmUpdates 56 | Decline any updates which are exclusively for farm deployment installations. 57 | 58 | .PARAMETER DeclineLanguagesExclude 59 | Array of update language codes to not decline. 60 | 61 | .PARAMETER DeclineLanguagesInclude 62 | Array of update language codes to decline. 63 | 64 | .PARAMETER DeclinePrereleaseUpdates 65 | Decline any updates which are exclusively for pre-release products (e.g. betas). 66 | 67 | .PARAMETER DeclineSecurityOnlyUpdates 68 | Decline any Security Only updates. 69 | 70 | .PARAMETER DeclineSupersededUpdates 71 | Specifies that the cmdlet declines superseded updates. 72 | 73 | .PARAMETER DeclineWindowsNextUpdates 74 | Decline any Windows Next updates. 75 | 76 | .PARAMETER FindSuspectDeclines 77 | Scan all declined updates for any that may have been inadvertently declined. 78 | 79 | The returned suspect updates are those which: 80 | - Are not superseded or expired 81 | - Are not cluster or farm updates (if set to decline) 82 | - Are not in the filtered list of updates to decline from the bundled catalogue 83 | 84 | .PARAMETER RunDefaultTasks 85 | Performs all clean-up tasks except for declining any unneeded updates as defined in the included update catalogue CSV file. 86 | 87 | You can disable one or more of the default clean-up tasks by setting the associated switch parameter to false (e.g. -CompressUpdates:$false). 88 | 89 | You can perform a clean-up of unneeded updates by specifying the DeclineCategoriesInclude or DeclineCategoriesExclude parameter with your chosen categories. 90 | 91 | Also note that this does not perform a server synchronisation before clean-up or find suspect declined updates. These tasks can be included via their respective parameters. 92 | 93 | .PARAMETER SynchroniseServer 94 | Perform a synchronisation against the upstream server before running clean-up. 95 | 96 | .PARAMETER UpdateServer 97 | The WSUS server to perform operations on as returned by Get-WsusServer. 98 | 99 | If omitted we'll attempt to connect to a WSUS server on the local system. 100 | 101 | .EXAMPLE 102 | PS C:\>$SuspectDeclines = Invoke-WsusSpringClean -RunDefaultTasks -FindSuspectDeclines 103 | 104 | Runs the default clean-up tasks & checks for declined updates that may not be intentional. 105 | 106 | .EXAMPLE 107 | PS C:\>Invoke-WsusSpringClean -DeclineCategoriesInclude @('Region - US', 'Superseded') 108 | 109 | Declines all unneeded updates in the "Region - US" & "Superseded" categories. 110 | 111 | .EXAMPLE 112 | PS C:\>Invoke-WsusSpringClean -DeclineLanguagesExclude @('en-AU') 113 | 114 | Declines all language specific updates excluding those for English (Australia). 115 | 116 | .EXAMPLE 117 | PS C:\>Invoke-WsusSpringClean -DeclineArchitectures @('arm64', 'ia64') 118 | 119 | Declines all architecture specific updates for ARM64 & IA64 (Itanium) systems. 120 | 121 | .NOTES 122 | The script intentionally avoids usage of most WSUS cmdlets provided by the UpdateServices module as many are extremely slow. This is particularly true of the Get-WsusUpdate cmdlet. 123 | 124 | The efficiency of the update declining logic could be substantially improved. That said, this script is not typically run frequently (~monthly), so this isn't a major priority. 125 | 126 | .LINK 127 | https://github.com/ralish/PSWsusSpringClean 128 | #> 129 | 130 | [CmdletBinding(DefaultParameterSetName = 'Default', SupportsShouldProcess)] 131 | [OutputType([Void], [Microsoft.UpdateServices.Internal.BaseApi.Update[]])] 132 | Param( 133 | [Microsoft.UpdateServices.Internal.BaseApi.UpdateServer]$UpdateServer, 134 | 135 | [Switch]$RunDefaultTasks, 136 | [Switch]$SynchroniseServer, 137 | [Switch]$FindSuspectDeclines, 138 | 139 | [Switch]$DeclineClusterUpdates, 140 | [Switch]$DeclineFarmUpdates, 141 | [Switch]$DeclinePrereleaseUpdates, 142 | [Switch]$DeclineSecurityOnlyUpdates, 143 | [Switch]$DeclineWindowsNextUpdates, 144 | 145 | [String[]]$DeclineCategoriesExclude, 146 | [String[]]$DeclineCategoriesInclude, 147 | 148 | [ValidateScript( { Test-WsusSpringCleanArchitectures -Architectures $_ } )] 149 | [String[]]$DeclineArchitectures, 150 | 151 | [ValidateScript( { Test-WsusSpringCleanLanguageCodes -LanguageCodes $_ } )] 152 | [String[]]$DeclineLanguagesExclude, 153 | 154 | [ValidateScript( { Test-WsusSpringCleanLanguageCodes -LanguageCodes $_ } )] 155 | [String[]]$DeclineLanguagesInclude, 156 | 157 | # Wrapping of Invoke-WsusServerCleanup 158 | [Switch]$CleanupObsoleteComputers, 159 | [Switch]$CleanupObsoleteUpdates, 160 | [Switch]$CleanupUnneededContentFiles, 161 | [Switch]$CompressUpdates, 162 | [Switch]$DeclineExpiredUpdates, 163 | [Switch]$DeclineSupersededUpdates 164 | ) 165 | 166 | if ($PSBoundParameters.ContainsKey('DeclineCategoriesExclude') -and $PSBoundParameters.ContainsKey('DeclineCategoriesInclude')) { 167 | throw 'Can only specify one of DeclineCategoriesExclude and DeclineCategoriesInclude.' 168 | } 169 | 170 | if ($PSBoundParameters.ContainsKey('DeclineLanguagesExclude') -and $PSBoundParameters.ContainsKey('DeclineLanguagesInclude')) { 171 | throw 'Can only specify one of DeclineLanguagesExclude and DeclineLanguagesInclude.' 172 | } 173 | 174 | if (!$PSBoundParameters.ContainsKey('UpdateServer')) { 175 | try { 176 | $UpdateServer = Get-WsusServer 177 | } catch { 178 | throw 'Failed to connect to local WSUS server via Get-WsusServer.' 179 | } 180 | } 181 | 182 | if ($RunDefaultTasks) { 183 | $DefaultTasks = @( 184 | 'DeclineClusterUpdates', 185 | 'DeclineFarmUpdates', 186 | 'DeclinePrereleaseUpdates', 187 | 'DeclineSecurityOnlyUpdates', 188 | 'DeclineWindowsNextUpdates', 189 | 190 | 'CleanupObsoleteComputers', 191 | 'CleanupObsoleteUpdates', 192 | 'CleanupUnneededContentFiles', 193 | 'CompressUpdates', 194 | 'DeclineExpiredUpdates', 195 | 'DeclineSupersededUpdates' 196 | ) 197 | 198 | foreach ($Task in $DefaultTasks) { 199 | if ($PSBoundParameters.ContainsKey($Task)) { 200 | Set-Variable -Name $Task -Value (Get-Variable -Name $Task).Value -WhatIf:$false 201 | } else { 202 | Set-Variable -Name $Task -Value $true -WhatIf:$false 203 | } 204 | } 205 | } 206 | 207 | Import-WsusSpringCleanMetadata 208 | 209 | # Determine which categories of updates to decline (if any) 210 | if ($PSBoundParameters.ContainsKey('DeclineCategoriesExclude') -or $PSBoundParameters.ContainsKey('DeclineCategoriesInclude')) { 211 | Import-WsusSpringCleanCatalogue 212 | $CatalogueCategories = $Script:WscCatalogue.Category | Sort-Object | Get-Unique 213 | 214 | if ($PSBoundParameters.ContainsKey('DeclineCategoriesExclude')) { 215 | $DeclineCategories = $CatalogueCategories | Where-Object { $_ -notin $DeclineCategoriesExclude } 216 | } else { 217 | $DeclineCategories = $CatalogueCategories | Where-Object { $_ -in $DeclineCategoriesInclude } 218 | } 219 | } 220 | 221 | # Fetch the metadata for any architectures we're going to decline 222 | if ($PSBoundParameters.ContainsKey('DeclineArchitectures')) { 223 | $DeclineArchitecturesMetadata = @() 224 | foreach ($Architecture in $DeclineArchitectures) { 225 | $DeclineArchitecturesMetadata += $Script:WscMetadata.Architectures.Architecture | Where-Object name -EQ $Architecture 226 | } 227 | } 228 | 229 | # Fetch the metadata for any languages we're going to decline 230 | if ($PSBoundParameters.ContainsKey('DeclineLanguagesExclude')) { 231 | $DeclineLanguagesMetadata = $Script:WscMetadata.Languages.Language | Where-Object code -NotIn $DeclineLanguagesExclude 232 | } elseif ($PSBoundParameters.ContainsKey('DeclineLanguagesInclude')) { 233 | $DeclineLanguagesMetadata = $Script:WscMetadata.Languages.Language | Where-Object code -In $DeclineLanguagesInclude 234 | } 235 | 236 | $WriteProgressParams = @{ 237 | Id = 0 238 | Activity = 'Running WSUS spring-clean' 239 | } 240 | 241 | $TasksDone = 0 242 | $TasksTotal = 3 243 | 244 | if ($SynchroniseServer) { 245 | $TasksTotal++ 246 | } 247 | 248 | if ($FindSuspectDeclines) { 249 | $TasksTotal++ 250 | } 251 | 252 | if ($SynchroniseServer) { 253 | Write-Progress @WriteProgressParams -Status 'Running server synchronisation' -PercentComplete ($TasksDone / $TasksTotal * 100) 254 | Invoke-WsusServerSynchronisation -UpdateServer $UpdateServer 255 | $TasksDone++ 256 | } 257 | 258 | Write-Progress @WriteProgressParams -Status 'Running server clean-up (Phase 1)' -PercentComplete ($TasksDone / $TasksTotal * 100) 259 | $CleanupWrapperParams = @{ 260 | UpdateServer = $UpdateServer 261 | CleanupObsoleteUpdates = $CleanupObsoleteUpdates 262 | CompressUpdates = $CompressUpdates 263 | DeclineExpiredUpdates = $DeclineExpiredUpdates 264 | DeclineSupersededUpdates = $DeclineSupersededUpdates 265 | } 266 | Invoke-WsusServerCleanupWrapper @CleanupWrapperParams -ProgressParentId $WriteProgressParams['Id'] 267 | $TasksDone++ 268 | 269 | $SpringCleanParams = @{ 270 | UpdateServer = $UpdateServer 271 | DeclineClusterUpdates = $DeclineClusterUpdates 272 | DeclineFarmUpdates = $DeclineFarmUpdates 273 | DeclinePrereleaseUpdates = $DeclinePrereleaseUpdates 274 | DeclineSecurityOnlyUpdates = $DeclineSecurityOnlyUpdates 275 | DeclineWindowsNextUpdates = $DeclineWindowsNextUpdates 276 | } 277 | 278 | if ($PSBoundParameters.ContainsKey('DeclineCategoriesExclude') -or $PSBoundParameters.ContainsKey('DeclineCategoriesInclude')) { 279 | $SpringCleanParams['DeclineCategories'] = $DeclineCategories 280 | } 281 | 282 | if ($PSBoundParameters.ContainsKey('DeclineArchitectures')) { 283 | $SpringCleanParams['DeclineArchitectures'] = $DeclineArchitecturesMetadata 284 | } 285 | 286 | if ($PSBoundParameters.ContainsKey('DeclineLanguagesExclude') -or $PSBoundParameters.ContainsKey('DeclineLanguagesInclude')) { 287 | $SpringCleanParams['DeclineLanguages'] = $DeclineLanguagesMetadata 288 | } 289 | 290 | Write-Progress @WriteProgressParams -Status 'Running server clean-up (Phase 2)' -PercentComplete ($TasksDone / $TasksTotal * 100) 291 | Invoke-WsusServerSpringClean @SpringCleanParams -ProgressParentId $WriteProgressParams['Id'] 292 | $TasksDone++ 293 | 294 | Write-Progress @WriteProgressParams -Status 'Running server clean-up (Phase 3)' -PercentComplete ($TasksDone / $TasksTotal * 100) 295 | $CleanupWrapperParams = @{ 296 | UpdateServer = $UpdateServer 297 | CleanupObsoleteComputers = $CleanupObsoleteComputers 298 | CleanupUnneededContentFiles = $CleanupUnneededContentFiles 299 | } 300 | Invoke-WsusServerCleanupWrapper @CleanupWrapperParams -ProgressParentId $WriteProgressParams['Id'] 301 | $TasksDone++ 302 | 303 | Write-Progress @WriteProgressParams -Status 'Searching for suspect declined updates' -PercentComplete ($TasksDone / $TasksTotal * 100) 304 | if ($FindSuspectDeclines) { 305 | Get-WsusSuspectDeclines @SpringCleanParams -ProgressParentId $WriteProgressParams['Id'] 306 | $TasksDone++ 307 | } 308 | 309 | Write-Progress @WriteProgressParams -Completed 310 | } 311 | 312 | Function Get-WsusSuspectDeclines { 313 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '')] 314 | [CmdletBinding()] 315 | [OutputType([Void], [Microsoft.UpdateServices.Internal.BaseApi.Update[]])] 316 | Param( 317 | [Parameter(Mandatory)] 318 | [Microsoft.UpdateServices.Internal.BaseApi.UpdateServer]$UpdateServer, 319 | 320 | [Switch]$DeclineClusterUpdates, 321 | [Switch]$DeclineFarmUpdates, 322 | [Switch]$DeclinePrereleaseUpdates, 323 | [Switch]$DeclineSecurityOnlyUpdates, 324 | [Switch]$DeclineWindowsNextUpdates, 325 | 326 | [String[]]$DeclineCategories, 327 | [Xml.XmlElement[]]$DeclineArchitectures, 328 | [Xml.XmlElement[]]$DeclineLanguages, 329 | 330 | [ValidateRange(-1, [Int]::MaxValue)] 331 | [Int]$ProgressParentId 332 | ) 333 | 334 | $WriteProgressParams = @{ 335 | Activity = 'Searching for suspect declined updates' 336 | } 337 | 338 | if ($PSBoundParameters.ContainsKey('ProgressParentId')) { 339 | $WriteProgressParams['ParentId'] = $ProgressParentId 340 | $WriteProgressParams['Id'] = $ProgressParentId + 1 341 | } 342 | 343 | $UpdateScope = New-Object -TypeName 'Microsoft.UpdateServices.Administration.UpdateScope' 344 | 345 | Write-Progress @WriteProgressParams -Status 'Retrieving declined updates' -PercentComplete 0 346 | $UpdateScope.ApprovedStates = [Microsoft.UpdateServices.Administration.ApprovedStates]::Declined 347 | $WsusDeclined = $UpdateServer.GetUpdates($UpdateScope) 348 | 349 | # Filter superseded and expired updates first as it's likely there will be 350 | # lots. This will help to improve performance of the remaining filtering. 351 | Write-Progress @WriteProgressParams -Status 'Filtering superseded and expired updates' -PercentComplete 20 352 | $WsusDeclined = $WsusDeclined | Where-Object { 353 | $_.IsSuperseded -eq $false -and 354 | $_.PublicationState -ne 'Expired' 355 | } 356 | 357 | # Filter any declined architectures 358 | if ($PSBoundParameters.ContainsKey('DeclineArchitectures')) { 359 | Write-Progress @WriteProgressParams -Status 'Filtering declined architectures' -PercentComplete 30 360 | $RegExArchitectures = '\s({0})' -f [String]::Join('|', $DeclineArchitectures.regex) 361 | $WsusDeclined = $WsusDeclined | Where-Object Title -NotMatch $RegExArchitectures 362 | } 363 | 364 | # Filter any declined languages 365 | if ($PSBoundParameters.ContainsKey('DeclineLanguages')) { 366 | foreach ($Language in $DeclineLanguages) { 367 | $Status = 'Filtering declined language: {0}' -f $Language.code 368 | Write-Progress @WriteProgressParams -Status $Status -PercentComplete 40 369 | 370 | $RegExLanguageCode = '\s\[?{0}(_LI?P)?\]?' -f $Language.code 371 | $RegExLanguageName = '\b{0} (Language( Interface)?|LIP) Pack\b' -f [Regex]::Escape($Language.name) 372 | $FilteredDeclines = New-Object -TypeName 'Collections.Generic.List[Microsoft.UpdateServices.Internal.BaseApi.Update]' 373 | 374 | foreach ($Update in $WsusDeclined) { 375 | # If an update matches a BCP 47 language range and a language 376 | # name, then only act on the former as it's more specific. 377 | if ($Update.Title -match $RegExLanguageCode) { 378 | continue 379 | } 380 | 381 | if ($Update.Title -match $RegExLanguageName) { 382 | continue 383 | } 384 | 385 | $FilteredDeclines.Add($Update) 386 | } 387 | 388 | $WsusDeclined = $FilteredDeclines 389 | } 390 | } 391 | 392 | # Ignore declined categories 393 | if ($PSBoundParameters.ContainsKey('DeclineCategories')) { 394 | $IgnoredCatalogueCategories = $Script:WscCatalogue | Where-Object Category -In $DeclineCategories 395 | } 396 | 397 | Write-Progress @WriteProgressParams -Status 'Analyzing declined updates' -PercentComplete 50 398 | $SuspectDeclines = New-Object -TypeName 'Collections.Generic.List[Microsoft.UpdateServices.Internal.BaseApi.Update]' 399 | $UpdatesProcessed = 0 400 | foreach ($Update in $WsusDeclined) { 401 | # Update progress every 100 updates 402 | if ($UpdatesProcessed % 100 -eq 0) { 403 | $PercentComplete = $UpdatesProcessed / $WsusDeclined.Count * 50 + 50 404 | Write-Progress @WriteProgressParams -PercentComplete $PercentComplete 405 | } 406 | 407 | # Ignore cluster updates if they were declined 408 | if ($DeclineClusterUpdates -and $Update.Title -match $RegExClusterUpdates) { 409 | continue 410 | } 411 | 412 | # Ignore farm updates if they were declined 413 | if ($DeclineFarmUpdates -and $Update.Title -match $RegExFarmUpdates) { 414 | continue 415 | } 416 | 417 | # Ignore pre-release updates if they were declined 418 | if ($DeclinePrereleaseUpdates -and $Update.Title -match $RegExPrereleaseUpdates) { 419 | continue 420 | } 421 | 422 | # Ignore Security Only Quality updates if they were declined 423 | if ($DeclineSecurityOnlyUpdates -and $Update.Title -match $RegExSecurityOnlyUpdates) { 424 | continue 425 | } 426 | 427 | # Ignore Windows Next updates if they were declined 428 | if ($DeclineWindowsNextUpdates -and $Update.Title -match $RegExWindowsNextUpdates) { 429 | continue 430 | } 431 | 432 | # Ignore any update categories which were declined 433 | if ($PSBoundParameters.ContainsKey('DeclineCategories')) { 434 | if ($Update.Title -in $IgnoredCatalogueCategories.Title) { 435 | continue 436 | } 437 | } 438 | 439 | $SuspectDeclines.Add($Update) 440 | $UpdatesProcessed++ 441 | } 442 | 443 | Write-Progress @WriteProgressParams -Completed 444 | return $SuspectDeclines.ToArray() 445 | } 446 | 447 | Function Import-WsusSpringCleanMetadata { 448 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '')] 449 | [CmdletBinding()] 450 | [OutputType([Void])] 451 | Param() 452 | 453 | if (Get-Variable -Name 'WscCatalogue' -Scope Script -ErrorAction SilentlyContinue) { 454 | return 455 | } 456 | 457 | Write-Verbose -Message 'Importing module metadata ...' 458 | $MetadataPath = Join-Path -Path $PSScriptRoot -ChildPath 'PSWsusSpringClean.xml' 459 | $Script:WscMetadata = ([Xml](Get-Content -Path $MetadataPath)).PSWsusSpringClean 460 | } 461 | 462 | Function Invoke-WsusDeclineUpdatesByCatalogue { 463 | [CmdletBinding(SupportsShouldProcess)] 464 | [OutputType([Void])] 465 | Param( 466 | [Parameter(Mandatory)] 467 | [Microsoft.UpdateServices.Internal.BaseApi.Update[]]$Updates, 468 | 469 | [Parameter(Mandatory)] 470 | [String]$Category 471 | ) 472 | 473 | $UpdatesToDecline = $Script:WscCatalogue | Where-Object Category -EQ $Category 474 | $MatchingUpdates = $Updates | Where-Object Title -In $UpdatesToDecline.Title 475 | 476 | foreach ($Update in $MatchingUpdates) { 477 | if ($PSCmdlet.ShouldProcess($Update.Title, 'Decline')) { 478 | Write-Verbose -Message ('Declining update: {0}' -f $Update.Title) 479 | $Update.Decline() 480 | } 481 | } 482 | } 483 | 484 | Function Invoke-WsusDeclineUpdatesByRegEx { 485 | [CmdletBinding(SupportsShouldProcess)] 486 | [OutputType([Void])] 487 | Param( 488 | [Parameter(Mandatory)] 489 | [Microsoft.UpdateServices.Internal.BaseApi.Update[]]$Updates, 490 | 491 | [Parameter(Mandatory)] 492 | [String]$RegEx 493 | ) 494 | 495 | foreach ($Update in $Updates) { 496 | if ($Update.Title -match $RegEx) { 497 | if ($PSCmdlet.ShouldProcess($Update.Title, 'Decline')) { 498 | Write-Verbose -Message ('Declining update: {0}' -f $Update.Title) 499 | $Update.Decline() 500 | } 501 | } 502 | } 503 | } 504 | 505 | Function Invoke-WsusServerCleanupWrapper { 506 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingWriteHost', '')] 507 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSShouldProcess', '')] 508 | [CmdletBinding(SupportsShouldProcess)] 509 | [OutputType([Void])] 510 | Param( 511 | [Parameter(Mandatory)] 512 | [Microsoft.UpdateServices.Internal.BaseApi.UpdateServer]$UpdateServer, 513 | 514 | [Switch]$CleanupObsoleteComputers, 515 | [Switch]$CleanupObsoleteUpdates, 516 | [Switch]$CleanupUnneededContentFiles, 517 | [Switch]$CompressUpdates, 518 | [Switch]$DeclineExpiredUpdates, 519 | [Switch]$DeclineSupersededUpdates, 520 | 521 | [ValidateRange(-1, [Int]::MaxValue)] 522 | [Int]$ProgressParentId 523 | ) 524 | 525 | $WriteProgressParams = @{ 526 | Activity = 'Running Microsoft built-in clean-up tasks' 527 | } 528 | 529 | if ($PSBoundParameters.ContainsKey('ProgressParentId')) { 530 | $WriteProgressParams['ParentId'] = $ProgressParentId 531 | $WriteProgressParams['Id'] = $ProgressParentId + 1 532 | } 533 | 534 | $TasksDone = 0 535 | $TasksTotal = 0 536 | $ValidTasks = @( 537 | 'CleanupObsoleteComputers' 538 | 'CleanupObsoleteUpdates' 539 | 'CleanupUnneededContentFiles' 540 | 'CompressUpdates' 541 | 'DeclineExpiredUpdates' 542 | 'DeclineSupersededUpdates' 543 | ) 544 | 545 | foreach ($SwitchParam in ($MyInvocation.MyCommand.Parameters.Values | Where-Object SwitchParameter)) { 546 | # This kind of sucks but as we're enumerating switch parameters we'll 547 | # also get built-in ones like -Verbose. I'm not aware of any way to 548 | # programmatically filter these out, and a blocklist feels brittle. 549 | if ($SwitchParam.Name -notin $ValidTasks) { 550 | continue 551 | } 552 | 553 | if (Get-Variable -Name $SwitchParam.Name -ValueOnly) { 554 | $TasksTotal++ 555 | } 556 | } 557 | 558 | if ($CleanupObsoleteComputers) { 559 | Write-Progress @WriteProgressParams -Status 'Deleting obsolete computers' -PercentComplete ($TasksDone / $TasksTotal * 100) 560 | Write-Host (Invoke-WsusServerCleanup -UpdateServer $UpdateServer -CleanupObsoleteComputers) 561 | $TasksDone++ 562 | } 563 | 564 | if ($CleanupObsoleteUpdates) { 565 | Write-Progress @WriteProgressParams -Status 'Deleting obsolete updates' -PercentComplete ($TasksDone / $TasksTotal * 100) 566 | Write-Host (Invoke-WsusServerCleanup -UpdateServer $UpdateServer -CleanupObsoleteUpdates) 567 | $TasksDone++ 568 | } 569 | 570 | if ($CleanupUnneededContentFiles) { 571 | Write-Progress @WriteProgressParams -Status 'Deleting unneeded update files' -PercentComplete ($TasksDone / $TasksTotal * 100) 572 | Write-Host (Invoke-WsusServerCleanup -UpdateServer $UpdateServer -CleanupUnneededContentFiles) 573 | $TasksDone++ 574 | } 575 | 576 | if ($CompressUpdates) { 577 | Write-Progress @WriteProgressParams -Status 'Deleting obsolete update revisions' -PercentComplete ($TasksDone / $TasksTotal * 100) 578 | Write-Host (Invoke-WsusServerCleanup -UpdateServer $UpdateServer -CompressUpdates) 579 | $TasksDone++ 580 | } 581 | 582 | if ($DeclineExpiredUpdates) { 583 | Write-Progress @WriteProgressParams -Status 'Declining expired updates' -PercentComplete ($TasksDone / $TasksTotal * 100) 584 | Write-Host (Invoke-WsusServerCleanup -UpdateServer $UpdateServer -DeclineExpiredUpdates) 585 | $TasksDone++ 586 | } 587 | 588 | if ($DeclineSupersededUpdates) { 589 | Write-Progress @WriteProgressParams -Status 'Declining superseded updates' -PercentComplete ($TasksDone / $TasksTotal * 100) 590 | Write-Host (Invoke-WsusServerCleanup -UpdateServer $UpdateServer -DeclineSupersededUpdates) 591 | $TasksDone++ 592 | } 593 | 594 | Write-Progress @WriteProgressParams -Completed 595 | } 596 | 597 | Function Invoke-WsusServerSynchronisation { 598 | [CmdletBinding(SupportsShouldProcess)] 599 | [OutputType([Void])] 600 | Param( 601 | [Parameter(Mandatory)] 602 | [Microsoft.UpdateServices.Internal.BaseApi.UpdateServer]$UpdateServer 603 | ) 604 | 605 | if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, 'WSUS synchronization')) { 606 | $SyncStatus = $UpdateServer.GetSubscription().GetSynchronizationStatus() 607 | if ($SyncStatus -eq 'NotProcessing') { 608 | $UpdateServer.GetSubscription().StartSynchronization() 609 | } elseif ($SyncStatus -eq 'Running') { 610 | Write-Warning -Message 'A synchronisation appears to already be running! Waiting for it to complete ...' 611 | } else { 612 | throw 'WSUS server returned unknown synchronisation status: {0}' -f $SyncStatus 613 | } 614 | 615 | do { 616 | Start-Sleep -Seconds 5 617 | } while ($UpdateServer.GetSubscription().GetSynchronizationStatus() -eq 'Running') 618 | 619 | $SyncResult = $UpdateServer.GetSubscription().GetLastSynchronizationInfo().Result 620 | if ($SyncResult -ne 'Succeeded') { 621 | throw 'WSUS server synchronisation completed with unexpected result: {0}' -f $SyncResult 622 | } 623 | } 624 | } 625 | 626 | Function Invoke-WsusServerSpringClean { 627 | [CmdletBinding(SupportsShouldProcess)] 628 | [OutputType([Void])] 629 | Param( 630 | [Parameter(Mandatory)] 631 | [Microsoft.UpdateServices.Internal.BaseApi.UpdateServer]$UpdateServer, 632 | 633 | [Switch]$DeclineClusterUpdates, 634 | [Switch]$DeclineFarmUpdates, 635 | [Switch]$DeclinePrereleaseUpdates, 636 | [Switch]$DeclineSecurityOnlyUpdates, 637 | [Switch]$DeclineWindowsNextUpdates, 638 | 639 | [String[]]$DeclineCategories, 640 | [Xml.XmlElement[]]$DeclineArchitectures, 641 | [Xml.XmlElement[]]$DeclineLanguages, 642 | 643 | [ValidateRange(-1, [Int]::MaxValue)] 644 | [Int]$ProgressParentId 645 | ) 646 | 647 | $WriteProgressParams = @{ 648 | Activity = 'Running PSWsusSpringClean custom clean-up tasks' 649 | } 650 | 651 | if ($PSBoundParameters.ContainsKey('ProgressParentId')) { 652 | $WriteProgressParams['ParentId'] = $ProgressParentId 653 | $WriteProgressParams['Id'] = $ProgressParentId + 1 654 | } 655 | 656 | $TasksDone = 0 657 | $TasksTotal = 2 # Retrieving approved & unapproved updates 658 | $ValidTasks = @( 659 | 'DeclineClusterUpdates' 660 | 'DeclineFarmUpdates' 661 | 'DeclinePrereleaseUpdates' 662 | 'DeclineSecurityOnlyUpdates' 663 | 'DeclineWindowsNextUpdates' 664 | 'DeclineCategories' 665 | 'DeclineCategories' 666 | 'DeclineLanguages' 667 | ) 668 | 669 | foreach ($Param in $MyInvocation.MyCommand.Parameters.Values) { 670 | # This kind of sucks but as we're enumerating switch parameters we'll 671 | # also get built-in ones like -Verbose. I'm not aware of any way to 672 | # programmatically filter these out, and a blocklist feels brittle. 673 | if ($Param.Name -notin $ValidTasks) { 674 | continue 675 | } 676 | 677 | if ($Param.SwitchParameter) { 678 | if ((Get-Variable -Name $Param.Name -ValueOnly) -eq $true) { 679 | $TasksTotal++ 680 | } 681 | } else { 682 | $ArrayVar = Get-Variable -Name $Param.Name -ValueOnly 683 | if ($null -ne $ArrayVar -and $ArrayVar.Count -gt 0) { 684 | $TasksTotal++ 685 | } 686 | } 687 | } 688 | 689 | $UpdateScope = New-Object -TypeName 'Microsoft.UpdateServices.Administration.UpdateScope' 690 | 691 | Write-Progress @WriteProgressParams -Status 'Retrieving approved updates' -PercentComplete ($TasksDone / $TasksTotal * 100) 692 | $UpdateScope.ApprovedStates = [Microsoft.UpdateServices.Administration.ApprovedStates]::LatestRevisionApproved 693 | $WsusApproved = $UpdateServer.GetUpdates($UpdateScope) 694 | $TasksDone++ 695 | 696 | Write-Progress @WriteProgressParams -Status 'Retrieving unapproved updates' -PercentComplete ($TasksDone / $TasksTotal * 100) 697 | $UpdateScope.ApprovedStates = [Microsoft.UpdateServices.Administration.ApprovedStates]::NotApproved 698 | $WsusUnapproved = $UpdateServer.GetUpdates($UpdateScope) 699 | $TasksDone++ 700 | 701 | $WsusAnyExceptDeclined = $WsusApproved + $WsusUnapproved 702 | 703 | if ($DeclineClusterUpdates) { 704 | Write-Progress @WriteProgressParams -Status 'Declining cluster updates' -PercentComplete ($TasksDone / $TasksTotal * 100) 705 | Invoke-WsusDeclineUpdatesByRegEx -Updates $WsusAnyExceptDeclined -RegEx $Script:RegExClusterUpdates 706 | $TasksDone++ 707 | } 708 | 709 | if ($DeclineFarmUpdates) { 710 | Write-Progress @WriteProgressParams -Status 'Declining farm updates' -PercentComplete ($TasksDone / $TasksTotal * 100) 711 | Invoke-WsusDeclineUpdatesByRegEx -Updates $WsusAnyExceptDeclined -RegEx $Script:RegExFarmUpdates 712 | $TasksDone++ 713 | } 714 | 715 | if ($DeclinePrereleaseUpdates) { 716 | Write-Progress @WriteProgressParams -Status 'Declining pre-release updates' -PercentComplete ($TasksDone / $TasksTotal * 100) 717 | Invoke-WsusDeclineUpdatesByRegEx -Updates $WsusAnyExceptDeclined -RegEx $Script:RegExPrereleaseUpdates 718 | $TasksDone++ 719 | } 720 | 721 | if ($DeclineSecurityOnlyUpdates) { 722 | Write-Progress @WriteProgressParams -Status 'Declining Security Only updates' -PercentComplete ($TasksDone / $TasksTotal * 100) 723 | Invoke-WsusDeclineUpdatesByRegEx -Updates $WsusAnyExceptDeclined -RegEx $Script:RegExSecurityOnlyUpdates 724 | $TasksDone++ 725 | } 726 | 727 | if ($DeclineWindowsNextUpdates) { 728 | Write-Progress @WriteProgressParams -Status 'Declining Windows Next updates' -PercentComplete ($TasksDone / $TasksTotal * 100) 729 | Invoke-WsusDeclineUpdatesByRegEx -Updates $WsusAnyExceptDeclined -RegEx $Script:RegExWindowsNextUpdates 730 | $TasksDone++ 731 | } 732 | 733 | if ($PSBoundParameters.ContainsKey('DeclineCategories')) { 734 | $PercentComplete = $TasksDone / $TasksTotal * 100 735 | foreach ($Category in $DeclineCategories) { 736 | $Status = 'Declining updates in category: {0}' -f $Category 737 | Write-Progress @WriteProgressParams -Status $Status -PercentComplete $PercentComplete 738 | Invoke-WsusDeclineUpdatesByCatalogue -Updates $WsusAnyExceptDeclined -Category $Category 739 | } 740 | $TasksDone++ 741 | } 742 | 743 | if ($PSBoundParameters.ContainsKey('DeclineArchitectures')) { 744 | $PercentComplete = $TasksDone / $TasksTotal * 100 745 | foreach ($Architecture in $DeclineArchitectures) { 746 | $Status = 'Declining updates with architecture: {0}' -f $Architecture.name 747 | Write-Progress @WriteProgressParams -Status $Status -PercentComplete $PercentComplete 748 | $RegExArchitecture = '\s({0})' -f $Architecture.regex 749 | Invoke-WsusDeclineUpdatesByRegEx -Updates $WsusAnyExceptDeclined -RegEx $RegExArchitecture 750 | } 751 | $TasksDone++ 752 | } 753 | 754 | if ($PSBoundParameters.ContainsKey('DeclineLanguages')) { 755 | $PercentComplete = $TasksDone / $TasksTotal * 100 756 | 757 | # Limit the updates we iterate over for each language 758 | $WsusLanguageUpdates = $WsusAnyExceptDeclined | Where-Object { 759 | $_.Title -match $RegExBcp47 -or 760 | $_.Title -match $RegExLanguageUpdates 761 | } 762 | 763 | foreach ($Language in $DeclineLanguages) { 764 | $Status = 'Declining updates with language: {0}' -f $Language.code 765 | Write-Progress @WriteProgressParams -Status $Status -PercentComplete $PercentComplete 766 | 767 | $RegExLanguageCode = '\s\[?{0}(_LI?P)?\]?' -f $Language.code 768 | $RegExLanguageName = '\b{0} (Language( Interface)?|LIP) Pack\b' -f [Regex]::Escape($Language.name) 769 | 770 | foreach ($Update in $WsusLanguageUpdates) { 771 | # If an update matches a BCP 47 language range and a language 772 | # name, then only act on the former as it's more specific. 773 | if ($Update.Title -match $RegExLanguageCode) { 774 | if ($PSCmdlet.ShouldProcess($Update.Title, 'Decline')) { 775 | Write-Verbose -Message ('Declining update: {0}' -f $Update.Title) 776 | $Update.Decline() 777 | } 778 | 779 | continue 780 | } 781 | 782 | if ($Update.Title -match $RegExLanguageName) { 783 | if ($PSCmdlet.ShouldProcess($Update.Title, 'Decline')) { 784 | Write-Verbose -Message ('Declining update: {0}' -f $Update.Title) 785 | $Update.Decline() 786 | } 787 | } 788 | } 789 | } 790 | $TasksDone++ 791 | } 792 | 793 | Write-Progress @WriteProgressParams -Completed 794 | } 795 | 796 | Function Test-WsusSpringCleanArchitectures { 797 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '')] 798 | [OutputType([Boolean])] 799 | Param( 800 | [Parameter(Mandatory)] 801 | [String[]]$Architectures 802 | ) 803 | 804 | Import-WsusSpringCleanMetadata 805 | 806 | $KnownArchitectures = $Script:WscMetadata.Architectures.Architecture.name 807 | foreach ($Architecture in $Architectures) { 808 | if ($Architecture -notin $KnownArchitectures) { 809 | throw 'Unknown architecture specified: {0}' -f $Architecture 810 | } 811 | } 812 | 813 | return $true 814 | } 815 | 816 | Function Test-WsusSpringCleanLanguageCodes { 817 | [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '')] 818 | [OutputType([Boolean])] 819 | Param( 820 | [Parameter(Mandatory)] 821 | [String[]]$LanguageCodes 822 | ) 823 | 824 | Import-WsusSpringCleanMetadata 825 | 826 | $KnownLanguageCodes = $Script:WscMetadata.Languages.Language.code 827 | foreach ($LanguageCode in $LanguageCodes) { 828 | if ($LanguageCode -notin $KnownLanguageCodes) { 829 | throw 'Unknown language code specified: {0}' -f $LanguageCode 830 | } 831 | } 832 | 833 | return $true 834 | } 835 | 836 | Function ConvertTo-WsusSpringCleanCatalogue { 837 | [OutputType([PSCustomObject[]])] 838 | Param( 839 | [Parameter(Mandatory, ValueFromPipeline)] 840 | [Microsoft.UpdateServices.Internal.BaseApi.Update[]]$Updates 841 | ) 842 | 843 | Process { 844 | foreach ($Update in $Updates) { 845 | $ProductTitles = New-Object -TypeName 'Collections.Generic.List[String]' 846 | foreach ($ProductTitle in $Update.ProductTitles) { 847 | $ProductTitles.Add($ProductTitle) 848 | } 849 | 850 | [PSCustomObject]@{ 851 | 'Category' = 'Unknown' 852 | 'Title' = $Update.Title 853 | 'ProductTitles' = [String]::Join(', ', $ProductTitles) 854 | } 855 | } 856 | } 857 | } 858 | 859 | Function Import-WsusSpringCleanCatalogue { 860 | [CmdletBinding()] 861 | [OutputType([Void])] 862 | Param( 863 | [ValidateNotNullOrEmpty()] 864 | [String]$CataloguePath 865 | ) 866 | 867 | if (!$PSBoundParameters.ContainsKey('CataloguePath')) { 868 | $CataloguePath = Join-Path -Path $PSScriptRoot -ChildPath 'PSWsusSpringClean.csv' 869 | } 870 | 871 | Write-Verbose -Message 'Importing update catalogue ...' 872 | $Script:WscCatalogue = Import-Csv -Path $CataloguePath 873 | } 874 | 875 | Function Test-WsusSpringCleanCatalogue { 876 | [CmdletBinding()] 877 | [OutputType([PSCustomObject[]])] 878 | Param( 879 | [Microsoft.UpdateServices.Internal.BaseApi.UpdateServer]$UpdateServer, 880 | 881 | [ValidateNotNullOrEmpty()] 882 | [String]$CataloguePath 883 | ) 884 | 885 | if (!$PSBoundParameters.ContainsKey('UpdateServer')) { 886 | try { 887 | $UpdateServer = Get-WsusServer 888 | } catch { 889 | throw 'Failed to connect to local WSUS server via Get-WsusServer.' 890 | } 891 | } 892 | 893 | if ($PSBoundParameters.ContainsKey('CataloguePath')) { 894 | Import-WsusSpringCleanCatalogue @PSBoundParameters 895 | } else { 896 | Import-WsusSpringCleanCatalogue 897 | } 898 | 899 | $WriteProgressParams = @{ 900 | Activity = 'Testing PSWsusSpringClean catalogue' 901 | } 902 | 903 | $Results = New-Object -TypeName 'Collections.Generic.List[PSCustomObject]' 904 | $TasksDone = 0 905 | $TasksTotal = 3 906 | 907 | Write-Progress @WriteProgressParams -Status 'Retrieving all updates' -PercentComplete ($TasksDone / $TasksTotal * 100) 908 | $WsusUpdateScope = New-Object -TypeName 'Microsoft.UpdateServices.Administration.UpdateScope' 909 | $WsusUpdateScope.ApprovedStates = [Microsoft.UpdateServices.Administration.ApprovedStates]::Any 910 | $WsusUpdates = $UpdateServer.GetUpdates($WsusUpdateScope) 911 | $TasksDone++ 912 | 913 | Write-Progress @WriteProgressParams -Status 'Scanning for updates marked as superseded' -PercentComplete ($TasksDone / $TasksTotal * 100) 914 | foreach ($Update in ($Script:WscCatalogue | Where-Object Category -EQ 'Superseded')) { 915 | if ($Update.Title -in $WsusUpdates.Title) { 916 | $MatchedUpdates = @($WsusUpdates | Where-Object Title -EQ $Update.Title) 917 | $SupersededUpdates = @($MatchedUpdates | Where-Object IsSuperseded -EQ $true) 918 | 919 | if ($MatchedUpdates.Count -eq $SupersededUpdates.Count) { 920 | $Results.Add($Update) 921 | } 922 | } 923 | } 924 | $TasksDone++ 925 | 926 | Write-Progress @WriteProgressParams -Status 'Scanning for updates not present in WSUS' -PercentComplete ($TasksDone / $TasksTotal * 100) 927 | foreach ($Update in $Script:WscCatalogue) { 928 | if ($Update.Title -notin $WsusUpdates.Title) { 929 | $Results.Add($Update) 930 | } 931 | } 932 | $TasksDone++ 933 | 934 | Write-Progress @WriteProgressParams -Completed 935 | return $Results.ToArray() 936 | } 937 | --------------------------------------------------------------------------------