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'
 
 .Example
   # Simply command sample
   Optimize-WsusContents -ConfigFileName 'C:\ProgramData\Wsustainable\0.1\Config.json'
 
#>


$VerbosePreference = 'Continue'
Function Optimize-WsusContents{
    Param(
        [Parameter(Mandatory)][String]$ConfigPath
    )
    Function Deny-WsusFilteredUpdates($Config, $DeclineRule, $UpdateScope, $RetryCount, $Month, $Mode){
        $CategoryTitle = $UpdateScope.Categories[0].Title
        $DeclineUpdateCount = 0

        If ($RetryCount -gt $Config.UpdatesFindMode.MaximumRetry){
            Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] $CategoryTitle の更新プログラム取得はスキップされました"
            Return
        }

        If (($Config.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 ($Mode){
                    ([WsusFilteredUpdatesMode]::H1){
                        Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] $CategoryTitle の更新プログラム $($Month.ToString("y")) (1/5) はありませんでした"
                    }
                    ([WsusFilteredUpdatesMode]::H2){
                        Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] $CategoryTitle の更新プログラム $($Month.ToString("y")) (2/5) はありませんでした"
                    }
                    ([WsusFilteredUpdatesMode]::H3){
                        Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] $CategoryTitle の更新プログラム $($Month.ToString("y")) (3/5) はありませんでした"
                    }
                    ([WsusFilteredUpdatesMode]::H4){
                        Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] $CategoryTitle の更新プログラム $($Month.ToString("y")) (4/5) はありませんでした"
                    }
                    ([WsusFilteredUpdatesMode]::H5){
                        Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] $CategoryTitle の更新プログラム $($Month.ToString("y")) (5/5) はありませんでした"
                    }
                    ([WsusFilteredUpdatesMode]::Full){
                        Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] $CategoryTitle の更新プログラム $($Month.ToString("y")) はありませんでした"
                    }
                    Default{
                        Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] $CategoryTitle の更新プログラムはありませんでした"
                    }
                }
                Return
            }
            ElseIf (($Mode -eq $Null) -and ($UpdateCount -ge $Config.UpdatesFindMode.ForceHalfModePerMonthLength)){
                Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] $CategoryTitle の更新プログラムが $UpdateCount 件ありましたので分割して取得します"
                ForEach($Month in $Months){
                    Deny-WsusFilteredUpdates $Config $DeclineRule $UpdateScope $RetryCount $Month ([WsusFilteredUpdatesMode]::Full)
                }
                Return
            }
            ElseIf (($Month -ne $Null) -and ($Mode -eq ([WsusFilteredUpdatesMode]::Full)) -and ($UpdateCount -ge $Config.UpdatesFindMode.ForceHalfModePerMonthLength)){
                Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] $CategoryTitle の更新プログラム $($Month.ToString("y")) が $UpdateCount 件ありましたので分割して取得します"
                Deny-WsusFilteredUpdates $Config $DeclineRule $UpdateScope $RetryCount $Month ([WsusFilteredUpdatesMode]::H1) $DeclineRule.DeclineOldVersions
                Return
            }
        }

        Switch ($Mode){
            ([WsusFilteredUpdatesMode]::H1){
                $UpdateScope.FromCreationDate = $Month
                $UpdateScope.ToCreationDate = $Month.AddDays(8).AddSeconds(-1)
                Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] $CategoryTitle の更新プログラム $($Month.ToString("y")) (1/5) [未承認 $($WsusServer.GetUpdateCount($UpdateScope)) 個]"
            }
            ([WsusFilteredUpdatesMode]::H2){
                $UpdateScope.FromCreationDate = $Month.AddDays(7)
                $UpdateScope.ToCreationDate = $Month.AddDays(15).AddSeconds(-1)
                Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] $CategoryTitle の更新プログラム $($Month.ToString("y")) (2/5) [未承認 $($WsusServer.GetUpdateCount($UpdateScope)) 個]"
            }
            ([WsusFilteredUpdatesMode]::H3){
                $UpdateScope.FromCreationDate = $Month.AddDays(14)
                $UpdateScope.ToCreationDate = $Month.AddDays(22).AddSeconds(-1)
                Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] $CategoryTitle の更新プログラム $($Month.ToString("y")) (3/5) [未承認 $($WsusServer.GetUpdateCount($UpdateScope)) 個]"
            }
            ([WsusFilteredUpdatesMode]::H4){
                $UpdateScope.FromCreationDate = $Month.AddDays(21)
                $UpdateScope.ToCreationDate = $Month.AddDays(28).AddSeconds(-1)
                Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] $CategoryTitle の更新プログラム $($Month.ToString("y")) (4/5) [未承認 $($WsusServer.GetUpdateCount($UpdateScope)) 個]"
            }
            ([WsusFilteredUpdatesMode]::H5){
                $UpdateScope.FromCreationDate = $Month.AddDays(28)
                $UpdateScope.ToCreationDate = $Month.AddMonths(1).AddSeconds(-1)
                Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] $CategoryTitle の更新プログラム $($Month.ToString("y")) (5/5) [未承認 $($WsusServer.GetUpdateCount($UpdateScope)) 個]"
            }
            ([WsusFilteredUpdatesMode]::Full){
                Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] $CategoryTitle の更新プログラム $($Month.ToString("y")) [未承認 $UpdateCount 個]"
            }
            Default{
                Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] $CategoryTitle の更新プログラム [未承認 $UpdateCount 個]"
            }
        }

