Public/Invoke-ComputerMaintenance.ps1

function Invoke-ComputerMaintenance {

    [CmdletBinding()]
    Param (
        [Parameter(Mandatory)]
        [string]$ComputerName,
        [int]$PreventiveLockTimeout = $ModuleWidePreventiveLockTimeout,
        [System.TimeSpan]$PreventiveLockThreshold = $ModuleWidePreventiveLockThreshold,
        [switch]$SkipNotLockable = $ModuleWideSkipNotLockable,
        [switch]$SkipPreventivelyLocked = $ModuleWideSkipPreventivelyLocked,
        [switch]$EnableMaintenanceLog = $ModuleWideEnableMaintenanceLog
    )

    $ErrorActionPreference = 'Stop'

    Write-Debug -Message ('ENTER {0}' -f $MyInvocation.MyCommand.Name)

    try {
        Write-Debug -Message ('ENTER TRY {0}' -f $MyInvocation.MyCommand.Name)

        Write-Debug -Message ('$ComputerName = ''{0}''' -f $ComputerName)
        Write-Debug -Message ('$PreventiveLockTimeout = {0}' -f $PreventiveLockTimeout)
        Write-Debug -Message ('$PreventiveLockThreshold: ''{0}''' -f [string]$PreventiveLockThreshold)
        Write-Debug -Message ('$SkipNotLockable = ${0}' -f $SkipNotLockable)
        Write-Debug -Message ('$SkipPreventivelyLocked = ${0}' -f $SkipPreventivelyLocked)
        Write-Debug -Message ('$EnableMaintenanceLog = ${0}' -f $EnableMaintenanceLog)

        Write-Debug -Message ('$IsMaintenanceAllowed = Test-MaintenanceAllowed -ComputerName ''{0}''' -f $ComputerName)
        $IsMaintenanceAllowed = Test-MaintenanceAllowed -ComputerName $ComputerName
        Write-Debug -Message ('$IsMaintenanceAllowed = ${0}' -f $IsMaintenanceAllowed)
        Write-Debug -Message 'if ($IsMaintenanceAllowed)'
        if ($IsMaintenanceAllowed) {
            Write-Debug -Message '$HostLock = $null'
            $HostLock = $null
            Write-Debug -Message ('$HostLock: ''{0}''' -f $HostLock)
            Write-Debug -Message '$DestinationHostLock = $null'
            $DestinationHostLock = $null
            Write-Debug -Message ('$DestinationHostLock: ''{0}''' -f $DestinationHostLock)
            Write-Debug -Message ('$PendingReboot = Test-PendingReboot -ComputerName ''{0}'' -Detailed' -f $ComputerName)
            $PendingReboot = Test-PendingReboot -ComputerName $ComputerName -Detailed
            Write-Debug -Message ('$PendingReboot: ''{0}''' -f $PendingReboot)
            Write-Debug -Message ('$PendingReboot.PendingFileRenameOperationsValue: ''{0}''' -f [string]$PendingReboot.PendingFileRenameOperationsValue)
            Write-Debug -Message ('$PendingReboot.ComponentBasedServicing: ''{0}''' -f $PendingReboot.ComponentBasedServicing)
            Write-Debug -Message 'if ($PendingReboot.ComponentBasedServicing)'
            if ($PendingReboot.ComponentBasedServicing) {
                # Sometimes, when CBS requires reboot, search for updates hangs up. Therefore, we need to reboot the machine first.
                Write-Debug -Message ('$ComputerWorkload = Initialize-ComputerMaintenance -ComputerName ''{0}'' -HostLock ([ref]$HostLock) -DestinationHostLock ([ref]$DestinationHostLock) -SkipNotLockable:${1} -SkipPreventivelyLocked:${2}' -f $ComputerName, $SkipNotLockable, $SkipPreventivelyLocked)
                $ComputerWorkload = Initialize-ComputerMaintenance -ComputerName $ComputerName -HostLock ([ref]$HostLock) -DestinationHostLock ([ref]$DestinationHostLock) -SkipNotLockable:$SkipNotLockable -SkipPreventivelyLocked:$SkipPreventivelyLocked
                Write-Debug -Message ('$ComputerWorkload: ''{0}''' -f [string]$ComputerWorkload)

                Write-Debug -Message ('$HostLock: ''{0}''' -f $HostLock)
                Write-Debug -Message 'if ($HostLock)'
                if ($HostLock) {
                    Write-Debug -Message ('Invoke-ComputerRestart -ComputerName ''{0}''' -f $ComputerName)
                    Invoke-ComputerRestart -ComputerName $ComputerName
                    Write-Debug -Message ('$IsMaintenanceNeeded = Test-MaintenanceNeeded -ComputerName ''{0}''' -f $ComputerName)
                    $IsMaintenanceNeeded = Test-MaintenanceNeeded -ComputerName $ComputerName
                    Write-Debug -Message ('$IsMaintenanceNeeded = ${0}' -f $IsMaintenanceNeeded)
                    if ($IsMaintenanceNeeded) {
                        Write-Debug -Message ('Start-Maintenance -ComputerName ''{0}'' -EnableMaintenanceLog:${1}' -f $ComputerName, $EnableMaintenanceLog)
                        Start-Maintenance -ComputerName $ComputerName -EnableMaintenanceLog:$EnableMaintenanceLog
                    }
                    Write-Debug -Message ('$ComputerWorkload: ''{0}''' -f [string]$ComputerWorkload)
                    Write-Debug -Message ('$DestinationHostLock: ''{0}''' -f $DestinationHostLock)
                    Write-Debug -Message ('Complete-ComputerMaintenance -ComputerName ''{0}'' -ComputerWorkload $ComputerWorkload -DestinationHostLock ([ref]$DestinationHostLock)' -f $ComputerName)
                    Complete-ComputerMaintenance -ComputerName $ComputerName -ComputerWorkload $ComputerWorkload -DestinationHostLock ([ref]$DestinationHostLock)
                }
            }
            else {
                # Most frequent case: CBS does not prevent from searching for updates
                Write-Debug -Message ('$IsMaintenanceNeeded = Test-MaintenanceNeeded -ComputerName ''{0}''' -f $ComputerName)
                $IsMaintenanceNeeded = Test-MaintenanceNeeded -ComputerName $ComputerName
                Write-Debug -Message ('$IsMaintenanceNeeded = ${0}' -f $IsMaintenanceNeeded)
                if ($IsMaintenanceNeeded) {
                    Write-Debug -Message ('$ComputerWorkload = Initialize-ComputerMaintenance -ComputerName ''{0}'' -HostLock ([ref]$HostLock) -DestinationHostLock ([ref]$DestinationHostLock) -SkipNotLockable:${1} -SkipPreventivelyLocked:${2}' -f $ComputerName, $SkipNotLockable, $SkipPreventivelyLocked)
                    $ComputerWorkload = Initialize-ComputerMaintenance -ComputerName $ComputerName -HostLock ([ref]$HostLock) -DestinationHostLock ([ref]$DestinationHostLock) -SkipNotLockable:$SkipNotLockable -SkipPreventivelyLocked:$SkipPreventivelyLocked
                    Write-Debug -Message ('$ComputerWorkload: ''{0}''' -f [string]$ComputerWorkload)
                    Write-Debug -Message ('$HostLock: ''{0}''' -f $HostLock)
                    Write-Debug -Message 'if ($HostLock)'
                    if ($HostLock) {
                        Write-Debug -Message ('Start-Maintenance -ComputerName ''{0}'' -EnableMaintenanceLog:${1}' -f $ComputerName, $EnableMaintenanceLog)
                        Start-Maintenance -ComputerName $ComputerName -EnableMaintenanceLog:$EnableMaintenanceLog
                        Write-Debug -Message ('$ComputerWorkload: ''{0}''' -f [string]$ComputerWorkload)
                        Write-Debug -Message ('$DestinationHostLock: ''{0}''' -f $DestinationHostLock)
                        Write-Debug -Message ('Complete-ComputerMaintenance -ComputerName ''{0}'' -ComputerWorkload $ComputerWorkload -DestinationHostLock ([ref]$DestinationHostLock)' -f $ComputerName)
                        Complete-ComputerMaintenance -ComputerName $ComputerName -ComputerWorkload $ComputerWorkload -DestinationHostLock ([ref]$DestinationHostLock)
                    }
                }
                else {
                    # If the machine does not need any updates, but just a reboot, let's reboot it.
                    Write-Debug -Message ('$PendingReboot.IsRebootPending: ''{0}''' -f $PendingReboot.IsRebootPending)
                    if ($PendingReboot.IsRebootPending) {
                        Write-Debug -Message ('$ComputerWorkload = Initialize-ComputerMaintenance -ComputerName ''{0}'' -HostLock ([ref]$HostLock) -DestinationHostLock ([ref]$DestinationHostLock) -SkipNotLockable:${1} -SkipPreventivelyLocked:${2}' -f $ComputerName, $SkipNotLockable, $SkipPreventivelyLocked)
                        $ComputerWorkload = Initialize-ComputerMaintenance -ComputerName $ComputerName -HostLock ([ref]$HostLock) -DestinationHostLock ([ref]$DestinationHostLock) -SkipNotLockable:$SkipNotLockable -SkipPreventivelyLocked:$SkipPreventivelyLocked
                        Write-Debug -Message ('$ComputerWorkload: ''{0}''' -f [string]$ComputerWorkload)
                        Write-Debug -Message ('$HostLock: ''{0}''' -f $HostLock)
                        Write-Debug -Message 'if ($HostLock)'
                        if ($HostLock) {
                            Write-Debug -Message ('Invoke-ComputerRestart -ComputerName ''{0}''' -f $ComputerName)
                            Invoke-ComputerRestart -ComputerName $ComputerName
                            Write-Debug -Message ('$ComputerWorkload: ''{0}''' -f [string]$ComputerWorkload)
                            Write-Debug -Message ('$DestinationHostLock: ''{0}''' -f $DestinationHostLock)
                            Write-Debug -Message ('Complete-ComputerMaintenance -ComputerName ''{0}'' -ComputerWorkload $ComputerWorkload -DestinationHostLock ([ref]$DestinationHostLock)' -f $ComputerName)
                            Complete-ComputerMaintenance -ComputerName $ComputerName -ComputerWorkload $ComputerWorkload -DestinationHostLock ([ref]$DestinationHostLock)
                        }
                    }
                }
            }
        }

        Write-Debug -Message ('EXIT TRY {0}' -f $MyInvocation.MyCommand.Name)
    }
    catch {
        Write-Debug -Message ('ENTER CATCH {0}' -f $MyInvocation.MyCommand.Name)

        Write-Debug -Message ('{0}: $PSCmdlet.ThrowTerminatingError($_)' -f $MyInvocation.MyCommand.Name)
        $PSCmdlet.ThrowTerminatingError($_)

        Write-Debug -Message ('EXIT CATCH {0}' -f $MyInvocation.MyCommand.Name)
    }
    finally {
        Write-Debug -Message ('ENTER FINALLY {0}' -f $MyInvocation.MyCommand.Name)

        Write-Debug -Message ('$FinallyVariables = Invoke-CustomScriptBlockCommand -Mode ''Finally'' -ComputerName ''{0}'' -Variables (Get-Variable | Where-Object -FilterScript {{$_ -is [System.Management.Automation.PSVariable]}})' -f $ComputerName)
        $FinallyVariables = Invoke-CustomScriptBlockCommand -ComputerName $ComputerName -Mode 'Finally' -Variables (Get-Variable | Where-Object -FilterScript { $_ -is [System.Management.Automation.PSVariable] })
        Write-Debug -Message ('$FinallyVariables: ''{0}''' -f [string]$FinallyVariables)
        Write-Debug -Message 'if ($FinallyVariables)'
        if ($FinallyVariables) {
            foreach ($FinallyVariable in $FinallyVariables) {
                Write-Debug -Message ('$FinallyVariable: ''{0}''' -f [string]$FinallyVariable)
                Write-Debug -Message ('Set-Variable -Name ''{0}'' -Value ''{1}'' -Scope ''Script''' -f $FinallyVariable.Name, [string]$ErrorHandlingVariable.Value)
                Set-Variable -Name $FinallyVariable.Name -Value $FinallyVariable.Value -Scope 'Script'
            }
        }

        Write-Debug -Message ('$HostLock: ''{0}''' -f $HostLock)
        Write-Debug -Message 'if ($HostLock)'
        if ($HostLock) {
            Write-Debug -Message 'Unlock-Resource -LockObject $HostLock'
            Unlock-Resource -LockObject $HostLock
        }
        Write-Debug -Message ('$DestinationHostLock: ''{0}''' -f $DestinationHostLock)
        Write-Debug -Message 'if ($DestinationHostLock)'
        if ($DestinationHostLock) {
            Write-Debug -Message 'Unlock-Resource -LockObject $DestinationHostLock'
            Unlock-Resource -LockObject $DestinationHostLock
        }

        Write-Debug -Message ('EXIT FINALLY {0}' -f $MyInvocation.MyCommand.Name)
    }

    Write-Debug -Message ('EXIT {0}' -f $MyInvocation.MyCommand.Name)
}