Schedule-Shutdown.ps1


Description

Purpose

Executes a shutdown, restart, or hibernate command on a remote computer using shutdown.exe.

Detailed Description

This function provides a PowerShell wrapper around the shutdown.exe command-line utility to perform remote shutdown, restart, or hibernate operations. It includes comprehensive error handling, parameter validation, and security features.

Back to Top

Usage

Example 1

Invoke-RemoteShutdown -ComputerName "Server01" -Action "Restart" -Timeout 60 -Comment "Planned maintenance restart"

Example 2

Invoke-RemoteShutdown -ComputerName "192.168.1.100" -Action "Shutdown" -Force -UseDirectMethod

Example 3

Invoke-RemoteShutdown -ComputerName "Server01" -Abort

Back to Top

Notes

Author: PowerShell Automation Team Version: 2.0 Requires: PowerShell 5.1 or later

This function requires appropriate permissions on the target computer and may require PowerShell remoting (WinRM) to be enabled unless UseDirectMethod is specified.

Back to Top


Script

<#
.SYNOPSIS
    Executes a shutdown, restart, or hibernate command on a remote computer using shutdown.exe.

.DESCRIPTION
    This function provides a PowerShell wrapper around the shutdown.exe command-line utility to perform
    remote shutdown, restart, or hibernate operations. It includes comprehensive error handling, parameter
    validation, and security features.

.PARAMETER ComputerName
    The name or IP address of the target computer. Must be a valid computer name or IP address format.

.PARAMETER Action
    The action to perform. Valid values are "Shutdown", "Restart", or "Hibernate".

.PARAMETER Timeout
    The timeout in seconds before the action is executed. Must be between 0 and 315360000 (10 years).
    Default is 30 seconds.

.PARAMETER Comment
    A comment to display to users on the target computer. Limited to 512 characters and special characters
    are escaped for security.

.PARAMETER Force
    Forces running applications to close without saving. Use with caution.

.PARAMETER Abort
    Cancels a previously scheduled shutdown/restart operation.

.PARAMETER Credential
    Credentials to use for the remote operation. If not specified, current user credentials are used.

.PARAMETER UseDirectMethod
    Uses direct shutdown.exe execution instead of PowerShell remoting. Useful when WinRM is not available.

.EXAMPLE
    Invoke-RemoteShutdown -ComputerName "Server01" -Action "Restart" -Timeout 60 -Comment "Planned maintenance restart"

.EXAMPLE
    Invoke-RemoteShutdown -ComputerName "192.168.1.100" -Action "Shutdown" -Force -UseDirectMethod

.EXAMPLE
    Invoke-RemoteShutdown -ComputerName "Server01" -Abort

.NOTES
    Author: PowerShell Automation Team
    Version: 2.0
    Requires: PowerShell 5.1 or later
    
    This function requires appropriate permissions on the target computer and may require
    PowerShell remoting (WinRM) to be enabled unless UseDirectMethod is specified.

.LINK
    https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/shutdown
