SCVMM (System Center: Virtual Machine Manager) 2012 and 2012R2 can manage the patch compliance on your servers. That’s a great feature but normally involves some manual work as you have to add each update to the Baselines manually.
My colleague Mikael Nyström (MVP) made a script to handle this automatically, which I’ve developed a bit further.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 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 |
<# .Synopsis Script to automatically keep SCVMM Baselines in sync with WSUS .DESCRIPTION Script that synchronizes WSUS Updates with SCVMM, both adding new updates and removes old inactive updates. .EXAMPLE Update-BaseLineUpdates $Baselinename # Author Current Author, Markus Lassfolk @Truesec Original Author, Mikael Nyström @Truesec # Version 1.2 Markus Lassfolk - Added section to remove inactive updates # Version 1.0 Markus Lassfolk - Initial Release # Version 0.5 Mikael Nyström #> Function Update-BaseLineUpdates{ Param ( [Parameter(Mandatory=$false, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, ValueFromRemainingArguments=$false, Position=0)] [String] $BaseLineName ) $baseline = Get-SCBaseline -Name $BaseLineName # Set-SCBaseline -Baseline $baseline -Name $BaseLineName -Description $BaseLineName -RemoveUpdates $baseline.Updates write-host $baseline.UpdateCount : Current number of Updates in Baseline $BaseLineName $addedUpdateList = "" $addedUpdateList = @() if ($baseline.UpdateCount -eq 0) { write-host "No previous updates in" $BaselineName", adding all existing updates for" $BaseLineName "from WSUS" $addedUpdateList += Get-SCUpdate | Where-Object -Property UpdateClassification -EQ -Value $BaseLineName | Where-Object -Property IsApproved -Like -Value "True" | Where-Object -Property IsDeclined -Like -Value "False"| Where-Object -Property IsExpired -Like -Value "False" | Where-Object -Property IsSuperseded -Like -Value "False" | Where-Object -Property Products -like "*Windows Server 2012*" write-host $addedUpdateList.Count ": New updates to add in" $Baseline Set-SCBaseline -Baseline $baseline -Name $BaseLineName -Description $BaseLineName -AddUpdates $addedUpdateList -RunAsynchronously } if ($baseline.UpdateCount -gt 0 ) { write-host "Scanning Newest 500 WSUS Updates for matching updates for $BaselineName" $LatestUpdates = Get-SCUpdate -Newest 500 | Where-Object -Property UpdateClassification -EQ -Value $BaseLineName | Where-Object -Property IsApproved -Like -Value "True" | Where-Object -Property IsDeclined -Like -Value "False"| Where-Object -Property IsExpired -Like -Value "False" | Where-Object -Property IsSuperseded -Like -Value "False" | Where-Object -Property Products -like "*Windows Server 2012*" write-host $LatestUpdates.Count ": Updates found, verifying if update(s) already exist in" $BaseLineName Compare-Object -ReferenceObject $baseline.Updates -DifferenceObject $LatestUpdates -IncludeEqual | % { if($_.SideIndicator -eq '=>') { $addedUpdateList += Get-SCUpdate -ID $_.inputobject.id } } write-host $addedUpdateList.Count : New updates to be added to SCVMM for $BaseLineName write-host $addedUpdateList | ft Set-SCBaseline -Baseline $baseline -Name $BaseLineName -Description $BaseLineName -AddUpdate $addedupdateList -RunAsynchronously } write-host "Scan WSUS for Updates that should not be Checked anymore" $remove = "" $remove = @() $removeUpdateList = "" $removeUpdateList = @() $remove += Get-SCUpdate | Where-Object -Property UpdateClassification -EQ -Value $BaseLineName | Where-Object -Property IsApproved -Like -Value "False" | Where-Object -Property Products -like "*Windows Server 2012*" $remove += Get-SCUpdate | Where-Object -Property UpdateClassification -EQ -Value $BaseLineName | Where-Object -Property IsDeclined -Like -Value "True"| Where-Object -Property Products -like "*Windows Server 2012*" $remove += Get-SCUpdate | Where-Object -Property UpdateClassification -EQ -Value $BaseLineName | Where-Object -Property IsExpired -Like -Value "True" | Where-Object -Property Products -like "*Windows Server 2012*" $remove += Get-SCUpdate | Where-Object -Property UpdateClassification -EQ -Value $BaseLineName | Where-Object -Property IsSuperseded -Like -Value "True" | Where-Object -Property Products -like "*Windows Server 2012*" write-host $remove.count "Remove Unapproved/Superseded/Expired/Declined updates" Compare-Object -ReferenceObject $baseline.Updates -DifferenceObject $remove -IncludeEqual | % { if($_.SideIndicator -eq '==') { $removeUpdateList += Get-SCUpdate -ID $_.inputobject.id } } Set-SCBaseline -Baseline $baseline -Name $BaseLineName -Description $BaseLineName -RemoveUpdates $RemoveupdateList } Function Add-BaseLine{ Param ( [Parameter(Mandatory=$false, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true, ValueFromRemainingArguments=$false, Position=0)] [String] $BaseLineName ) $baseline = New-SCBaseline -Name $BaseLineName -Description $BaseLineName $scope = Get-SCVMHostGroup -Name "All Hosts" Set-SCBaseline -Baseline $baseline -AddAssignmentScope $scope $scope2 = Get-SCVMMManagedComputer ForEach($Server in $scope2){ Set-SCBaseline -Baseline $baseline -Name $baseLine -AddAssignmentScope $Server } } Write-Host "Synchronizing with WSUS Server" Get-SCUpdateServer | Start-SCUpdateServerSynchronization . Update-BaseLineUpdates "Security Updates" . Update-BaseLineUpdates "Critical Updates" . Update-BaseLineUpdates "Updates" . Update-BaseLineUpdates "Update Rollups" #. Update-BaseLineUpdates "Definition Updates" #. Update-BaseLineUpdates "Service Packs" #. Update-BaseLineUpdates "Feature Packs" write-host "Start Compliance Scan for all Servers" Get-SCVMMManagedComputer | Start-SCComplianceScan |
The script has a few Pre-Requisites;
- A WSUS Server defined in SCVMM
- Approved patches for “Windows Server 2012” and “Windows Server 2012 R2” in WSUS
- Pre-Defined Baselines (you can use Add-Baseline to create them) with these names;
- Security Updates
- Critical Updates
- Updates
- Update Rollups
That’s it! You can now run the script and automatically import all matching updates.
The following actions will be performed;
- Synchronize updates with WSUS
- Check if there are any updates in the Baseline already
- If the baseline is empty, import ALL matching updates
- If the baseline is NOT empty, check the Newest 500 updates and import all matching updates
- Remove inactive updates
- Repeat for all Baselines
- Start a compliance scan
The script will not initiate any remediation. And as the script normally only checks the newest 500 updates, it has to be run fairly regular. In my environment, 500 updates is about 1 month of updates. Though to be safe, run it once a week.