Wsus-Maintenance.ps1

<#PSScriptInfo
 
.VERSION 23.04.28
 
.GUID 56dc6e4a-4f05-414c-9419-c575f17f581f
 
.AUTHOR Mike Galvin Contact: mike@gal.vin / twitter.com/mikegalvin_ / discord.gg/5ZsnJ5k
 
.COMPANYNAME Mike Galvin
 
.COPYRIGHT (C) Mike Galvin. All rights reserved.
 
.TAGS WSUS Windows Server Update Services Maintenance Clean up
 
.LICENSEURI
 
.PROJECTURI https://gal.vin/utils/wsus-maint-utility/
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
 
#>


<#
    .SYNOPSIS
    WSUS Maintenance Utility - Clean up your WSUS.
 
    .DESCRIPTION
    Runs the built-in maintenance/clean up routine for WSUS.
    Run with -help or no arguments for usage.
#>


## Set up command line switches.
[CmdletBinding()]
Param(
    [alias("Server")]
    $WsusServer,
    [alias("Port")]
    $WsusPort,
    [alias("L")]
    $LogPathUsr,
    [alias("LogRotate")]
    $LogHistory,
    [alias("Subject")]
    $MailSubject,
    [alias("SendTo")]
    $MailTo,
    [alias("From")]
    $MailFrom,
    [alias("Smtp")]
    $SmtpServer,
    [alias("SmtpPort")]
    $SmtpSvrPort,
    [alias("User")]
    $SmtpUser,
    [alias("Pwd")]
    [ValidateScript({Test-Path -Path $_ -PathType Leaf})]
    $SmtpPwd,
    [Alias("Webhook")]
    [ValidateScript({Test-Path -Path $_ -PathType Leaf})]
    [string]$Webh,
    [switch]$Run,
    [switch]$WsusSsl,
    [switch]$UseSsl,
    [switch]$Help,
    [switch]$NoBanner)

If ($NoBanner -eq $False)
{
    Write-Host -ForegroundColor Yellow -BackgroundColor Black -Object "
        o o o-o o o o-o o o o
        | | | | | | |\ /| o |
        o o o o-o | | o-o | O | oo o-o -o- o-o o-o oo o-o o-o o-o
         \ / \ / | | | | | | | | | | | | |-' | | | | | | | |-'
          o o o--o o-o o--o o o o-o-| o o o o-o o o o-o-o o o-o o-o
                                                                                            
        o o o o o Mike Galvin
        | | | o | o | https://gal.vin
        | | -o- | -o- o o
        | | | | | | | | | Version 23.04.28
         o-o o | o | o o--O See -help for usage
                               |
                            o--o Donate: https://www.paypal.me/digressive
"

}

If ($PSBoundParameters.Values.Count -eq 0 -or $Help)
{
    Write-Host -Object "Usage:
    From a terminal run: [path\]Wsus-Maintenance.ps1 -Run
    This will run the maintenance jobs on the local server
 
    Use -Server [server name] to specify a remote server.
    The local computer running the script must have WSUS management tools installed.
 
    Enable an SSL connection to the WSUS server with -WsusSsl
    Specify the port to use with -Port [port number]
    If none is specified then the default of 8530 will be used, or 8531 if SSL is used.
 
    To output a log: -L [path\].
    To remove logs produced by the utility older than X days: -LogRotate [number].
    Run with no ASCII banner: -NoBanner
 
    To send the log to a webhook on job completion:
    Specify a txt file containing the webhook URI with -Webhook [path\]webhook.txt
 
    To use the 'email log' function:
    Specify the subject line with -Subject ""'[subject line]'"" If you leave this blank a default subject will be used
    Make sure to encapsulate it with double & single quotes as per the example for Powershell to read it correctly.
 
    Specify the 'to' address with -SendTo [example@contoso.com]
    For multiple address, separate with a comma.
 
    Specify the 'from' address with -From [example@contoso.com]
    Specify the SMTP server with -Smtp [smtp server name]
 
    Specify the port to use with the SMTP server with -Port [port number].
    If none is specified then the default of 25 will be used.
 
    Specify the user to access SMTP with -User [example@contoso.com]
    Specify the password file to use with -Pwd [path\]ps-script-pwd.txt.
    Use SSL for SMTP server connection with -UseSsl.
 
    To generate an encrypted password file run the following commands
    on the computer and the user that will run the script:
"

    Write-Host -Object ' $creds = Get-Credential
    $creds.Password | ConvertFrom-SecureString | Set-Content [path\]ps-script-pwd.txt'

}

