Public/Invoke-RPAReboot.ps1

<#
.SYNOPSIS
    Checks if a specified service on a computer is operational.
 
.DESCRIPTION
    This function attempts to verify if a computer is responsive and if a specified service is actively running. It continuously checks the system's WMI accessibility and the service status until the service is running or a timeout occurs.
 
.PARAMETER ComputerName
    The name of the computer to check.
 
.PARAMETER ServiceName
    The name of the service to check for running status.
 
.PARAMETER TimeoutSeconds
    The maximum time in seconds to wait for the system and service to become responsive.
 
.EXAMPLE
    Check-SystemState -ComputerName "Server01" -ServiceName "loginagent" -TimeoutSeconds 100
    Checks if the "loginagent" service on "Server01" is running, with a timeout of 100 seconds.
 
.NOTES
    Ensure that the computer is accessible over the network and that WMI services are enabled.
#>


Function Check-SystemState {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$ComputerName,

        [Parameter(Mandatory = $true)]
        [string]$ServiceName,

        [int]$TimeoutSeconds = 100
    )

    $startTime = [datetime]::Now

    while (!(Get-WmiObject Win32_Bios -ComputerName $ComputerName -ErrorAction SilentlyContinue)) {
        Start-Sleep -Seconds 5
        if (([datetime]::Now - $startTime).TotalSeconds -gt $TimeoutSeconds) {
            Write-Output "Connection to $ComputerName timed out."
            return $false
        }
    }

    Write-Output "$ComputerName is responsive. Checking if the 'loginagent' service is started."
    do {
        $service = Get-Service -ComputerName $ComputerName -Name $ServiceName -ErrorAction SilentlyContinue
        Start-Sleep -Seconds 5
    } while ($service.Status -ne 'Running' -and ([datetime]::Now - $startTime).TotalSeconds -lt $TimeoutSeconds)

    if ($service.Status -eq 'Running') {
        Write-Output "The 'loginagent' service on $ComputerName has started successfully."
        return $true
    } else {
        Write-Output "Failed to verify that the 'loginagent' service is running on $ComputerName."
        return $false
    }
}

<#
.SYNOPSIS
    Retrieves the logged-in status of a user on a specified remote computer.
 
.DESCRIPTION
    This function checks if any user is currently logged into the specified computer. It uses WMI to retrieve user login status and outputs whether a user is logged in or not.
 
.PARAMETER ComputerName
    The name of the computer to check the login status of.
 
.EXAMPLE
    Get-LoggedInStatus -ComputerName "Server01"
    Checks if any user is logged in on "Server01".
 
.NOTES
    Requires network connectivity and appropriate permissions to access WMI on the target computer.
#>


Function Get-LoggedInStatus {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$ComputerName
    )

    if (Test-Connection -ComputerName $ComputerName -Count 2 -Quiet) {
        try {
            $userDetails = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $ComputerName -ErrorAction Stop
            if ($userDetails.UserName) {
                Write-Output "Computer in use."
            } else {
                Write-Output 'Not logged on.'
            }
        } catch {
            Write-Output "Failed to retrieve user information: $_"
        }
    } else {
        Write-Output 'Computer offline.'
    }
}

<#
.SYNOPSIS
    Initiates a reboot of a specified host and verifies its operational status afterward.
 
.DESCRIPTION
    This function reboots the server if no users are logged on. Post-reboot, it confirms if a specified service is running on the system.
 
.PARAMETER HostName
    The name of the host to reboot.
 
.PARAMETER ServiceName
    The service to check for operational status post-reboot.
 
.EXAMPLE
    Invoke-RPAReboot -HostName "Server01" -ServiceName "loginagent"
    Attempts to reboot "Server01" and checks if the "loginagent" service is running post-reboot.
 
.NOTES
    The function checks for user logoff before initiating a reboot. It requires network access and administrative privileges on the target host.
#>


Function Invoke-RPAReboot {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [string]$HostName,

        [Parameter(Mandatory = $true, Position = 1)]
        [string]$ServiceName
    )

    if (Test-Connection -ComputerName $HostName -Count 2 -Quiet) {
        $status = Get-LoggedInStatus -ComputerName $HostName

        if ($status -eq 'Not logged on.') {
            Write-Output "Please wait while we reboot the server $HostName."
            try {
                if ((Get-WmiObject Win32_OperatingSystem -ComputerName $HostName | Invoke-WmiMethod -Name Win32Shutdown -ArgumentList 6).ReturnValue -eq 0) {
                    Start-Sleep -Seconds 15
                    if (Check-SystemState -ComputerName $HostName -ServiceName $ServiceName) {
                        Write-Output "$(Get-Date) - $HostName Successfully Restarted"
                    } else {
                        Write-Output "Failed to verify restart on $HostName."
                    }
                } else {
                    Write-Output "Failed to initiate reboot on $HostName."
                }
            } catch {
                Write-Output "Error during reboot process: $_"
            }
        } elseif ($status -eq 'Computer in use.') {
            $userDetails = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $HostName -ErrorAction Stop
            Write-Output "$($userDetails.UserName) is logged in already on $HostName. Please log off the user before retrying."
        }
    } else {
        Write-Output "$HostName is not reachable."
    }
}