<#
.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
Hi there.
Thank you for this solution. It works with my SCVMM 2012 R2.
I would like to schedule this script but it doesn’t work properly (Task Scheduler is configured to run this script as SYSTEM).
TS returns 0 (success) after running task manually, but SCVMM Jobs doesn’t show any changes….
Any ideas how to Schedule it?
Tried as vanilla as possible but still same result. Giving up and using full sync. But for reference if anyone else is searching and finds this post.
Get-SCUpdate -KBArticle 2884846 = False
Get-SCUpdateServer | Start-SCUpdateServerSynchronization
Get-SCUpdate -KBArticle 2884846 = False
Get-SCUpdateServer | Start-SCUpdateServerSynchronization -ForceFullUpdateCatalogImport
Get-SCUpdate -KBArticle 2884846 = True
Ok, well the workaround works for me. Too bad I couldn’t get confirmation that it wasn’t me that was doing someting wrong but. Might to some more test installs in different ways just for fun. Will let you know if I get different results.
I’m sorry Patrik but I’ve not been able to reproduce the problem in three different environments, or find any relevant logfiles to dig deeper into. Sorry
Same problem in UR6.
UR5
Will upgrade later today and see I it helps. Do you know if there is a log file for the synchronization ? Tried running with -debug but got nothing.
Hmm interesting. Are you using UR6?
I found the issue, at least i think.
If i do a Start-SCUpdateServerSynchronization it says it syncing but IsApproved status doesn’t update in vmm (get-scupdate). But if i do Start-SCUpdateServerSynchronization -ForceFullUpdateCatalogImport the IsApproved status is uppdated from Wsus.
Tried a couple og updates and I’m able to reproduce the issue every time.
It could be this line (54);
$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*”
It’s only checking the latest 500 updates in WSUS. And if there are no “Windows Server 2012*” updates in the last 500 (can be a lot of anti-virus definition updates) it won’t import anything. And if you don’t run the script often enough, it may miss updates due to that.
So I’ve changed that in my production environment to look like this;
$LatestUpdates = 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”
So it’s importing all updates for all products and not just checking the last 500.
Also as I noticed it would be miss for example IE and .NET Framework updates.
I’v approved the updates in WSUS and synced but noting gets to baselines.
Checked the updates in VMM with Get-SCUpdate and there they are all IsApproved = False.
If i manualy add an update to a baseline i can see with get-scupdate that it changes to IsApproved = true.
What am i missing ?