Scripts/Optimize-WsusContents.ps1
#Requires -Version 5.0 <# .Synopsis Cleanup wsus contents .Description The task schedule automatically deletes the old version. .Parameter ConfigPath Set ODT config path Create from 'Show-WsustainableSettingsView' .Parameter FistLaunch FistLaunch mode .Parameter Verbose Display verbose message .Example # Simply command sample Optimize-WsusContents -ConfigFileName 'C:\ProgramData\Wsustainable\0.1\Config.json' #> Function Optimize-WsusContents{ Param( [Parameter(Mandatory)][String]$ConfigPath, [Switch]$FistLaunch ) Function Global:Get-SqlCmdPath{ $Path = (Get-ChildItem "$($env:ProgramFiles)\Microsoft SQL Server\Client SDK\ODBC\" -Recurse -Filter "sqlcmd.exe" -File | Sort-Object FullName -Descending | Select-Object -First 1).FullName If ($Path -eq $Null){ $Path = $DefaultConfig.MaintenanceSql.SqlCmdPath } Return $Path } Function Global:Get-WsusSqlServerName{ If (Test-Path "HKLM:\SOFTWARE\Microsoft\Update Services\Server\Setup"){ $ServerInstancePath = (Get-Item -Path "HKLM:\SOFTWARE\Microsoft\Update Services\Server\Setup").GetValue("SqlServerName") If ($ServerInstancePath -eq "MICROSOFT##WID"){ $ServerInstancePath = "\\.\pipe\Microsoft##WID\tsql\query" } Return $ServerInstancePath } } Function Deny-WsusFilteredUpdates($DeclineRule, $UpdateScope, $RetryCount, $Month, $FindMode){ $Global:UpdatesResult = @() Get-WsusFilteredUpdates $DeclineRule $UpdateScope $RetryCount $Month $FindMode If (-not [String]::IsNullOrEmpty($DeclineRule.RegexMode)){ Write-Verbose "[Deny-WsusFilteredUpdates] RegexMode: $($DeclineRule.RegexMode)" switch ($DeclineRule.RegexMode){ "Decline All" { $Updates = @($UpdatesResult | Where-Object Title -like $DeclineRule.Filter) Write-Verbose "$(Get-Date -Format F): [Decline All] Updates.Count: $($Updates.Count) / UpdatesResult.Count: $($UpdatesResult.Count)" Write-Verbose "$(Get-Date -Format F): [Decline All] Filter: $($DeclineRule.Filter)" $DeclineUpdateCount = 0 $ActionDetails = "" $Updates | ForEach-Object{ $Update = $_ $Update.Decline() | Out-Null $DeclineUpdateCount++ If ($CurrentConfig.Log.IsLogging){ If ($CurrentConfig.Log.Verbose){ $Update | Select-Object Title, @{Name="ProductTitles";Expression={($_.ProductTitles -Join "`n")}}, CreationDate, LegacyName, @{Name="Id.RevisionNumber";Expression={($_.Id.RevisionNumber -Join "`n")}}, @{Name="Id.UpdateId";Expression={($_.Id.UpdateId -Join "`n")}}, @{Name="KnowledgebaseArticles";Expression={($_.KnowledgebaseArticles -Join "`n")}}, @{Name="SecurityBulletins";Expression={($_.SecurityBulletins -Join "`n")}}, UpdateClassificationTitle, @{Name="ProductFamilyTitles";Expression={($_.ProductFamilyTitles -Join "`n")}}, UpdateType, @{Name="Action";Expression={$ActionDetails}} | Export-Csv -Path (Join-Path $LogDirectory "$LogFileName.csv") -Encoding UTF8 -NoTypeInformation -Append | Out-Null } Else{ $Update | Select-Object Title, LegacyName, UpdateClassificationTitle | Export-Csv -Path (Join-Path $LogDirectory "$LogFileName.csv") -Encoding UTF8 -NoTypeInformation -Append | Out-Null } } } Write-Verbose "$(Get-Date -Format F): [Decline All] Denied: $DeclineUpdateCount" } "Windows 11 FU Exclude Languages" { $Updates = @($UpdatesResult | Where-Object {$_.Title -match $DeclineRule.Filter} | Select-Object @{Name="Item";Expression={$_}}, @{Name="Language";Expression={$_.Title -match $DeclineRule.Filter | Out-Null; Return $Matches["Language"]}} | Where-Object Language -ne $Null) Write-Verbose "$(Get-Date -Format F): [Windows 11 FU Exclude Languages] Updates.Count: $($Updates.Count) / UpdatesResult.Count: $($UpdatesResult.Count)" Write-Verbose "$(Get-Date -Format F): [Windows 11 FU Exclude Languages] Filter: $($DeclineRule.Filter)" Write-Verbose "$(Get-Date -Format F): [Windows 11 FU Exclude Languages] Exclude Languages:$($CurrentConfig.ChooseProducts.'Windows 11'.ExcludeLanguages)" $DeclineUpdateCount = 0 $ActionDetails = "" $Languages = ($CurrentConfig.ChooseProducts.'Windows 11'.ExcludeLanguages -split ",") $Updates | Where-Object Language -notin $Languages | ForEach-Object{ $Update = $_.Item $ActionDetails = "$($Update.Language) -notin $($CurrentConfig.ChooseProducts.'Windows 11'.ExcludeLanguages)" $Update.Decline() | Out-Null $DeclineUpdateCount++ If ($CurrentConfig.Log.IsLogging){ If ($CurrentConfig.Log.Verbose){ $Update | Select-Object Title, @{Name="ProductTitles";Expression={($_.ProductTitles -Join "`n")}}, CreationDate, LegacyName, @{Name="Id.RevisionNumber";Expression={($_.Id.RevisionNumber -Join "`n")}}, @{Name="Id.UpdateId";Expression={($_.Id.UpdateId -Join "`n")}}, @{Name="KnowledgebaseArticles";Expression={($_.KnowledgebaseArticles -Join "`n")}}, @{Name="SecurityBulletins";Expression={($_.SecurityBulletins -Join "`n")}}, UpdateClassificationTitle, @{Name="ProductFamilyTitles";Expression={($_.ProductFamilyTitles -Join "`n")}}, UpdateType, @{Name="Action";Expression={$ActionDetails}} | Export-Csv -Path (Join-Path $LogDirectory "$LogFileName.csv") -Encoding UTF8 -NoTypeInformation -Append | Out-Null } Else{ $Update | Select-Object Title, LegacyName, UpdateClassificationTitle | Export-Csv -Path (Join-Path $LogDirectory "$LogFileName.csv") -Encoding UTF8 -NoTypeInformation -Append | Out-Null } } } Write-Verbose "$(Get-Date -Format F): [Windows 11 FU Exclude Languages] Denied: $DeclineUpdateCount" } "Windows 10 FU Exclude Languages" { $Updates = @($UpdatesResult | Where-Object {$_.Title -match $DeclineRule.Filter} | Select-Object @{Name="Item";Expression={$_}}, @{Name="Language";Expression={$_.Title -match $DeclineRule.Filter | Out-Null; Return $Matches["Language"]}} | Where-Object Language -ne $Null) Write-Verbose "$(Get-Date -Format F): [Windows 10 FU Exclude Languages] Updates.Count: $($Updates.Count) / UpdatesResult.Count: $($UpdatesResult.Count)" Write-Verbose "$(Get-Date -Format F): [Windows 10 FU Exclude Languages] Filter: $($DeclineRule.Filter)" Write-Verbose "$(Get-Date -Format F): [Windows 10 FU Exclude Languages] Exclude Languages:$($CurrentConfig.ChooseProducts.'Windows 10'.ExcludeLanguages)" $DeclineUpdateCount = 0 $ActionDetails = "" $Languages = ($CurrentConfig.ChooseProducts.'Windows 10'.ExcludeLanguages -split ",") $Updates | Where-Object Language -notin $Languages | ForEach-Object{ $Update = $_.Item $ActionDetails = "$($Update.Language) -notin $($CurrentConfig.ChooseProducts.'Windows 10'.ExcludeLanguages)" $Update.Decline() | Out-Null $DeclineUpdateCount++ If ($CurrentConfig.Log.IsLogging){ If ($CurrentConfig.Log.Verbose){ $Update | Select-Object Title, @{Name="ProductTitles";Expression={($_.ProductTitles -Join "`n")}}, CreationDate, LegacyName, @{Name="Id.RevisionNumber";Expression={($_.Id.RevisionNumber -Join "`n")}}, @{Name="Id.UpdateId";Expression={($_.Id.UpdateId -Join "`n")}}, @{Name="KnowledgebaseArticles";Expression={($_.KnowledgebaseArticles -Join "`n")}}, @{Name="SecurityBulletins";Expression={($_.SecurityBulletins -Join "`n")}}, UpdateClassificationTitle, @{Name="ProductFamilyTitles";Expression={($_.ProductFamilyTitles -Join "`n")}}, UpdateType, @{Name="Action";Expression={$ActionDetails}} | Export-Csv -Path (Join-Path $LogDirectory "$LogFileName.csv") -Encoding UTF8 -NoTypeInformation -Append | Out-Null } Else{ $Update | Select-Object Title, LegacyName, UpdateClassificationTitle | Export-Csv -Path (Join-Path $LogDirectory "$LogFileName.csv") -Encoding UTF8 -NoTypeInformation -Append | Out-Null } } } Write-Verbose "$(Get-Date -Format F): [Windows 10 FU Exclude Languages] Denied: $DeclineUpdateCount" } "Decline Old Version" { $Updates = @($UpdatesResult | Where-Object {$_.Title -match $DeclineRule.Filter} | Select-Object @{Name="Item";Expression={$_}}, @{Name="Version";Expression={$_.Title -match $DeclineRule.Filter | Out-Null; Return $Matches["Version"]}} | Where-Object Version -ne $Null) $LatestVersion = @($Updates | Sort-Object {[Version]$_.Version} -Descending)[0].Version Write-Verbose "$(Get-Date -Format F): [Decline Old Version] Updates.Count: $($Updates.Count) / UpdatesResult.Count: $($UpdatesResult.Count)" Write-Verbose "$(Get-Date -Format F): [Decline Old Version] Filter: $($DeclineRule.Filter)" Write-Verbose "$(Get-Date -Format F): [Decline Old Version] LatestVersion: $LatestVersion" $DeclineUpdateCount = 0 $ActionDetails = "" $Updates | Where-Object Version -ne $LatestVersion | ForEach-Object{ $Update = $_.Item $ActionDetails = "$($Update.Version) < $LatestVersion" $Update.Decline() | Out-Null $DeclineUpdateCount++ If ($CurrentConfig.Log.IsLogging){ If ($CurrentConfig.Log.Verbose){ $Update | Select-Object Title, @{Name="ProductTitles";Expression={($_.ProductTitles -Join "`n")}}, CreationDate, LegacyName, @{Name="Id.RevisionNumber";Expression={($_.Id.RevisionNumber -Join "`n")}}, @{Name="Id.UpdateId";Expression={($_.Id.UpdateId -Join "`n")}}, @{Name="KnowledgebaseArticles";Expression={($_.KnowledgebaseArticles -Join "`n")}}, @{Name="SecurityBulletins";Expression={($_.SecurityBulletins -Join "`n")}}, UpdateClassificationTitle, @{Name="ProductFamilyTitles";Expression={($_.ProductFamilyTitles -Join "`n")}}, UpdateType, @{Name="Action";Expression={$ActionDetails}} | Export-Csv -Path (Join-Path $LogDirectory "$LogFileName.csv") -Encoding UTF8 -NoTypeInformation -Append | Out-Null } Else{ $Update | Select-Object Title, LegacyName, UpdateClassificationTitle | Export-Csv -Path (Join-Path $LogDirectory "$LogFileName.csv") -Encoding UTF8 -NoTypeInformation -Append | Out-Null } } } Write-Verbose "$(Get-Date -Format F): [Decline Old Version] Denied: $DeclineUpdateCount" } default { Write-Warning "[Deny-WsusFilteredUpdates] Not supported RegexMode: $($DeclineRule.RegexMode)" } } } } Function Get-WsusFilteredUpdates($DeclineRule, $UpdateScope, $RetryCount, $Month, $FindMode){ $DeclineUpdateCount = 0 If ($RetryCount -gt $CurrentConfig.UpdatesFindMode.MaximumRetry){ Write-Verbose "$(Get-Date -Format F): [Get-WsusFilteredUpdates] skiped" Return } If (($CurrentConfig.UpdatesFindMode | Get-Member -Name 'ForceHalfModePerMonthLength').Count -eq 1){ If ($Month -ne $Null){ $UpdateScope.FromCreationDate = $Month $UpdateScope.ToCreationDate = $Month.AddMonths(1).AddSeconds(-1) } $UpdateCount = $WsusServer.GetUpdateCount($UpdateScope) If ($UpdateCount -eq 0){ Switch ($FindMode){ ([UpdateFindMode]::H1){ Write-Verbose "$(Get-Date -Format F): [Get-WsusFilteredUpdates] Not found. $LogFileName updates $($Month.ToString("y")) (1/5)" } ([UpdateFindMode]::H2){ Write-Verbose "$(Get-Date -Format F): [Get-WsusFilteredUpdates] Not found. $LogFileName updates $($Month.ToString("y")) (2/5)" } ([UpdateFindMode]::H3){ Write-Verbose "$(Get-Date -Format F): [Get-WsusFilteredUpdates] Not found. $LogFileName updates $($Month.ToString("y")) (3/5)" } ([UpdateFindMode]::H4){ Write-Verbose "$(Get-Date -Format F): [Get-WsusFilteredUpdates] Not found. $LogFileName updates $($Month.ToString("y")) (4/5)" } ([UpdateFindMode]::H5){ Write-Verbose "$(Get-Date -Format F): [Get-WsusFilteredUpdates] Not found. $LogFileName updates $($Month.ToString("y")) (5/5)" } ([UpdateFindMode]::Full){ Write-Verbose "$(Get-Date -Format F): [Get-WsusFilteredUpdates] Not found. $LogFileName updates $($Month.ToString("y"))" } Default{ Write-Verbose "$(Get-Date -Format F): [Get-WsusFilteredUpdates] Not found. $LogFileName" } } Return } ElseIf (($FindMode -eq $Null) -and ($UpdateCount -ge $CurrentConfig.UpdatesFindMode.ForceHalfModePerMonthLength)){ Write-Verbose "$(Get-Date -Format F): [Get-WsusFilteredUpdates] Switch to split mode. $LogFileName updates count: $UpdateCount" ForEach($Month in $Months){ Get-WsusFilteredUpdates $DeclineRule $UpdateScope $RetryCount $Month ([UpdateFindMode]::Full) } Return } ElseIf (($Month -ne $Null) -and ($FindMode -eq ([UpdateFindMode]::Full)) -and ($UpdateCount -ge $CurrentConfig.UpdatesFindMode.ForceHalfModePerMonthLength)){ Write-Verbose "$(Get-Date -Format F): [Get-WsusFilteredUpdates] Switch to split mode. $LogFileName updates count: $UpdateCount date: $($Month.ToString("y"))" Get-WsusFilteredUpdates $DeclineRule $UpdateScope $RetryCount $Month ([UpdateFindMode]::H1) Return } } Switch ($FindMode){ ([UpdateFindMode]::H1){ $UpdateScope.FromCreationDate = $Month $UpdateScope.ToCreationDate = $Month.AddDays(8).AddSeconds(-1) Write-Verbose "$(Get-Date -Format F): [Get-WsusFilteredUpdates] $LogFileName updates $($Month.ToString("y")) (1/5) Found: $($WsusServer.GetUpdateCount($UpdateScope))" } ([UpdateFindMode]::H2){ $UpdateScope.FromCreationDate = $Month.AddDays(7) $UpdateScope.ToCreationDate = $Month.AddDays(15).AddSeconds(-1) Write-Verbose "$(Get-Date -Format F): [Get-WsusFilteredUpdates] $LogFileName updates $($Month.ToString("y")) (2/5) Found: $($WsusServer.GetUpdateCount($UpdateScope))" } ([UpdateFindMode]::H3){ $UpdateScope.FromCreationDate = $Month.AddDays(14) $UpdateScope.ToCreationDate = $Month.AddDays(22).AddSeconds(-1) Write-Verbose "$(Get-Date -Format F): [Get-WsusFilteredUpdates] $LogFileName updates $($Month.ToString("y")) (3/5) Found: $($WsusServer.GetUpdateCount($UpdateScope))" } ([UpdateFindMode]::H4){ $UpdateScope.FromCreationDate = $Month.AddDays(21) $UpdateScope.ToCreationDate = $Month.AddDays(28).AddSeconds(-1) Write-Verbose "$(Get-Date -Format F): [Get-WsusFilteredUpdates] $LogFileName updates $($Month.ToString("y")) (4/5) Found: $($WsusServer.GetUpdateCount($UpdateScope))" } ([UpdateFindMode]::H5){ $UpdateScope.FromCreationDate = $Month.AddDays(28) $UpdateScope.ToCreationDate = $Month.AddMonths(1).AddSeconds(-1) Write-Verbose "$(Get-Date -Format F): [Get-WsusFilteredUpdates] $LogFileName updates $($Month.ToString("y")) (5/5) Found: $($WsusServer.GetUpdateCount($UpdateScope))" } ([UpdateFindMode]::Full){ Write-Verbose "$(Get-Date -Format F): [Get-WsusFilteredUpdates] $LogFileName updates $($Month.ToString("y")) Found: $UpdateCount" } Default{ Write-Verbose "$(Get-Date -Format F): [Get-WsusFilteredUpdates] $LogFileName updates Found: $UpdateCount" } } Try { $Updates = $WsusServer.GetUpdates($UpdateScope) If ([String]::IsNullOrEmpty($DeclineRule.RegexMode)){ Write-Verbose "[Deny-WsusFilteredUpdates] Find TextIncludes (NotRegexMode)" $Updates = $Updates | ForEach-Object { $ActionDetails = "" $Update = $_ $DeclineUpdateCount++ $Update.Decline() If ($CurrentConfig.Log.IsLogging){ If ($CurrentConfig.Log.Verbose){ $Update | Select-Object Title, @{Name="ProductTitles";Expression={($_.ProductTitles -Join "`n")}}, CreationDate, LegacyName, @{Name="Id.RevisionNumber";Expression={($_.Id.RevisionNumber -Join "`n")}}, @{Name="Id.UpdateId";Expression={($_.Id.UpdateId -Join "`n")}}, @{Name="KnowledgebaseArticles";Expression={($_.KnowledgebaseArticles -Join "`n")}}, @{Name="SecurityBulletins";Expression={($_.SecurityBulletins -Join "`n")}}, UpdateClassificationTitle, @{Name="ProductFamilyTitles";Expression={($_.ProductFamilyTitles -Join "`n")}}, UpdateType, @{Name="Action";Expression={$ActionDetails}} | Export-Csv -Path (Join-Path $LogDirectory "$LogFileName.csv") -Encoding UTF8 -NoTypeInformation -Append | Out-Null } Else{ $Update | Select-Object Title, LegacyName, UpdateClassificationTitle, @{Name="Action";Expression={$ActionDetails}} | Export-Csv -Path (Join-Path $LogDirectory "$LogFileName.csv") -Encoding UTF8 -NoTypeInformation -Append | Out-Null } } } } $Global:UpdatesResult += $Updates } Catch{ Write-Warning "$(Get-Date -Format F): [Get-WsusFilteredUpdates] $LogFileName updatesを取得できなかったため再試行します`n$($_.Exception)" $RetryCount++ Get-WsusFilteredUpdates $DeclineRule $UpdateScope $RetryCount $Month $FindMode } Switch ($FindMode){ ([UpdateFindMode]::H1){ Write-Verbose "$(Get-Date -Format F): [Get-WsusFilteredUpdates] $LogFileName updates $($Month.ToString("y")) (1/5) Target: $DeclineUpdateCount" Get-WsusFilteredUpdates $DeclineRule $UpdateScope $RetryCount $Month ([UpdateFindMode]::H2) } ([UpdateFindMode]::H2){ Write-Verbose "$(Get-Date -Format F): [Get-WsusFilteredUpdates] $LogFileName updates $($Month.ToString("y")) (2/5) Target: $DeclineUpdateCount" Get-WsusFilteredUpdates $DeclineRule $UpdateScope $RetryCount $Month ([UpdateFindMode]::H3) } ([UpdateFindMode]::H3){ Write-Verbose "$(Get-Date -Format F): [Get-WsusFilteredUpdates] $LogFileName updates $($Month.ToString("y")) (3/5) Target: $DeclineUpdateCount" Get-WsusFilteredUpdates $DeclineRule $UpdateScope $RetryCount $Month ([UpdateFindMode]::H4) } ([UpdateFindMode]::H4){ Write-Verbose "$(Get-Date -Format F): [Get-WsusFilteredUpdates] $LogFileName updates $($Month.ToString("y")) (4/5) Target: $DeclineUpdateCount" Get-WsusFilteredUpdates $DeclineRule $UpdateScope $RetryCount $Month ([UpdateFindMode]::H5) } ([UpdateFindMode]::H5){ Write-Verbose "$(Get-Date -Format F): [Get-WsusFilteredUpdates] $LogFileName updates $($Month.ToString("y")) (5/5) Target: $DeclineUpdateCount" } ([UpdateFindMode]::Full){ Write-Verbose "$(Get-Date -Format F): [Get-WsusFilteredUpdates] $LogFileName updates $($Month.ToString("y")) Target: $DeclineUpdateCount" } Default{ Write-Verbose "$(Get-Date -Format F): [Get-WsusFilteredUpdates] $LogFileName updates Target: $DeclineUpdateCount" } } Return } Function Stop-WsusSynchronization($WsusServer, $TimeOut){ If ($TimeOut -eq $Null){ $TimeOut = (Get-Date).AddMinutes(10) } Switch ($WsusServer.GetSubscription().GetSynchronizationStatus()){ ([Microsoft.UpdateServices.Administration.SynchronizationStatus]::NotProcessing){ Write-Verbose "$(Get-Date -Format F): [Stop-WsusSynchronization] Already stopped." } ([Microsoft.UpdateServices.Administration.SynchronizationStatus]::Running){ Write-Verbose "$(Get-Date -Format F): [Stop-WsusSynchronization] Stopping now..." $WsusServer.GetSubscription().StopSynchronization() Start-Sleep -Seconds 5 Stop-WsusSynchronization -WsusServer $WsusServer -TimeOut $TimeOut } ([Microsoft.UpdateServices.Administration.SynchronizationStatus]::Stopping){ If ((Get-Date) -ge $TimeOut){ Write-Error "$(Get-Date -Format F): [Stop-WsusSynchronization] Timed out." } Else{ Write-Verbose "$(Get-Date -Format F): [Stop-WsusSynchronization] Stopping now..." Start-Sleep -Seconds 10 Stop-WsusSynchronization -WsusServer $WsusServer -TimeOut $TimeOut } } } } # Load config If ([String]::IsNullOrWhiteSpace($ConfigPath)){ $TestConfigPathResult = $False } Else{ $TestConfigPathResult = (Test-Path $ConfigPath -PathType Leaf) } If ($TestConfigPathResult){ $Global:CurrentConfig = Get-Content $ConfigPath -Encoding UTF8 | ConvertFrom-Json }Else{ Write-Error ([System.IO.FileNotFoundException]::new("Not found config. Check access to $ConfigPath")) -ErrorAction Stop } # Logging . (Join-Path (Get-Module Wsustainable).ModuleBase "Scripts\LogManager.ps1") Initialize-Directories Start-Logging # Initialize config $Global:CurrentConfig = Get-DeclineRules -Config $CurrentConfig If ($Verbose -or $CurrentConfig.Log.Verbose){ $CurrentConfig | ConvertTo-Json -Depth 10 | Out-File -FilePath (Join-Path $LogDirectory "InternalConfig.json") -Encoding UTF8 } If (@($CurrentConfig.DeclineRules).Count -eq 0){ Write-Warning "Not found decline rules" } # Load assemblies Import-Module UpdateServices If (@(Get-Module –Name UpdateServices).Count -eq 0){ Write-Error "このスクリプトの動作に必要な UpdateServices が見つかりませんでした" } # Connect to WSUS server $WsusServer = Get-WsusServer -Name $CurrentConfig.Wsus.Server -PortNumber $CurrentConfig.Wsus.Port If ($WsusServer -eq $Null){ Write-Error "WSUS サーバーに接続できませんでした" -ErrorAction Stop Break } If ($CurrentConfig.Wsus.PreferredCulture -ne $Null){ $WsusServer.PreferredCulture = $CurrentConfig.Wsus.PreferredCulture } Stop-WsusSynchronization -WsusServer $WsusServer $ApprovedStates = [Microsoft.UpdateServices.Administration.ApprovedStates]::unknown If ($FistLaunch){ @($CurrentConfig.FistLaunch.ApprovedStates) | ForEach-Object { $ApprovedStates = $ApprovedStates -bor [Microsoft.UpdateServices.Administration.ApprovedStates]::$_ } } Else{ @($CurrentConfig.UpdatesFindMode.ApprovedStates) | ForEach-Object { $ApprovedStates = $ApprovedStates -bor [Microsoft.UpdateServices.Administration.ApprovedStates]::$_ } } Write-Verbose "$(Get-Date -Format F): ApprovedStates is $ApprovedStates" If ($ApprovedStates -eq [Microsoft.UpdateServices.Administration.ApprovedStates]::unknown){ Write-Verbose "$(Get-Date -Format F): ApprovedStates is NotApproved" $ApprovedStates = [Microsoft.UpdateServices.Administration.ApprovedStates]::NotApproved } $CurrentDate = [datetime]$CurrentConfig.UpdatesFindMode.MinimumDate $Months = @() Do{ $Months += $CurrentDate $CurrentDate = $CurrentDate.AddMonths(1) } While($CurrentDate -lt (Get-Date -Day 1)) $UpdateClassification = $WsusServer.GetUpdateClassifications() @($CurrentConfig.DeclineRules) | ForEach-Object { $DeclineRule = $_ Write-Verbose "`n[DeclineRule] RegexMode: $($_.RegexMode), Product: $($_.Product), Version: $($_.Version), Type: $($_.Type), Architecture: $($_.Architecture)`n" # generate logfilename $Global:LogFileName = "$($_.Product)-$($_.Version)-$($_.Architecture)-$($_.Type)-$($_.RegexMode)" [System.IO.Path]::GetInvalidFileNameChars() | ForEach-Object {$Global:LogFileName = $LogFileName.Replace("$_","")} $UpdateScope = New-Object Microsoft.UpdateServices.Administration.UpdateScope Try{ $UpdateScope.ApprovedStates = $ApprovedStates @($DeclineRule.TargetProductId -split "`n") | ForEach-Object { $Category = $WsusServer.GetUpdateCategory($_) If ($Category -ne $Null){ $TargetProductTitle = $Category.Title Write-Verbose "[DeclineRule] TargetProductTitle: $TargetProductTitle, TargetProductId: $_" $UpdateScope.Categories.Add($Category) | Out-Null }Else{ Write-Warning "[DeclineRule] Not found TargetProductId: $_" } } @($DeclineRule.TargetClassifications -split "`n") | ForEach-Object { $UpdateClassification | Where-Object Id -eq $_ | ForEach-Object {$UpdateScope.Classifications.Add($_) | Out-Null} } Try{ If (-not $_.RegexMode -and $CurrentConfig.ChooseProducts.($_.Product).FilterType -eq "Title"){ Write-Verbose "[DeclineRule] Set to TextIncludes: $($_.Filter)" $UpdateScope.TextIncludes = $_.Filter } $Script:LatestDeclineUpdate = $Null Deny-WsusFilteredUpdates -DeclineRule $DeclineRule -UpdateScope $UpdateScope -RetryCount 0 } Catch{ Write-Error "$(Get-Date -Format F): $LogFileName `n$($_.Exception)" } } Catch{ Write-Error "$(Get-Date -Format F): UpdateScope error $LogFileName `n$($_.Exception)" } } If ($CurrentConfig.DeclineOptions.CleanupWizard.CompressUpdates){ #CleanupWizard: 不要な更新および更新のリビジョン Write-Verbose "[CleanupWizard] CompressUpdates: $($WsusServer | Invoke-WsusServerCleanup -CompressUpdates)" } If ($CurrentConfig.DeclineOptions.CleanupWizard.CleanupObsoleteUpdates){ #CleanupWizard: 期限の切れた更新 Write-Verbose "[CleanupWizard] CleanupObsoleteUpdates: $($WsusServer | Invoke-WsusServerCleanup -CleanupObsoleteUpdates)" } If ($CurrentConfig.DeclineOptions.CleanupWizard.CleanupObsoleteComputers){ #CleanupWizard: サーバーにアクセスしていないコンピューター Write-Verbose "[CleanupWizard] CleanupObsoleteComputers: $($WsusServer | Invoke-WsusServerCleanup -CleanupObsoleteComputers)" } If ($CurrentConfig.DeclineOptions.CleanupWizard.CleanupUnneededContentFiles){ #CleanupWizard: 不要な更新ファイル Write-Verbose "[CleanupWizard] CleanupUnneededContentFiles: $($WsusServer | Invoke-WsusServerCleanup -CleanupUnneededContentFiles)" } If ($CurrentConfig.DeclineOptions.CleanupWizard.DeclineExpiredUpdates){ #CleanupWizard: 期限の切れた更新 Write-Verbose "[CleanupWizard] DeclineExpiredUpdates: $($WsusServer | Invoke-WsusServerCleanup -DeclineExpiredUpdates)" } If ($CurrentConfig.DeclineOptions.CleanupWizard.DeclineSupersededUpdates){ #CleanupWizard: 置き換えられた更新 Write-Verbose "[CleanupWizard] DeclineSupersededUpdates: $($WsusServer | Invoke-WsusServerCleanup -DeclineSupersededUpdates)" } If ($CurrentConfig.DeclineOptions.ForceDeclineSupersededUpdates){ #置き換えられた更新 (強制) $UpdateScope = New-Object Microsoft.UpdateServices.Administration.UpdateScope $UpdateScope.ApprovedStates = [Microsoft.UpdateServices.Administration.ApprovedStates]::NotApproved $ActionDetails = "" $WsusServer.GetUpdates($UpdateScope) | Where-Object IsSuperseded | ForEach-Object { Try{ $_.Decline() If ($CurrentConfig.Log.IsLogging){ If ($CurrentConfig.Log.Verbose){ $Update | Select-Object Title, @{Name="ProductTitles";Expression={($_.ProductTitles -Join "`n")}}, CreationDate, LegacyName, @{Name="Id.RevisionNumber";Expression={($_.Id.RevisionNumber -Join "`n")}}, @{Name="Id.UpdateId";Expression={($_.Id.UpdateId -Join "`n")}}, @{Name="KnowledgebaseArticles";Expression={($_.KnowledgebaseArticles -Join "`n")}}, @{Name="SecurityBulletins";Expression={($_.SecurityBulletins -Join "`n")}}, UpdateClassificationTitle, @{Name="ProductFamilyTitles";Expression={($_.ProductFamilyTitles -Join "`n")}}, UpdateType, @{Name="Action";Expression={$ActionDetails}} | Export-Csv -Path (Join-Path $LogDirectory "ForceDeclineSupersededUpdates.csv") -Encoding UTF8 -NoTypeInformation -Append | Out-Null } Else{ If ($IsMatch){ $Update | Select-Object Title, LegacyName, UpdateClassificationTitle, @{Name="Action";Expression={$ActionDetails}} | Export-Csv -Path (Join-Path $LogDirectory "DeclineSupersededUpdates.csv") -Encoding UTF8 -NoTypeInformation -Append | Out-Null } } } } Catch{ Write-Error "$(Get-Date -Format F): 更新プログラムを取得できませんでした`n$($_.Exception)" } } } If ($CurrentConfig.MaintenanceSql.SqlCmdMode -eq "psmodule"){ If ($CurrentConfig.MaintenanceSql.UpdateStatisticsAndDbccDbReIndex){ Write-Verbose "[psmodule] UpdateStatisticsAndDbccDbReIndex: $(Invoke-Sqlcmd -ServerInstance (Get-WsusSqlServerName) -InputFile (Join-Path $PSScriptRoot '..\Assets\UpdateStatisticsAndDbccDbReIndex.sql') -Encrypt Optional)" } If ($CurrentConfig.MaintenanceSql.WsusDBMaintenance){ Write-Verbose "[psmodule] WsusDBMaintenance: $(Invoke-Sqlcmd -ServerInstance (Get-WsusSqlServerName) -InputFile (Join-Path $PSScriptRoot '..\Assets\WsusDBMaintenance.sql') -Encrypt Optional)" } } Else{ If ($CurrentConfig.MaintenanceSql.UpdateStatisticsAndDbccDbReIndex){ Write-Verbose "[$($CurrentConfig.MaintenanceSql.SqlCmdMode)] UpdateStatisticsAndDbccDbReIndex: $((Start-Process (Get-SqlCmdPath) -ArgumentList "-S $(Get-WsusSqlServerName) -i ""$(Join-Path $PSScriptRoot '..\Assets\UpdateStatisticsAndDbccDbReIndex.sql')"" -Wait -NoNewWindow -PassThru").ExitCode)" } If ($CurrentConfig.MaintenanceSql.WsusDBMaintenance){ Write-Verbose "[$($CurrentConfig.MaintenanceSql.SqlCmdMode)] WsusDBMaintenance: $((Start-Process (Get-SqlCmdPath) -ArgumentList "-S $(Get-WsusSqlServerName) -i ""$(Join-Path $PSScriptRoot '..\Assets\WsusDBMaintenance.sql')"" -Wait -NoNewWindow -PassThru").ExitCode)" } } If ($CurrentConfig.Log.IsLogging){ If ($CurrentConfig.Log.Verbose){ $UpdateScope = New-Object Microsoft.UpdateServices.Administration.UpdateScope $UpdateScope.ApprovedStates = [Microsoft.UpdateServices.Administration.ApprovedStates]::NotApproved $ActionDetails = "" Try{ $WsusServer.GetUpdates($UpdateScope) | Select-Object Title, @{Name="ProductTitles";Expression={($_.ProductTitles -Join "`n")}}, CreationDate, LegacyName, @{Name="Id.RevisionNumber";Expression={($_.Id.RevisionNumber -Join "`n")}}, @{Name="Id.UpdateId";Expression={($_.Id.UpdateId -Join "`n")}}, @{Name="KnowledgebaseArticles";Expression={($_.KnowledgebaseArticles -Join "`n")}}, @{Name="SecurityBulletins";Expression={($_.SecurityBulletins -Join "`n")}}, UpdateClassificationTitle, @{Name="ProductFamilyTitles";Expression={($_.ProductFamilyTitles -Join "`n")}}, UpdateType, @{Name="Action";Expression={$ActionDetails}} | Export-Csv -Path (Join-Path $LogDirectory "NotApproved.csv") -Encoding UTF8 -NoTypeInformation -Append | Out-Null } Catch{ Write-Error "$(Get-Date -Format F): [NotApproved] 更新プログラムを取得できませんでした`n$($_.Exception)" } $UpdateScope = New-Object Microsoft.UpdateServices.Administration.UpdateScope $UpdateScope.ApprovedStates = [Microsoft.UpdateServices.Administration.ApprovedStates]::HasStaleUpdateApprovals $ActionDetails = "" Try{ $WsusServer.GetUpdates($UpdateScope) | Select-Object Title, @{Name="ProductTitles";Expression={($_.ProductTitles -Join "`n")}}, CreationDate, LegacyName, @{Name="Id.RevisionNumber";Expression={($_.Id.RevisionNumber -Join "`n")}}, @{Name="Id.UpdateId";Expression={($_.Id.UpdateId -Join "`n")}}, @{Name="KnowledgebaseArticles";Expression={($_.KnowledgebaseArticles -Join "`n")}}, @{Name="SecurityBulletins";Expression={($_.SecurityBulletins -Join "`n")}}, UpdateClassificationTitle, @{Name="ProductFamilyTitles";Expression={($_.ProductFamilyTitles -Join "`n")}}, UpdateType, @{Name="Action";Expression={$ActionDetails}} | Export-Csv -Path (Join-Path $LogDirectory "HasStaleUpdateApprovals.csv") -Encoding UTF8 -NoTypeInformation -Append | Out-Null } Catch{ Write-Error "$(Get-Date -Format F): [HasStaleUpdateApprovals] 更新プログラムを取得できませんでした`n$($_.Exception)" } $UpdateScope = New-Object Microsoft.UpdateServices.Administration.UpdateScope $UpdateScope.ApprovedStates = [Microsoft.UpdateServices.Administration.ApprovedStates]::LatestRevisionApproved $ActionDetails = "" Try{ $WsusServer.GetUpdates($UpdateScope) | Select-Object Title, @{Name="ProductTitles";Expression={($_.ProductTitles -Join "`n")}}, CreationDate, LegacyName, @{Name="Id.RevisionNumber";Expression={($_.Id.RevisionNumber -Join "`n")}}, @{Name="Id.UpdateId";Expression={($_.Id.UpdateId -Join "`n")}}, @{Name="KnowledgebaseArticles";Expression={($_.KnowledgebaseArticles -Join "`n")}}, @{Name="SecurityBulletins";Expression={($_.SecurityBulletins -Join "`n")}}, UpdateClassificationTitle, @{Name="ProductFamilyTitles";Expression={($_.ProductFamilyTitles -Join "`n")}}, UpdateType, @{Name="Action";Expression={$ActionDetails}} | Export-Csv -Path (Join-Path $LogDirectory "HasStaleUpdateApprovals.csv") -Encoding UTF8 -NoTypeInformation -Append | Out-Null } Catch{ Write-Error "$(Get-Date -Format F): [LatestRevisionApproved] 更新プログラムを取得できませんでした`n$($_.Exception)" } } } Stop-Logging } Export-ModuleMember -Function Optimize-WsusContents |