Public/sessions/Remove-RdpSession.ps1
|
#Requires -Version 5.1 function Remove-RdpSession { <# .SYNOPSIS Logs off (removes) an RDP session on local or remote computers .DESCRIPTION Forces a logoff of specified RDP sessions by session ID on one or more computers using logoff.exe. This terminates the session completely and closes all applications. Unsaved work will be lost. Use Disconnect-RdpSession for a graceful disconnect without logoff. Local machines are targeted directly via logoff.exe with no WinRM dependency. Remote machines are targeted via Invoke-Command (WinRM), which executes logoff.exe in the remote session. When -Credential is provided, it is forwarded to Invoke-Command for authentication. Supports ShouldProcess for -WhatIf and -Confirm operations. .PARAMETER ComputerName One or more computer names where sessions should be removed. Defaults to the local machine. Supports pipeline input by property name. .PARAMETER SessionID The session ID(s) to remove. Can be retrieved using Get-RdpSession. Supports pipeline input by value and by property name. .PARAMETER Credential Credential to use when connecting to remote computers via WinRM. If not specified, uses the current user's credentials. Not used for local session logoff. .PARAMETER Force Bypass confirmation prompts. Use with caution as this will forcefully terminate sessions and may result in data loss. .EXAMPLE Remove-RdpSession -SessionID 2 Logs off session ID 2 on the local computer after confirmation. .EXAMPLE Get-RdpSession -ComputerName 'SRV01' | Where-Object { $_.IdleTime -gt (New-TimeSpan -Days 1) } | Remove-RdpSession -Force Forcefully removes all sessions idle for more than 1 day on SRV01 without confirmation. .EXAMPLE Remove-RdpSession -ComputerName 'WEB01' -SessionID 3 -WhatIf Shows what would happen if session 3 were removed from WEB01. .EXAMPLE 'APP01' | Get-RdpSession | Where-Object { $_.UserName -eq 'DOMAIN\olduser' } | Remove-RdpSession -Credential $cred Removes all sessions for a specific user on APP01 using provided credentials. .OUTPUTS PSWinOps.RdpSessionAction Logoff action result with session details and status. .NOTES Author: Franck SALLET Version: 2.0.0 Last Modified: 2026-03-20 Requires: PowerShell 5.1+, logoff.exe (built-in on all Windows editions) Permissions: Local Administrator on target machines WinRM access required when using the -Credential parameter WARNING: This operation terminates sessions forcefully and may cause data loss .LINK https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/logoff #> [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')] [OutputType('PSWinOps.RdpSessionAction')] param( [Parameter(Mandatory = $false, ValueFromPipelineByPropertyName = $true)] [ValidateNotNullOrEmpty()] [Alias('CN', 'Name', 'DNSHostName')] [string]$ComputerName = $env:COMPUTERNAME, [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] [ValidateRange(0, 65536)] [int[]]$SessionID, [Parameter(Mandatory = $false)] [ValidateNotNull()] [System.Management.Automation.PSCredential]$Credential, [Parameter(Mandatory = $false)] [switch]$Force ) begin { Write-Verbose "[$($MyInvocation.MyCommand)] Starting - PowerShell $($PSVersionTable.PSVersion)" $logoffCmd = Get-Command -Name 'logoff.exe' -CommandType Application -ErrorAction SilentlyContinue if ($null -eq $logoffCmd) { $errorRecord = [System.Management.Automation.ErrorRecord]::new( [System.IO.FileNotFoundException]::new( 'logoff.exe was not found on this system. Ensure Remote Desktop Services tools are available.' ), 'LogoffNotFound', [System.Management.Automation.ErrorCategory]::ObjectNotFound, 'logoff.exe' ) $PSCmdlet.ThrowTerminatingError($errorRecord) } Write-Verbose "[$($MyInvocation.MyCommand)] Found logoff.exe at: $($logoffCmd.Source)" if ($Force -and -not $WhatIfPreference) { $ConfirmPreference = 'None' } $LOCAL_IDENTIFIERS = @($env:COMPUTERNAME, 'localhost', '.', '127.0.0.1', '::1') $logoffBlock = { param([int]$SessId) $null = & logoff.exe $SessId 2>&1 return $LASTEXITCODE } } process { foreach ($session in $SessionID) { $targetDescription = "$ComputerName (Session ID: $session)" Write-Verbose "[$($MyInvocation.MyCommand)] Processing $targetDescription" if ($PSCmdlet.ShouldProcess($targetDescription, 'Log off RDP session (FORCE TERMINATE)')) { $isLocalMachine = $ComputerName -in $LOCAL_IDENTIFIERS $success = $false try { $invokeParams = @{ ScriptBlock = $logoffBlock ArgumentList = @($session) ErrorAction = 'Stop' } if (-not $isLocalMachine) { $invokeParams['ComputerName'] = $ComputerName if ($PSBoundParameters.ContainsKey('Credential')) { $invokeParams['Credential'] = $Credential } } $exitCode = Invoke-Command @invokeParams $success = ($null -ne $exitCode -and $exitCode -eq 0) if ($success) { Write-Verbose "[$($MyInvocation.MyCommand)] [OK] Logged off $targetDescription" } else { Write-Warning "[$($MyInvocation.MyCommand)] logoff.exe returned exit code $exitCode for $targetDescription" } } catch [System.Management.Automation.Remoting.PSRemotingTransportException] { Write-Error "[$($MyInvocation.MyCommand)] WinRM connection failed to $ComputerName - $_" } catch [System.UnauthorizedAccessException] { Write-Error "[$($MyInvocation.MyCommand)] Access denied to $ComputerName - Requires administrative permissions" } catch { Write-Error "[$($MyInvocation.MyCommand)] Failed to log off $targetDescription - $_" } [PSCustomObject]@{ PSTypeName = 'PSWinOps.RdpSessionAction' ComputerName = $ComputerName SessionID = $session Action = 'Logoff' Success = $success Timestamp = Get-Date -Format 'o' } } } } end { Write-Verbose "[$($MyInvocation.MyCommand)] Completed" } } |