# Try {
            $Updates = $WsusServer.GetUpdates($UpdateScope)
            $LastDeclineUpdate = $Null

            $Updates | Where-Object {
                $DeclineReason = ""
                $IsMatch = $False
                $Update = $_
                If ($DeclineRule.LegacyNameFilter -ne $Null){
                $Global:DeclineRuleLegacyNameFilter = $DeclineRule.LegacyNameFilter
                $Global:UpdateLegacyName = $Update.LegacyName
                    If ($True -in ($DeclineRule.LegacyNameFilter -split "`n" | ForEach-Object {$Update.LegacyName -like $_})){
                        $IsMatch = $True
                        $DeclineReason += "LegacyNameFilter is mtach"
                    }
                }
                If ($DeclineRule.TitleFilter -ne $Null){
                    If ($True -in ($DeclineRule.TitleFilter -split "`n" | ForEach-Object {$Update.Title -like $_})){
                        $IsMatch = $True
                        $DeclineReason += "TitleFilter is mtach"
                    }
                }
                If ($IsMatch){
                    $DeclineUpdateCount++
                    $LastDeclineUpdate = $Update
                    $Update.Decline()
                }
                If ($CurrentConfig.Log.IsLogging){
                    If ($Config.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="DeclineReason";Expression={$DeclineReason}} | Export-Csv -Path (Join-Path $LogDirectory "$($DeclineRule.LogFileNameBase).csv") -Encoding UTF8 -NoTypeInformation -Append
                    }
                    Else{
                        If ($IsMatch){
                            $Update | Select-Object Title, LegacyName, UpdateClassificationTitle, @{Name="DeclineReason";Expression={$DeclineReason}} | Export-Csv -Path (Join-Path $LogDirectory "$($DeclineRule.LogFileNameBase).csv") -Encoding UTF8 -NoTypeInformation -Append
                        }
                    }
                }

            }
            If ($DeclineRule.DeclineOldVersions -and ($LastDeclineUpdate -ne $Null)){
                $LastDeclineUpdate.Approve("Install", $WsusServer.GetComputerTargetGroups()[0])
                Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] $($LastDeclineUpdate.ProductTitles[0]) は一度拒否されましたが、ルールにより承認されました"
                If ($Config.Log.Verbose){
                    $LastDeclineUpdate | 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="DeclineReason";Expression={"Approved"}} | Export-Csv -Path (Join-Path $LogDirectory "$($DeclineRule.LogFileNameBase).csv") -Encoding UTF8 -NoTypeInformation -Append
                }
                Else{
                    If ($IsMatch){
                        $LastDeclineUpdate | Select-Object Title, LegacyName, UpdateClassificationTitle, @{Name="DeclineReason";Expression={"Approved"}} | Export-Csv -Path (Join-Path $LogDirectory "$($DeclineRule.LogFileNameBase).csv") -Encoding UTF8 -NoTypeInformation -Append
                    }
                }
            }
# }
# Catch{
# $WsusServer.PreferredCulture = $CurrentWsusServerPreferredCulture
# Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] $CategoryTitle の更新プログラムを取得できませんでした"
# Write-Verbose $_.Exception

