Limpieza de firmas del SCCM con Powershell
Hola a todos!
Adjunto un script para limpiar las firmas de antimalware antiguas del SCCM.
| <# | |
| .SYNOPSIS | |
| Perform a clean up of expired and/or supersded Software Updates in all Software Update Groups | |
| .DESCRIPTION | |
| Use this script if you need to perform a clean up of expired and/or superseded Software Updates from all Software Upgrade Groups in ConfigMgr | |
| .PARAMETER SiteServer | |
| Site server name with SMS Provider installed | |
| .PARAMETER Option | |
| Select an option to clean either ExpiredOnly, SupersededOnly or ExpiredSuperseded Software Updates from each Software Update Group | |
| .PARAMETER RemoveContent | |
| Remove the content for those Software Updates that will be removed from a Software Upgrade Group | |
| .PARAMETER ShowProgress | |
| Show a progressbar displaying the current operation | |
| .EXAMPLE | |
| Clean Software Update Groups from expired Software Updates, while showing the current progress and removing downloaded content, on a Primary Site server called 'CM01': | |
| .Clean-CMSoftwareUpdateGroups.ps1 -SiteServer CM01 -Option ExpiredOnly -RemoveContent -ShowProgress | |
| Clean Software Update Groups from superseded Software Updates, while showing the current progress, on a Primary Site server called 'CM01': | |
| .Clean-CMSoftwareUpdateGroups.ps1 -SiteServer CM01 -Option SupersededOnly -ShowProgress | |
| Clean Software Update Groups from expired and supersded Software Updates with verbose output, on a Primary Site server called 'CM01': | |
| .Clean-CMSoftwareUpdateGroups.ps1 -SiteServer CM01 -Option ExpiredSuperseded -Verbose | |
| .NOTES | |
| Name: Clean-CMSoftwareUpdateGroups.ps1 | |
| Author: Nickolaj Andersen | |
| Contact: @NickolajA | |
| Created: 2015-12-17 | |
| Version: 2.1 | |
| #> | |
| [CmdletBinding(SupportsShouldProcess=$true)] | |
| param( | |
| [parameter(Mandatory=$true, HelpMessage="Site server where the SMS Provider is installed")] | |
| [ValidateScript({Test-Connection -ComputerName $_ -Count 1 -Quiet})] | |
| [ValidateNotNullOrEmpty()] | |
| [string]$SiteServer, | |
| [parameter(Mandatory=$true, HelpMessage="Select an option to clean either ExpiredOnly, SupersededOnly or ExpiredSuperseded Software Updates from each Software Update Group")] | |
| [ValidateNotNullOrEmpty()] | |
| [ValidateSet("ExpiredOnly","SupersededOnly","ExpiredSuperseded")] | |
| [string]$Option, | |
| [parameter(Mandatory=$false, HelpMessage="Remove the content for those Software Updates that will be removed from a Software Upgrade Group")] | |
| [ValidateNotNullOrEmpty()] | |
| [switch]$RemoveContent, | |
| [parameter(Mandatory=$false, HelpMessage="Show a progressbar displaying the current operation")] | |
| [ValidateNotNullOrEmpty()] | |
| [switch]$ShowProgress | |
| ) | |
| Begin { | |
| # Determine SiteCode from WMI | |
| try { | |
| Write-Verbose "Determining Site Code for Site server: '$($SiteServer)'" | |
| $SiteCodeObjects = Get-WmiObject -Namespace "rootSMS" -Class SMS_ProviderLocation -ComputerName $SiteServer -ErrorAction Stop | |
| foreach ($SiteCodeObject in $SiteCodeObjects) { | |
| if ($SiteCodeObject.ProviderForLocalSite -eq $true) { | |
| $SiteCode = $SiteCodeObject.SiteCode | |
| Write-Verbose -Message "Site Code: $($SiteCode)" | |
| } | |
| } | |
| } | |
| catch [System.UnauthorizedAccessException] { | |
| Write-Warning -Message "Access denied" ; break | |
| } | |
| catch [System.Exception] { | |
| Write-Warning -Message "Unable to determine Site Code" ; break | |
| } | |
| # Temporarily set ErrorActionPreference | |
| $ErrorActionPreference = "Stop" | |
| # Set ProgressCount | |
| if ($PSBoundParameters["ShowProgress"]) { | |
| $ProgressCount = 0 | |
| } | |
| } | |
| Process { | |
| try { | |
| $StartTime = [Diagnostics.Stopwatch]::StartNew() | |
| $SUGResults = (Get-WmiObject -Namespace "rootSMSsite_$($SiteCode)" -Class SMS_AuthorizationList -ComputerName $SiteServer -ErrorAction SilentlyContinue | Measure-Object).Count | |
| $SUGResults | |
| if ($SUGResults -ge 1) { | |
| # Get list of removable Software Updates | |
| switch ($Option) { | |
| "ExpiredOnly" { | |
| $Query = "SELECT SU.CI_ID FROM SMS_SoftwareUpdate AS SU JOIN SMS_CIRelation AS CIR ON SU.CI_ID = CIR.ToCIID WHERE CIR.RelationType = 1 AND SU.IsExpired = 1 AND SU.IsSuperseded = 0" | |
| } | |
| "SupersededOnly" { | |
| $Query = "SELECT SU.CI_ID FROM SMS_SoftwareUpdate AS SU JOIN SMS_CIRelation AS CIR ON SU.CI_ID = CIR.ToCIID WHERE CIR.RelationType = 1 AND SU.IsExpired = 0 AND SU.IsSuperseded = 1" | |
| } | |
| "ExpiredSuperseded" { | |
| $Query = "SELECT SU.CI_ID FROM SMS_SoftwareUpdate AS SU JOIN SMS_CIRelation AS CIR ON SU.CI_ID = CIR.ToCIID WHERE CIR.RelationType = 1 AND (SU.IsExpired = 1 OR SU.IsSuperseded = 1)" | |
| } | |
| } | |
| try { | |
| $RemovableUpdates = Get-WmiObject -Namespace "rootSMSsite_$($SiteCode)" -Query $Query -ComputerName $SiteServer -ErrorAction Stop | |
| $RemovableUpdatesList = New-Object -TypeName System.Collections.ArrayList | |
| foreach ($RemovableUpdate in $RemovableUpdates) { | |
| $RemovableUpdatesList.Add($RemovableUpdate.CI_ID) | Out-Null | |
| } | |
| } | |
| catch [System.Exception] { | |
| Write-Warning -Message "Unable to determine removable Software Updates from selected option" | |
| } | |
| # Enumerate each Software Update Group | |
| $AuthorizationLists = Get-WmiObject -Namespace "rootSMSsite_$($SiteCode)" -Class SMS_AuthorizationList -ComputerName $SiteServer -ErrorAction Stop | |
| foreach ($AuthorizationList in $AuthorizationLists) { | |
| Write-Verbose -Message "Start processing '$($AuthorizationList.LocalizedDisplayName)'" | |
| if ($PSBoundParameters["ShowProgress"]) { | |
| $ProgressCount++ | |
| } | |
| Write-Progress -Activity "Processing Software Updates Groups" -Id 1 -Status "$($ProgressCount) / $($SUGResults)" -CurrentOperation "Current Software Update Group: '$($AuthorizationList.LocalizedDisplayName)'" -PercentComplete (($ProgressCount / $SUGResults) * 100) | |
| $AuthorizationList = [wmi]"$($AuthorizationList.__PATH)" | |
| $UpdatesCount = $AuthorizationList.Updates.Count | |
| $UpdatesList = New-Object -TypeName System.Collections.ArrayList | |
| $RemovedUpdatesList = New-Object -TypeName System.Collections.ArrayList | |
| # Enumerate each Software Update in current Software Update Group if eligible for removal | |
| foreach ($Update in ($AuthorizationList.Updates)) { | |
| if ($Update -notin $RemovableUpdatesList) { | |
| $UpdatesList.Add($Update) | Out-Null | |
| } | |
| else { | |
| $RemovedUpdatesList.Add($Update) | Out-Null | |
| } | |
| } | |
| # Update Software Update Group updates if count of objects in UpdatesList is less than before | |
| if ($UpdatesCount -gt $UpdatesList.Count) { | |
| try { | |
| if ($PSCmdlet.ShouldProcess("$($AuthorizationList.LocalizedDisplayName)","Clean '$($UpdatesCount - ($UpdatesList.Count))' updates")) { | |
| if ($UpdatesList.Count -ge 1) { | |
| $AuthorizationList.Updates = $UpdatesList | |
| $AuthorizationList.Put() | Out-Null | |
| Write-Verbose -Message "Successfully cleaned up $($UpdatesCount - ($UpdatesList.Count)) updates from '$($AuthorizationList.LocalizedDisplayName)'" | |
| } | |
| else { | |
| $AuthorizationList.Updates = @() | |
| $AuthorizationList.Put() | Out-Null | |
| Write-Verbose -Message "Successfully cleaned up all updates from '$($AuthorizationList.LocalizedDisplayName)'" | |
| } | |
| } | |
| # Remove content for each CI_ID in the RemovedUpdatesList array | |
| if ($PSBoundParameters["RemoveContent"]) { | |
| try { | |
| $DeploymentPackageList = New-Object -TypeName System.Collections.ArrayList | |
| foreach ($CI_ID in $RemovedUpdatesList) { | |
| Write-Verbose -Message "Collecting content data for CI_ID: $($CI_ID)" | |
| $ContentQuery = "SELECT SMS_PackageToContent.ContentID,SMS_PackageToContent.PackageID from SMS_PackageToContent JOIN SMS_CIToContent ON SMS_CIToContent.ContentID = SMS_PackageToContent.ContentID WHERE SMS_CIToContent.CI_ID IN ($($CI_ID))" | |
| $ContentData = Get-WmiObject -Namespace "rootSMSsite_$($SiteCode)" -Query $ContentQuery -ComputerName $SiteServer -ErrorAction Stop | |
| Write-Verbose -Message "Found '$(($ContentData | Measure-Object).Count)' objects" | |
| foreach ($Content in $ContentData) { | |
| $ContentID = $Content | Select-Object -ExpandProperty ContentID | |
| $PackageID = $Content | Select-Object -ExpandProperty PackageID | |
| $DeploymentPackage = [wmi]"$($SiteServer)rootSMSsite_$($SiteCode):SMS_SoftwareUpdatesPackage.PackageID='$($PackageID)'" | |
| if ($DeploymentPackage.PackageID -notin $DeploymentPackageList) { | |
| $DeploymentPackageList.Add($DeploymentPackage.PackageID) | Out-Null | |
| } | |
| if ($PSCmdlet.ShouldProcess("$($PackageID)","Remove ContentID '$($ContentID)'")) { | |
| Write-Verbose -Message "Attempting to remove ContentID '$($ContentID)' from PackageID '$($PackageID)'" | |
| $ReturnValue = $DeploymentPackage.RemoveContent($ContentID, $false) | |
| if ($ReturnValue.ReturnValue -eq 0) { | |
| Write-Verbose -Message "Successfully removed ContentID '$($ContentID)' from PackageID '$($PackageID)'" | |
| } | |
| } | |
| } | |
| } | |
| } | |
| catch [Exception] { | |
| Write-Warning -Message "An error occured when attempting to remove ContentID '$($ContentID)' from '$($PackageID)'" | |
| } | |
| } | |
| } | |
| catch [Exception] { | |
| Write-Warning -Message "Unable to save changes to '$($AuthorizationList.LocalizedDisplayName)'" ; break | |
| } | |
| } | |
| else { | |
| Write-Verbose -Message "No changes detected, will not update '$($AuthorizationList.LocalizedDisplayName)'" | |
| } | |
| # Refresh content source for all Deployment Packages in the DeploymentPackageList array | |
| if (($DeploymentPackageList.Count -ge 1) -and ($PSBoundParameters["RemoveContent"])) { | |
| foreach ($DPackageID in $DeploymentPackageList) { | |
| if ($PSCmdlet.ShouldProcess("$($DPackageID)","Refresh content source")) { | |
| $DPackage = [wmi]"$($SiteServer)rootSMSsite_$($SiteCode):SMS_SoftwareUpdatesPackage.PackageID='$($DPackageID)'" | |
| Write-Verbose -Message "Attempting to refresh content source for Deployment Package '$($DPackage.Name)'" | |
| $ReturnValue = $DPackage.RefreshPkgSource() | |
| if ($ReturnValue.ReturnValue -eq 0) { | |
| Write-Verbose -Message "Successfully refreshed content source for Deployment Package '$($DPackage.Name)'" | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| else { | |
| Write-Warning -Message "Unable to locate any Software Update Groups" | |
| } | |
| } | |
| catch [Exception] { | |
| Write-Error -Message $_.Exception.Message | |
| } | |
| } | |
| End { | |
| # Temporarily set ErrorActionPreference | |
| $ErrorActionPreference = "Continue" | |
| # Complete write progress activity | |
| if ($PSBoundParameters["ShowProgress"]) { | |
| Write-Progress -Activity "Processing Software Update Groups" -Completed -ErrorAction SilentlyContinue | |
| } | |
| # Output script exection time | |
| $StartTime.Stop() | |
| Write-Verbose -Message "Script execution: $($StartTime.Elapsed.Minutes) min and $($StartTime.Elapsed.Seconds) seconds" | |
| } |
Guardas el script y lo ejecutas de 2 modos:
Sólo eliminar los expirados, y el otro expirados y los sustituidos. Para ejecutar el script es importante reemplazar la variable SERVIDOR por la de vuestro servidor SCCM
#JUST EXPIRED
.\Clean-CMSoftwareUpdateGroups.ps1 -SiteServer SERVIDOR -Option ExpiredOnly -Verbose -RemoveContent -ShowProgress
#EXPIRED AND SUPERSEDED
.\Clean-CMSoftwareUpdateGroups.ps1 -SiteServer SERVIDOR -Option ExpiredSuperseded -Verbose -RemoveContent -ShowProgress
Hasta pronto!