else {
    ## If logging is configured, start logging.
    ## If the log file already exists, clear it.
    If ($LogPathUsr)
    {
        ## Clean User entered string
        $LogPath = $LogPathUsr.trimend('\')

        ## Make sure the log directory exists.
        If ((Test-Path -Path $LogPath) -eq $False)
        {
            New-Item $LogPath -ItemType Directory -Force | Out-Null
        }

        $LogFile = ("WSUS-Maint_{0:yyyy-MM-dd_HH-mm-ss}.log" -f (Get-Date))
        $Log = "$LogPath\$LogFile"

        If (Test-Path -Path $Log)
        {
            Clear-Content -Path $Log
        }
    }

    ## Function to get date in specific format.
    Function Get-DateFormat
    {
        Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    }

    ## Function for logging.
    Function Write-Log($Type, $Evt)
    {
        If ($Type -eq "Info")
        {
            If ($LogPathUsr)
            {
                Add-Content -Path $Log -Encoding ASCII -Value "$(Get-DateFormat) [INFO] $Evt"
            }

            Write-Host -Object "$(Get-DateFormat) [INFO] $Evt"
        }

        If ($Type -eq "Succ")
        {
            If ($LogPathUsr)
            {
                Add-Content -Path $Log -Encoding ASCII -Value "$(Get-DateFormat) [SUCCESS] $Evt"
            }

            Write-Host -ForegroundColor Green -Object "$(Get-DateFormat) [SUCCESS] $Evt"
        }

        If ($Type -eq "Err")
        {
            If ($LogPathUsr)
            {
                Add-Content -Path $Log -Encoding ASCII -Value "$(Get-DateFormat) [ERROR] $Evt"
            }

            Write-Host -ForegroundColor Red -BackgroundColor Black -Object "$(Get-DateFormat) [ERROR] $Evt"
        }

        If ($Type -eq "Conf")
        {
            If ($LogPathUsr)
            {
                Add-Content -Path $Log -Encoding ASCII -Value "$Evt"
            }

            Write-Host -ForegroundColor Cyan -Object "$Evt"
        }
    }

    ## Function for Update Check
    Function UpdateCheck()
    {
        $ScriptVersion = "23.04.28"
        $RawSource = "https://raw.githubusercontent.com/Digressive/WSUS-Maintenance/master/Wsus-Maintenance.ps1"

        try {
            $SourceCheck = Invoke-RestMethod -uri "$RawSource"
            $VerCheck = $SourceCheck -split '\n' | Select-String -Pattern ".VERSION $ScriptVersion" -SimpleMatch -CaseSensitive -Quiet

            If ($VerCheck -ne $True)
            {
                Write-Log -Type Conf -Evt "*** There is an update available. ***"
            }
        }

        catch {
        }
    }

    ## If WSUS Server is null, set it to local
    If ($null -eq $WsusServer)
    {
        $WsusServer = $env:ComputerName

        try {
            Get-Service WsusService -ErrorAction Stop | Out-Null
        }

        catch {
            Write-Log -Type Err -Evt "WSUS is not installed on this local machine."
            Exit
        }
    }

    ## Default port if none is configured.
    If ($null -eq $WsusPort -And $WsusSsl -eq $False)
    {
        $WsusPort = "8530"
    }

    If ($null -eq $WsusPort -And $WsusSsl)
    {
        $WsusPort = "8531"
    }

    If ($null -eq $LogPathUsr -And $SmtpServer)
    {
        Write-Log -Type Err -Evt "You must specify -L [path\] to use the email log function."
        Exit
    }

    ## getting Windows Version info
    $OSVMaj = [environment]::OSVersion.Version | Select-Object -expand major
    $OSVMin = [environment]::OSVersion.Version | Select-Object -expand minor
    $OSVBui = [environment]::OSVersion.Version | Select-Object -expand build
    $OSV = "$OSVMaj" + "." + "$OSVMin" + "." + "$OSVBui"

    ##
    ## Display the current config and log if configured.
    ##
    Write-Log -Type Conf -Evt "--- Running with the following config ---"
    Write-Log -Type Conf -Evt "Utility Version: 23.04.28"
    UpdateCheck ## Run Update checker function
    Write-Log -Type Conf -Evt "Hostname: $Env:ComputerName."
    Write-Log -Type Conf -Evt "Windows Version: $OSV."
    If ($WsusServer)
    {
        Write-Log -Type Conf -Evt "WSUS Server name: $WsusServer."
    }

    If ($WsusPort)
    {
        Write-Log -Type Conf -Evt "WSUS Server port: $WsusPort."
    }

    If ($WsusSsl)
    {
        Write-Log -Type Conf -Evt "-WsusSSL switch is: $WsusSsl."
    }

    If ($LogPathUsr)
    {
        Write-Log -Type Conf -Evt "Logs directory: $LogPath."
    }

    If ($Webh)
    {
        Write-Log -Type Conf -Evt "Webhook: Configured"
    }

    If ($MailTo)
    {
        Write-Log -Type Conf -Evt "E-mail log to: $MailTo."
    }

    If ($MailFrom)
    {
        Write-Log -Type Conf -Evt "E-mail log from: $MailFrom."
    }

    If ($MailSubject)
    {
        Write-Log -Type Conf -Evt "E-mail subject: $MailSubject."
    }

    If ($SmtpServer)
    {
        Write-Log -Type Conf -Evt "SMTP server: Configured."
    }

    If ($SmtpUser)
    {
        Write-Log -Type Conf -Evt "SMTP auth: Configured."
    }
    Write-Log -Type Conf -Evt "---"
    Write-Log -Type Info -Evt "Process started"
    ##
    ## Display current config ends here.
    ##

    ## Run the Clean up process.
    Write-Log -Type Info -Evt "Connecting to WSUS server"
    Write-Log -Type Info -Evt "WSUS maintenance routine starting..."

    ## WSUS Clean up jobs
    $CleanUpJobs = "CleanupObsoleteComputers","DeclineExpiredUpdates","DeclineSupersededUpdates","CleanupObsoleteUpdates","CleanupUnneededContentFiles","CompressUpdates"

    ForEach ($CleanUpJob in $CleanUpJobs)
    {
        Write-Log -Type Info -Evt "$CleanUpJob..."
        try {
            ## If the WsusSsl switch is configured then connect to the WSUS server using SSL and if not then don't.
            If ($WsusSsl)
            {
                If ($LogPathUsr)
                {
                    Invoke-Expression "Get-WsusServer -Name $WsusServer -PortNumber $WsusPort -UseSSL | Invoke-WsusServerCleanup -$CleanUpJob | Out-File -Append $Log -Encoding ASCII"
                }
                else {
                    Invoke-Expression "Get-WsusServer -Name $WsusServer -PortNumber $WsusPort -UseSSL | Invoke-WsusServerCleanup -$CleanUpJob"
                }
            }

            else {
                If ($LogPathUsr)
                {
                    Invoke-Expression "Get-WsusServer -Name $WsusServer -PortNumber $WsusPort | Invoke-WsusServerCleanup -$CleanUpJob | Out-File -Append $Log -Encoding ASCII"
                }
                else {
                    Invoke-Expression "Get-WsusServer -Name $WsusServer -PortNumber $WsusPort | Invoke-WsusServerCleanup -$CleanUpJob"
                }
            }
        }
        catch {
            Write-Log -Type Err -Evt $_.Exception.Message
        }
    }

    Write-Log -Type Info -Evt "Process finished"

    If ($null -ne $LogHistory)
    {
        ## Cleanup logs.
        Write-Log -Type Info -Evt "Deleting logs older than: $LogHistory days"
        Get-ChildItem -Path "$LogPath\WSUS-Maint_*" -File | Where-Object CreationTime -lt (Get-Date).AddDays(-$LogHistory) | Remove-Item -Recurse
    }

    ## This whole block is for e-mail, if it is configured.
    If ($SmtpServer)
    {
        If (Test-Path -Path $Log)
        {
            ## Default e-mail subject if none is configured.
            If ($null -eq $MailSubject)
            {
                $MailSubject = "WSUS Maintenance Utility Log"
            }

            ## Default Smtp Port if none is configured.
            If ($null -eq $SmtpSvrPort)
            {
                $SmtpSvrPort = "25"
            }

            ## Setting the contents of the log to be the e-mail body.
            $MailBody = Get-Content -Path $Log | Out-String

            ForEach ($MailAddress in $MailTo)
            {
                ## If an smtp password is configured, get the username and password together for authentication.
                ## If an smtp password is not provided then send the e-mail without authentication and obviously no SSL.
                If ($SmtpPwd)
                {
                    $SmtpPwdEncrypt = Get-Content $SmtpPwd | ConvertTo-SecureString
                    $SmtpCreds = New-Object System.Management.Automation.PSCredential -ArgumentList ($SmtpUser, $SmtpPwdEncrypt)

                    ## If -ssl switch is used, send the email with SSL.
                    ## If it isn't then don't use SSL, but still authenticate with the credentials.
                    If ($UseSsl)
                    {
                        Send-MailMessage -To $MailAddress -From $MailFrom -Subject $MailSubject -Body $MailBody -SmtpServer $SmtpServer -Port $SmtpSvrPort -UseSsl -Credential $SmtpCreds
                    }

                    else {
                        Send-MailMessage -To $MailAddress -From $MailFrom -Subject $MailSubject -Body $MailBody -SmtpServer $SmtpServer -Port $SmtpSvrPort -Credential $SmtpCreds
                    }
                }

                else {
                    Send-MailMessage -To $MailAddress -From $MailFrom -Subject $MailSubject -Body $MailBody -SmtpServer $SmtpServer -Port $SmtpSvrPort
                }
            }
        }

        else {
            Write-Host -ForegroundColor Red -BackgroundColor Black -Object "There's no log file to email."
        }
    }
    ## End of Email block

    ## Webhook block
    If ($Webh)
    {
        $WebHookUri = Get-Content $Webh
        $WebHookArr = @()

        $title       = "WSUS Maintenance Utility"
        $description = Get-Content -Path $Log | Out-String

        $WebHookObj = [PSCustomObject]@{
            title = $title
            description = $description
        }

        $WebHookArr += $WebHookObj
        $payload = [PSCustomObject]@{
            embeds = $WebHookArr
        }

        Invoke-RestMethod -Uri $WebHookUri -Body ($payload | ConvertTo-Json -Depth 2) -Method Post -ContentType 'application/json'
    }
}
## End