# $RetryCount++
# Deny-WsusFilteredUpdates $Config $DeclineRule $UpdateScope $RetryCount $Month $Mode $DeclineRule.DeclineOldVersions
# }

        Switch ($Mode){
            ([WsusFilteredUpdatesMode]::H1){
                Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] $CategoryTitle の更新プログラム $($Month.ToString("y")) (1/5) [拒否 $DeclineUpdateCount 個]"
                Deny-WsusFilteredUpdates $Config $DeclineRule $UpdateScope $RetryCount $Month ([WsusFilteredUpdatesMode]::H2) $DeclineOldVersions
            }
            ([WsusFilteredUpdatesMode]::H2){
                Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] $CategoryTitle の更新プログラム $($Month.ToString("y")) (2/5) [拒否 $DeclineUpdateCount 個]"
                Deny-WsusFilteredUpdates $Config $DeclineRule $UpdateScope $RetryCount $Month ([WsusFilteredUpdatesMode]::H3) $DeclineOldVersions
            }
            ([WsusFilteredUpdatesMode]::H3){
                Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] $CategoryTitle の更新プログラム $($Month.ToString("y")) (3/5) [拒否 $DeclineUpdateCount 個]"
                Deny-WsusFilteredUpdates $Config $DeclineRule $UpdateScope $RetryCount $Month ([WsusFilteredUpdatesMode]::H4) $DeclineOldVersions
            }
            ([WsusFilteredUpdatesMode]::H4){
                Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] $CategoryTitle の更新プログラム $($Month.ToString("y")) (4/5) [拒否 $DeclineUpdateCount 個]"
                Deny-WsusFilteredUpdates $Config $DeclineRule $UpdateScope $RetryCount $Month ([WsusFilteredUpdatesMode]::H5) $DeclineOldVersions
            }
            ([WsusFilteredUpdatesMode]::H5){
                Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] $CategoryTitle の更新プログラム $($Month.ToString("y")) (5/5) [拒否 $DeclineUpdateCount 個]"
            }
            ([WsusFilteredUpdatesMode]::Full){
                Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] $CategoryTitle の更新プログラム $($Month.ToString("y")) [拒否 $DeclineUpdateCount 個]"
            }
            Default{
                Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] $CategoryTitle の更新プログラム [拒否 $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
                }
            }
        }
    }

    Import-Module UpdateServices

    If (@(Get-Module –Name UpdateServices).Count -eq 0){
        Write-Error "このスクリプトの動作に必要な UpdateServices が見つかりませんでした"
    }

    #$LegacyNameFiltersDirectory = (Join-Path $PSScriptRoot "DeclineFilters\LegacyName")
    #$TitleFiltersDirectory = (Join-Path $PSScriptRoot "DeclineFilters\Title.en")

    If (-Not (Test-Path $ConfigPath -PathType Leaf)){
        Write-Error ([System.IO.FileNotFoundException]::new("ConfigPath が見つかりませんでした: [$ConfigPath]")) -ErrorAction Stop
    }

    $CurrentConfig = (Get-Content $ConfigPath -Encoding UTF8 | ConvertFrom-Json)
    
    Start-Logging $CurrentConfig

    Get-DeclineRules $CurrentConfig | Out-Null
    $CurrentConfig = Set-RequiredConfigurationValues $CurrentConfig

    If ($CurrentConfig.Log.Verbose){
        $CurrentConfig | ConvertTo-Json -Depth 10 | Out-File -FilePath (Join-Path $LogDirectory "InternalConfig.json") -Encoding UTF8
    }

    $WsusServer = Get-WsusServer -Name $CurrentConfig.Wsus.Server -PortNumber $CurrentConfig.Wsus.Port

    If ($WsusServer -eq $Null){
        Write-Error "WSUS サーバーに接続できませんでした"
        Break
    }

    Stop-WsusSynchronization -WsusServer $WsusServer

    If ($CurrentConfig.Wsus.PreferredCulture -ne $Null){
        $WsusServer.PreferredCulture = $CurrentConfig.Wsus.PreferredCulture
    }

    $CurrentDate = [datetime]$CurrentConfig.UpdatesFindMode.MinimumDate
    $Months = @()
    Do{
        $Months += $CurrentDate
        $CurrentDate = $CurrentDate.AddMonths(1)
    }
    While($CurrentDate -lt (Get-Date -Day 1))

    ForEach($DeclineRule in $CurrentConfig.DeclineRules){
        If ($DeclineRule.'TargetProduct.Id' -ne $Null){
            $TargetProductDetails = "TargetProduct.Id: $($DeclineRule.'TargetProduct.Id')"
            If ($WsusServer.GetUpdateCategory($DeclineRule.'TargetProduct.Id') -ne $Null){
                $TargetProductTitle = $WsusServer.GetUpdateCategory($DeclineRule.'TargetProduct.Id').Title
                $TargetProductDetails = "TargetProductTitle: $TargetProductTitle, TargetProduct.Id: $($DeclineRule.'TargetProduct.Id')"
            }
        }
        Else{
            $TargetProductDetails = "TargetProductTitle: $($DeclineRule.'TargetProduct.Title')"
        }
        # DeclineRulesに必要な条件
        If ((($DeclineRule | Get-Member -Name 'LogFileNameBase').Count -eq 0) -or
            (($DeclineRule | Get-Member -Name 'TargetProduct.Id').Count -eq 0) -and (($DeclineRule | Get-Member -Name 'TargetProduct.Title').Count -eq 0) -or
            (($DeclineRule | Get-Member -Name 'LegacyNameFilterPath').Count -eq 0) -and (($DeclineRule | Get-Member -Name 'TitleFilterPath').Count -eq 0)){
            Write-Verbose "$(Get-Date -Format F): [$($DeclineRule.LogFileNameBase)] の条件には必要なパラメータがありません ($TargetProductDetails)" -Warning
            Continue
        }
        Else{
            Write-Verbose "$(Get-Date -Format F): [$($DeclineRule.LogFileNameBase)] の条件に合う更新プログラムを拒否します ($TargetProductDetails)"
                If ($DeclineRule.LegacyNameFilter -eq $Null){
                    Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] LegacyNameFilter はありません"
                }
                Else{
                    Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] LegacyNameFilter: $($DeclineRule.LegacyNameFilter.Replace("`n",","))"
                }
                If ($DeclineRule.TitleFilter -eq $Null){
                    Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] TitleFilter はありません"
                }
                Else{
                    Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] TitleFilter: $($DeclineRule.TitleFilter.Replace("`n",","))"
                }
                If ($DeclineRule.DeclineOldVersions){
                    Write-Verbose "$(Get-Date -Format F): [Deny-WsusFilteredUpdates] DeclineOldVersions"
                }
        }
        

        #Try{
            If ($DeclineRule.'TargetProduct.Id' -ne $Null){
                $CurrentCategory = $WsusServer.GetUpdateCategory($DeclineRule.'TargetProduct.Id')
            }
            ElseIf ($DeclineRule.'TargetProduct.Title' -ne $Null){
                $CurrentCategory = $WsusServer.GetUpdateCategories | Where-Object Title -eq $DeclineRule.'TargetProduct.Title'
            }

            $UpdateScope = New-Object Microsoft.UpdateServices.Administration.UpdateScope
            $UpdateScope.ApprovedStates = [Microsoft.UpdateServices.Administration.ApprovedStates]::NotApproved
            $UpdateScope.Categories.Add($CurrentCategory) | Out-Null
            
            If (($DeclineRule | Get-Member -Name 'UpdateClassification.Id').Count -eq 1){
                $UpdateClassification = $WsusServer.GetUpdateClassifications()
                $DeclineRule.'UpdateClassification.Id' | ForEach-Object {$UpdateClassification | Where-Object Id -eq $_ | ForEach-Object {$UpdateScope.Classifications.Add($_) | Out-Null}}
            }

            $Script:LatestDeclineUpdate = $Null
            Deny-WsusFilteredUpdates -Config $CurrentConfig -DeclineRule $DeclineRule -UpdateScope $UpdateScope -RetryCount 0
        Try{
        }
        Catch{
            Write-Error "$(Get-Date -Format F): $($CurrentCategory.Title) の更新プログラムを取得できませんでした`n$($_.Exception)"
        }
    }

    If ($CurrentConfig.DeclineOptions.CleanupWizard.CompressUpdate){
        #圧縮された更新プログラム
        $WsusServer | Invoke-WsusServerCleanup -CompressUpdates
    }
    If ($CurrentConfig.DeclineOptions.CleanupWizard.CleanupObsoleteUpdates){
        #削除された古い更新プログラム
        $WsusServer | Invoke-WsusServerCleanup -CleanupObsoleteUpdates
    }
    If ($CurrentConfig.DeclineOptions.CleanupWizard.CleanupObsoleteComputers){
        #
    }
    If ($CurrentConfig.DeclineOptions.CleanupWizard.CleanupUnneededContentFiles){
        #解放されたディスク領域
        $WsusServer | Invoke-WsusServerCleanup -CleanupUnneededContentFiles
    }
    If ($CurrentConfig.DeclineOptions.CleanupWizard.DeclineExpiredUpdates){
        #
    }
    If ($CurrentConfig.DeclineOptions.CleanupWizard.DeclineSupersededUpdates){
        #削除された古い更新プログラム
        $WsusServer | Invoke-WsusServerCleanup -DeclineSupersededUpdates
    }


    Stop-Logging
}
Export-ModuleMember -Function Optimize-WsusContents