Public/Restart-HorizonMachine.ps1

#Requires -Version 5.1

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

function Restart-HorizonMachine {

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    [OutputType('Enterprise.Horizon.MachineRestartPlan')]
    param(
        [Parameter(Mandatory)]
        [ValidateNotNullOrEmpty()]
        [string]$MachineName,

        [ValidateRange(0, 720)]
        [int]$MinimumDisconnectedHours = 6,

        [switch]$IncludeFailedSessions,

        [switch]$Execute
    )

    $machineMatches = @(Invoke-HorizonQuery -EntityType 'MachineSummaryView' |
        Where-Object { $_.Base.Name -eq $MachineName })

    if ($machineMatches.Count -eq 0) {
        throw "No Horizon machine named [$MachineName] was found. No action was taken."
    }
    if ($machineMatches.Count -gt 1) {
        throw "More than one Horizon machine named [$MachineName] was found. No action was taken."
    }

    $machine = $machineMatches[0]
    $poolName = $machine.Base.DesktopName

    $configuration = Import-HorizonConfiguration

    if ($configuration.Exclusions.Pools -contains $poolName) {
        throw "Machine [$MachineName] belongs to excluded pool [$poolName]. No action was taken."
    }

    $sessions = @(Get-HorizonSessions -MachineName $MachineName |
        Where-Object { $_.MachineName -eq $MachineName })

    $excludedUsers = @($sessions | Where-Object { $_.UserName -in $configuration.Exclusions.Users })

    if ($excludedUsers.Count -gt 0) {
        throw "Machine [$MachineName] has an excluded user session. No action was taken."
    }

    $blockingSessions = @($sessions | Where-Object { $_.SessionState -notin @('DISCONNECTED', 'ERROR') })

    if ($blockingSessions.Count -gt 0) {
        throw "Machine [$MachineName] has $($blockingSessions.Count) connected or transitional session(s). No action was taken."
    }

    $failedSessions = @($sessions | Where-Object { $_.SessionState -eq 'ERROR' })

    if ($failedSessions.Count -gt 0 -and -not $IncludeFailedSessions) {
        throw "Machine [$MachineName] has failed session(s). Re-run with -IncludeFailedSessions only after reviewing Get-HorizonFailedSessions."
    }

    $now = Get-Date

    $recentDisconnects = @($sessions | Where-Object {
            $_.SessionState -eq 'DISCONNECTED' -and
            ($null -eq $_.DisconnectTime -or ($now - $_.DisconnectTime).TotalHours -lt $MinimumDisconnectedHours)
        })

    if ($recentDisconnects.Count -gt 0) {
        throw "Machine [$MachineName] has session(s) disconnected for less than $MinimumDisconnectedHours hour(s). No action was taken."
    }

    $plan = [PSCustomObject]@{
        PSTypeName               = 'Enterprise.Horizon.MachineRestartPlan'
        MachineName              = $machine.Base.Name
        DesktopPool              = $poolName
        MachineId                = $machine.Id
        SessionCount             = $sessions.Count
        DisconnectedSessionCount = @($sessions | Where-Object SessionState -eq 'DISCONNECTED').Count
        FailedSessionCount       = $failedSessions.Count
        MinimumDisconnectedHours = $MinimumDisconnectedHours
        WillRestart              = [bool]$Execute
    }

    if (-not $Execute) {
        Write-Information "Restart plan created for [$MachineName]. Re-run with -Execute -Confirm to restart it." -InformationAction Continue
        return $plan
    }

    $services = Get-HorizonService

    if (-not ($services.Machine | Get-Member -Name 'Machine_Restart' -MemberType Method)) {
        throw 'This installed Horizon PowerCLI version does not expose Machine_Restart. No restart was performed.'
    }

    if ($PSCmdlet.ShouldProcess("machine [$MachineName] in pool [$poolName]", 'Restart Horizon machine')) {

        $null = $services.Machine.Machine_Restart($machine.Id)

        Write-HorizonLog `
            -Message "Restart requested for Horizon machine [$MachineName] in pool [$poolName]." `
            -Level Warning

    }

    return $plan

}