#>
function Invoke-RemoteShutdown {
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    param (
        [Parameter(Mandatory = $true, Position = 0, HelpMessage = "Enter the computer name or IP address")]
        [ValidateScript({
            if ($_ -match '^[a-zA-Z0-9\-\.]+$' -or $_ -match '^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$') {
                $true
            } else {
                throw "ComputerName must be a valid computer name or IP address"
            }
        })]
        [string]$ComputerName,

        [Parameter(Mandatory = $true, Position = 1, HelpMessage = "Select the action to perform")]
        [ValidateSet("Shutdown", "Restart", "Hibernate")]
        [string]$Action,

        [Parameter(Position = 2, HelpMessage = "Timeout in seconds (0-315360000)")]
        [ValidateRange(0, 315360000)]
        [int]$Timeout = 30,

        [Parameter(Position = 3, HelpMessage = "Comment to display (max 512 characters)")]
        [ValidateLength(0, 512)]
        [string]$Comment = "Scheduled by PowerShell",

        [Parameter(HelpMessage = "Force applications to close without saving")]
        [switch]$Force,

        [Parameter(HelpMessage = "Cancel a previously scheduled operation")]
        [switch]$Abort,

        [Parameter(HelpMessage = "Credentials for remote access")]
        [System.Management.Automation.PSCredential]$Credential,

        [Parameter(HelpMessage = "Use direct shutdown.exe instead of PowerShell remoting")]
        [switch]$UseDirectMethod
    )

    begin {
        Write-Verbose "Starting Invoke-RemoteShutdown for computer: $ComputerName"
        
        # Initialize error tracking
        $errorOccurred = $false
        $errorDetails = $null
        
        # Sanitize comment to prevent command injection
        $sanitizedComment = $Comment -replace '[&|<>^]', '_' -replace '"', '\"'
        Write-Verbose "Comment sanitized: $sanitizedComment"
    }

    process {
        try {
            # Step 1: Validate computer accessibility
            Write-Verbose "Testing network connectivity to $ComputerName"
            
            # Use Test-Connection for basic connectivity (cross-platform)
            if (-not (Test-Connection -ComputerName $ComputerName -Count 1 -Quiet -ErrorAction SilentlyContinue)) {
                throw "Computer '$ComputerName' is not reachable via ping. Check network connectivity and firewall settings."
            }
            
            # Additional RPC port check for Windows systems (when available)
            try {
                if ($PSVersionTable.PSVersion.Major -ge 6 -and $IsWindows -eq $false) {
                    Write-Verbose "Running on non-Windows system, skipping RPC port check"
                } else {
                    # Try to test RPC port if Test-NetConnection is available
                    if (Get-Command Test-NetConnection -ErrorAction SilentlyContinue) {
                        if (-not (Test-NetConnection -ComputerName $ComputerName -Port 135 -InformationLevel Quiet -WarningAction SilentlyContinue)) {
                            Write-Warning "RPC port (135) is not accessible on '$ComputerName'. This may cause issues with shutdown.exe remote execution."
                        }
                    } else {
                        Write-Verbose "Test-NetConnection not available, skipping RPC port check"
                    }
                }
            } catch {
                Write-Verbose "Could not test RPC connectivity: $($_.Exception.Message)"
            }

            # Step 2: Validate computer exists (if not IP address)
            if ($ComputerName -notmatch '^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$') {
                Write-Verbose "Attempting to resolve computer name: $ComputerName"
                try {
                    $null = [System.Net.Dns]::GetHostEntry($ComputerName)
                    Write-Verbose "Computer name resolved successfully"
                } catch {
                    Write-Warning "Could not resolve computer name '$ComputerName'. Proceeding anyway..."
                }
            }

            # Step 3: Determine the shutdown action
            $actionParam = switch ($Action) {
                "Shutdown" { "/s" }
                "Restart"  { "/r" }
                "Hibernate" { "/h" }
            }
            Write-Verbose "Action parameter set to: $actionParam"

            # Step 4: Build command parameters
            $forceParam = if ($Force) { "/f" } else { "" }
            
            # Step 5: Build the command
            if ($Abort) {
                $cmd = "shutdown.exe /m \\$ComputerName /a"
                $actionDescription = "Cancel Scheduled Operation"
            } else {
                $cmd = "shutdown.exe /m \\$ComputerName $actionParam /t $Timeout /c `"$sanitizedComment`" $forceParam".Trim()
                $actionDescription = "$Action (Timeout: $Timeout seconds)"
            }
            
            Write-Verbose "Command to execute: $cmd"

            # Step 6: Execute the command with proper error handling
            if ($PSCmdlet.ShouldProcess("Computer: $ComputerName", $actionDescription)) {
                
                if ($UseDirectMethod) {
                    Write-Verbose "Using direct shutdown.exe method"
                    $result = & cmd /c $cmd 2>&1
                    $exitCode = $LASTEXITCODE
                } else {
                    Write-Verbose "Using PowerShell remoting method"
                    
                    # Prepare remoting parameters
                    $remotingParams = @{
                        ComputerName = $ComputerName
                        ScriptBlock = { 
                            param($command)
                            $result = & cmd /c $command 2>&1
                            return @{
                                Output = $result
                                ExitCode = $LASTEXITCODE
                            }
                        }
                        ArgumentList = $cmd
                        ErrorAction = 'Stop'
                    }
                    
                    if ($Credential) {
                        $remotingParams.Credential = $Credential
                        Write-Verbose "Using provided credentials for remote connection"
                    }
                    
                    $remoteResult = Invoke-Command @remotingParams
                    $result = $remoteResult.Output
                    $exitCode = $remoteResult.ExitCode
                }

                # Step 7: Process results and handle shutdown.exe specific error codes
                switch ($exitCode) {
                    0 { 
                        $successMessage = if ($Abort) {
                            "Scheduled shutdown/restart/hibernate on '$ComputerName' has been canceled successfully."
                        } else {
                            "Action '$Action' scheduled on '$ComputerName' successfully. Operation will execute in $Timeout seconds."
                        }
                        Write-Host $successMessage -ForegroundColor Green
                        Write-Verbose "Operation completed successfully with exit code 0"
                    }
                    1190 { 
                        throw "A shutdown operation is already in progress on '$ComputerName'. Use -Abort to cancel it first."
                    }
                    1115 { 
                        throw "A system shutdown has already been initiated on '$ComputerName'."
                    }
                    1312 { 
                        throw "No shutdown operation is scheduled on '$ComputerName' to abort."
                    }
                    5 { 
                        throw "Access denied. You do not have permission to perform this operation on '$ComputerName'. Try using -Credential parameter or run as administrator."
                    }
                    53 { 
                        throw "Network path not found. Computer '$ComputerName' may not exist or network connectivity issues."
                    }
                    1326 { 
                        throw "Invalid credentials or authentication failure for '$ComputerName'."
                    }
                    default { 
                        $errorMsg = "Shutdown command failed with exit code $exitCode on '$ComputerName'."
                        if ($result) {
                            $errorMsg += " Output: $($result -join ' ')"
                        }
                        throw $errorMsg
                    }
                }
            }
        }
        catch {
            $errorOccurred = $true
            $errorDetails = $_.Exception.Message
            
            Write-Error "Failed to execute '$Action' on '$ComputerName': $errorDetails"
            Write-Verbose "Full error details: $($_.Exception | Out-String)"
            
            # If remoting failed and not using direct method, suggest fallback
            if (-not $UseDirectMethod -and $_.Exception.Message -like "*remoting*") {
                Write-Warning "PowerShell remoting failed. Consider using -UseDirectMethod parameter as a fallback."
            }
        }
    }

    end {
        if ($errorOccurred) {
            Write-Verbose "Operation completed with errors: $errorDetails"
        } else {
            Write-Verbose "Operation completed successfully"
        }
    }
}

<#
.SYNOPSIS
    Schedules a shutdown, restart, or hibernate operation at a specific date and time.

.DESCRIPTION
    This function calculates the time difference between the current time and the specified shutdown time,
    then calls Invoke-RemoteShutdown to schedule the operation. Includes comprehensive validation and
    error handling.

.PARAMETER ComputerName
    The name or IP address of the target computer. Must be a valid computer name or IP address format.

.PARAMETER ShutdownTime
    The date and time when the operation should occur. Must be a future date/time.

.PARAMETER Action
    The action to perform. Valid values are "Shutdown", "Restart", or "Hibernate". Default is "Shutdown".

.PARAMETER Comment
    A comment to display to users on the target computer. Limited to 512 characters.

.PARAMETER Force
    Forces running applications to close without saving. Use with caution.

.PARAMETER Credential
    Credentials to use for the remote operation. If not specified, current user credentials are used.

.PARAMETER UseDirectMethod
    Uses direct shutdown.exe execution instead of PowerShell remoting. Useful when WinRM is not available.

.EXAMPLE
    Schedule-Shutdown -ComputerName "Server01" -ShutdownTime (Get-Date "23:00") -Action "Shutdown" -Comment "End of day shutdown"

.EXAMPLE
    Schedule-Shutdown -ComputerName "192.168.1.100" -ShutdownTime (Get-Date).AddHours(2) -Action "Restart" -Force

.EXAMPLE
    $cred = Get-Credential
    Schedule-Shutdown -ComputerName "Server01" -ShutdownTime (Get-Date "2023-12-31 23:59") -Credential $cred

.NOTES
    Author: PowerShell Automation Team
    Version: 2.0
    Requires: PowerShell 5.1 or later
    
    The maximum timeout supported by shutdown.exe is 315360000 seconds (approximately 10 years).

.LINK
    Invoke-RemoteShutdown
#>
function Schedule-Shutdown {
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    param (
        [Parameter(Mandatory = $true, Position = 0, HelpMessage = "Enter the computer name or IP address")]
        [ValidateScript({
            if ($_ -match '^[a-zA-Z0-9\-\.]+$' -or $_ -match '^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$') {
                $true
            } else {
                throw "ComputerName must be a valid computer name or IP address"
            }
        })]
        [string]$ComputerName,

        [Parameter(Mandatory = $true, Position = 1, HelpMessage = "Enter the date and time for the operation")]
        [ValidateScript({
            if ($_ -gt (Get-Date)) {
                $true
            } else {
                throw "ShutdownTime must be a future date and time"
            }
        })]
        [datetime]$ShutdownTime,

        [Parameter(Position = 2, HelpMessage = "Select the action to perform")]
        [ValidateSet("Shutdown", "Restart", "Hibernate")]
        [string]$Action = "Shutdown",

        [Parameter(Position = 3, HelpMessage = "Comment to display (max 512 characters)")]
        [ValidateLength(0, 512)]
        [string]$Comment = "Scheduled by PowerShell",

        [Parameter(HelpMessage = "Force applications to close without saving")]
        [switch]$Force,

        [Parameter(HelpMessage = "Credentials for remote access")]
        [System.Management.Automation.PSCredential]$Credential,

        [Parameter(HelpMessage = "Use direct shutdown.exe instead of PowerShell remoting")]
        [switch]$UseDirectMethod
    )

    begin {
        Write-Verbose "Starting Schedule-Shutdown for computer: $ComputerName at time: $ShutdownTime"
    }

    process {
        try {
            # Calculate the time difference in seconds
            $currentDateTime = Get-Date
            $timeDifference = ($ShutdownTime - $currentDateTime).TotalSeconds
            
            Write-Verbose "Current time: $currentDateTime"
            Write-Verbose "Scheduled time: $ShutdownTime"
            Write-Verbose "Time difference: $timeDifference seconds"

            # Validate the time difference is still positive (double-check)
            if ($timeDifference -le 0) {
                throw "The specified time has already passed. Please specify a future time. Current time: $currentDateTime, Specified time: $ShutdownTime"
            }

            # Validate the timeout is within shutdown.exe limits
            if ($timeDifference -gt 315360000) {
                throw "The timeout ($timeDifference seconds) exceeds the maximum allowed by shutdown.exe (315360000 seconds / ~10 years). Please specify a closer time."
            }

            # Round to nearest second for shutdown.exe
            $timeoutSeconds = [math]::Round($timeDifference)
            Write-Verbose "Rounded timeout: $timeoutSeconds seconds"

            # Prepare parameters for Invoke-RemoteShutdown
            $invokeParams = @{
                ComputerName = $ComputerName
                Action = $Action
                Timeout = $timeoutSeconds
                Comment = $Comment
                Force = $Force
                UseDirectMethod = $UseDirectMethod
                Verbose = $VerbosePreference
            }

            if ($Credential) {
                $invokeParams.Credential = $Credential
            }

            # Show what will happen
            $timeSpan = New-TimeSpan -Seconds $timeoutSeconds
            $friendlyTime = if ($timeSpan.Days -gt 0) {
                "{0} days, {1:D2}:{2:D2}:{3:D2}" -f $timeSpan.Days, $timeSpan.Hours, $timeSpan.Minutes, $timeSpan.Seconds
            } else {
                "{0:D2}:{1:D2}:{2:D2}" -f $timeSpan.Hours, $timeSpan.Minutes, $timeSpan.Seconds
            }

            $actionDescription = "$Action scheduled for $ShutdownTime (in $friendlyTime)"

            if ($PSCmdlet.ShouldProcess("Computer: $ComputerName", $actionDescription)) {
                # Invoke the remote shutdown with the calculated timeout
                Invoke-RemoteShutdown @invokeParams
            }
        }
        catch {
            Write-Error "Failed to schedule '$Action' on '$ComputerName' for time '$ShutdownTime': $($_.Exception.Message)"
            Write-Verbose "Full error details: $($_.Exception | Out-String)"
        }
    }

    end {
        Write-Verbose "Schedule-Shutdown completed"
    }
}

# Example Usage:

# Basic shutdown with 30 second timeout
# Invoke-RemoteShutdown -ComputerName "Server01" -Action "Shutdown" -Timeout 30 -Comment "Maintenance shutdown"

# Immediate restart with force
# Invoke-RemoteShutdown -ComputerName "Server01" -Action "Restart" -Timeout 0 -Force

# Schedule a shutdown for tonight at 11 PM
# Schedule-Shutdown -ComputerName "Server01" -ShutdownTime (Get-Date "23:00") -Action "Shutdown" -Comment "End of day shutdown"

# Schedule with credentials and force flag
# $cred = Get-Credential
# Schedule-Shutdown -ComputerName "Server01" -ShutdownTime (Get-Date).AddHours(2) -Action "Restart" -Force -Credential $cred

# Cancel a scheduled operation
# Cancel-RemoteShutdown -ComputerName "Server01"

# Use direct method when PowerShell remoting is not available
# Invoke-RemoteShutdown -ComputerName "192.168.1.100" -Action "Shutdown" -UseDirectMethod

<#
.SYNOPSIS
    Tests if a computer is ready for shutdown operations.

.DESCRIPTION
    This helper function tests network connectivity and basic accessibility before attempting
    shutdown operations. Can be used to pre-validate targets.

.PARAMETER ComputerName
    The name or IP address of the target computer.

.PARAMETER Credential
    Credentials to use for testing access.

.EXAMPLE
    Test-ShutdownReadiness -ComputerName "Server01"

.EXAMPLE
    Test-ShutdownReadiness -ComputerName "Server01" -Credential (Get-Credential)
#>
function Test-ShutdownReadiness {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$ComputerName,

        [Parameter()]
        [System.Management.Automation.PSCredential]$Credential
    )

    $results = [PSCustomObject]@{
        ComputerName = $ComputerName
        NetworkConnectivity = $false
        RpcAccess = $false
        PowerShellRemoting = $false
        Overall = $false
    }

    try {
        # Test basic network connectivity
        Write-Verbose "Testing basic network connectivity to $ComputerName"
        if (Test-Connection -ComputerName $ComputerName -Count 1 -Quiet) {
            $results.NetworkConnectivity = $true
            Write-Verbose "Network connectivity: SUCCESS"
        } else {
            Write-Verbose "Network connectivity: FAILED"
            return $results
        }

        # Test RPC access (required for shutdown.exe) - Windows only
        Write-Verbose "Testing RPC access to $ComputerName"
        try {
            if ($PSVersionTable.PSVersion.Major -ge 6 -and $IsWindows -eq $false) {
                Write-Verbose "Running on non-Windows system, skipping RPC port check"
                $results.RpcAccess = $true  # Assume available if we can't test
            } else {
                if (Get-Command Test-NetConnection -ErrorAction SilentlyContinue) {
                    if (Test-NetConnection -ComputerName $ComputerName -Port 135 -InformationLevel Quiet -WarningAction SilentlyContinue) {
                        $results.RpcAccess = $true
                        Write-Verbose "RPC access: SUCCESS"
                    } else {
                        Write-Verbose "RPC access: FAILED"
                    }
                } else {
                    Write-Verbose "Test-NetConnection not available, assuming RPC access is available"
                    $results.RpcAccess = $true
                }
            }
        } catch {
            Write-Verbose "RPC access test failed: $($_.Exception.Message)"
        }

        # Test PowerShell remoting
        Write-Verbose "Testing PowerShell remoting to $ComputerName"
        try {
            $testParams = @{
                ComputerName = $ComputerName
                ScriptBlock = { $env:COMPUTERNAME }
                ErrorAction = 'Stop'
            }
            if ($Credential) {
                $testParams.Credential = $Credential
            }
            
            $null = Invoke-Command @testParams
            $results.PowerShellRemoting = $true
            Write-Verbose "PowerShell remoting: SUCCESS"
        } catch {
            Write-Verbose "PowerShell remoting: FAILED - $($_.Exception.Message)"
        }

        # Overall assessment
        $results.Overall = $results.NetworkConnectivity -and $results.RpcAccess

        return $results
    }
    catch {
        Write-Error "Error testing shutdown readiness for '$ComputerName': $($_.Exception.Message)"
        return $results
    }
}

<#
.SYNOPSIS
    Gets the current shutdown status of a remote computer.

.DESCRIPTION
    This function attempts to determine if there's already a shutdown operation scheduled
    on the target computer by trying to cancel a non-existent shutdown.

.PARAMETER ComputerName
    The name or IP address of the target computer.

.PARAMETER Credential
    Credentials to use for the operation.

.EXAMPLE
    Get-ShutdownStatus -ComputerName "Server01"
#>
function Get-ShutdownStatus {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]$ComputerName,

        [Parameter()]
        [System.Management.Automation.PSCredential]$Credential
    )

    try {
        Write-Verbose "Checking shutdown status for $ComputerName"
        
        $cmd = "shutdown.exe /m \\$ComputerName /a"
        
        if ($Credential) {
            $result = Invoke-Command -ComputerName $ComputerName -Credential $Credential -ScriptBlock { 
                param($command)
                $output = & cmd /c $command 2>&1
                return @{
                    Output = $output
                    ExitCode = $LASTEXITCODE
                }
            } -ArgumentList $cmd
        } else {
            $result = Invoke-Command -ComputerName $ComputerName -ScriptBlock { 
                param($command)
                $output = & cmd /c $command 2>&1
                return @{
                    Output = $output
                    ExitCode = $LASTEXITCODE
                }
            } -ArgumentList $cmd
        }

        switch ($result.ExitCode) {
            0 { return "Shutdown operation was scheduled and has been canceled" }
            1312 { return "No shutdown operation is currently scheduled" }
            default { return "Unknown status (Exit code: $($result.ExitCode))" }
        }
    }
    catch {
        Write-Error "Failed to check shutdown status for '$ComputerName': $($_.Exception.Message)"
        return "Error checking status"
    }
}

<#
.SYNOPSIS
    Cancels a previously scheduled shutdown, restart, or hibernate operation on a remote computer.

.DESCRIPTION
    This function cancels any pending shutdown, restart, or hibernate operation on the specified computer
    using the shutdown.exe /a command. Includes comprehensive error handling and validation.

.PARAMETER ComputerName
    The name or IP address of the target computer. Must be a valid computer name or IP address format.

.PARAMETER Credential
    Credentials to use for the remote operation. If not specified, current user credentials are used.

.PARAMETER UseDirectMethod
    Uses direct shutdown.exe execution instead of PowerShell remoting. Useful when WinRM is not available.

.EXAMPLE
    Cancel-RemoteShutdown -ComputerName "Server01"

.EXAMPLE
    Cancel-RemoteShutdown -ComputerName "192.168.1.100" -UseDirectMethod

.EXAMPLE
    $cred = Get-Credential
    Cancel-RemoteShutdown -ComputerName "Server01" -Credential $cred

.NOTES
    Author: PowerShell Automation Team
    Version: 2.0
    Requires: PowerShell 5.1 or later
    
    This function can only cancel operations that were scheduled with a timeout. 
    Immediate shutdowns cannot be canceled.

.LINK
    Invoke-RemoteShutdown
    Schedule-Shutdown
#>
function Cancel-RemoteShutdown {
    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')]
    param (
        [Parameter(Mandatory = $true, Position = 0, HelpMessage = "Enter the computer name or IP address")]
        [ValidateScript({
            if ($_ -match '^[a-zA-Z0-9\-\.]+$' -or $_ -match '^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$') {
                $true
            } else {
                throw "ComputerName must be a valid computer name or IP address"
            }
        })]
        [string]$ComputerName,

        [Parameter(HelpMessage = "Credentials for remote access")]
        [System.Management.Automation.PSCredential]$Credential,

        [Parameter(HelpMessage = "Use direct shutdown.exe instead of PowerShell remoting")]
        [switch]$UseDirectMethod
    )

    begin {
        Write-Verbose "Starting Cancel-RemoteShutdown for computer: $ComputerName"
    }

    process {
        try {
            # Prepare parameters for Invoke-RemoteShutdown with Abort flag
            $invokeParams = @{
                ComputerName = $ComputerName
                Action = "Shutdown"  # This will be ignored due to Abort flag
                Abort = $true
                UseDirectMethod = $UseDirectMethod
                Verbose = $VerbosePreference
            }

            if ($Credential) {
                $invokeParams.Credential = $Credential
            }

            if ($PSCmdlet.ShouldProcess("Computer: $ComputerName", "Cancel Scheduled Shutdown/Restart/Hibernate")) {
                # Use the improved Invoke-RemoteShutdown function with Abort parameter
                Invoke-RemoteShutdown @invokeParams
            }
        }
        catch {
            Write-Error "Failed to cancel scheduled operation on '$ComputerName': $($_.Exception.Message)"
            Write-Verbose "Full error details: $($_.Exception | Out-String)"
        }
    }

    end {
        Write-Verbose "Cancel-RemoteShutdown completed"
    }
}

# # Example usage
# Cancel-RemoteShutdown -ComputerName "RemotePC01"

Back to Top

Download

Please feel free to copy parts of the script or if you would like to download the entire script, simply click the download button. You can download the complete repository in a zip file by clicking the Download link in the menu bar on the left hand side of the page.


Report Issues

You can report an issue or contribute to this site on GitHub. Simply click the button below and add any relevant notes. I will attempt to respond to all issues as soon as possible.

Issue


Back to Top