← Files

Windows-LogOff#

Professional PowerShell 7+ utility for forcibly logging off Windows users and cleaning up residual processes. Designed for system administrators managing multi-session Windows environments.


Table of Contents#

  1. Overview

  2. Features

  3. Prerequisites

  4. Installation

  5. Configuration

  6. Usage

  7. How It Works

  8. Safety & Security Model

  9. Troubleshooting

  10. Contributing

  11. License

  12. Acknowledgments

  13. Author & Support


1. Overview#

Windows-LogOff (logoff.ps1) is a production-grade PowerShell 7+ script that provides system administrators with a reliable, auditable method for forcibly terminating user sessions on Windows systems. Unlike simple logoff.exe wrappers or Task Manager approaches, this utility implements a two-stage cleanup pipeline:

The script is fully compatible with PowerShell 7.0 and later, supports -WhatIf / -Confirm for dry-run testing, and produces structured PSCustomObject output suitable for logging, piping, and automation pipelines.


2. Features#


3. Prerequisites#

Requirement Detail
Operating System Windows 10, Windows 11, Windows Server 2016 through Server 2025
PowerShell Version PowerShell 7.0 or higher (PowerShell 7.5+ recommended)
Privileges Administrator (elevated) — required for session enumeration and process termination
Dependencies Built-in Windows utilities: quser / query user, logoff.exe
Execution Policy Script execution must be permitted (see Execution Policy)

4. Installation#

4.1 Clone the Repository#

git clone https://github.com/paulmann/Windows-LogOff.git
cd Windows-LogOff

4.2 Download Directly (No Git)#

Invoke-WebRequest -Uri "https://raw.githubusercontent.com/paulmann/Windows-LogOff/refs/heads/main/logoff.ps1" -OutFile ".\logoff.ps1"

4.3 Unblock the Script#

Windows may block downloaded scripts. Remove the Zone.Identifier mark-of-the-web:

Unblock-File -Path ".\logoff.ps1"

4.4 Set Execution Policy#

If script execution is restricted on your system:

# For current session only (recommended for testing)
Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process

# For current user permanently
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

4.5 Verify PowerShell Version#

$PSVersionTable.PSVersion
# Should display: 7.0 or higher

5. Configuration#

Windows-LogOff requires no configuration files. All behavior is controlled via command-line parameters at runtime. The script automatically detects:

Optional: Custom User Context#

For domain environments or when automatic context detection is ambiguous:

.\logoff.ps1 -UserName 'jsmith' -UserContext 'CONTOSO' -Verbose

This forces process owner matching against CONTOSO\jsmith instead of the default $env:COMPUTERNAME.


6. Usage#

6.1 Basic Syntax#

.\logoff.ps1 -UserName <string> [-Parameter ...]

6.2 Parameters#

Parameter Type Required Default Description
-UserName string Yes — Target username without domain prefix
-UserContext string No $env:COMPUTERNAME Computer or domain prefix for process owner matching
-SkipLogoff switch No $false Skip session logoff; only terminate processes
-SkipProcessTermination switch No $false Skip process termination; only log off sessions
-AllowCurrentSession switch No $false Allow logoff of the current controller session
-PostLogoffDelaySeconds int No 3 Delay between logoff and process sweep (0–120)
-Help / -? / -H switch No $false Display built-in help and exit
-Verbose switch No — Enable detailed diagnostic output
-WhatIf switch No — Show what would happen without making changes
-Confirm switch No — Prompt for confirmation before each action

6.3 Examples#

# Display built-in help
.\logoff.ps1 -Help

# Show help via Get-Help
Get-Help .\logoff.ps1 -Full
Get-Help .\logoff.ps1 -Examples

# Dry-run: see what would happen (no changes made)
.\logoff.ps1 -UserName 'md' -WhatIf

# Standard execution with verbose output
.\logoff.ps1 -UserName 'md' -Verbose

# Log off sessions only (skip process cleanup)
.\logoff.ps1 -UserName 'md' -SkipProcessTermination -Verbose

# Process cleanup only (skip session logoff)
.\logoff.ps1 -UserName 'md' -SkipLogoff -Verbose

# Allow termination of your own session (dangerous)
.\logoff.ps1 -UserName 'md' -AllowCurrentSession -Confirm:$false -Verbose

# Custom domain context for process matching
.\logoff.ps1 -UserName 'jsmith' -UserContext 'CONTOSO' -Verbose

# Export results to JSON for auditing
.\logoff.ps1 -UserName 'md' -Verbose | ConvertTo-Json -Depth 5 | Out-File "logoff-result.json"

7. How It Works#

7.1 Stage 1: Graceful Session Logoff#

The script begins by enumerating active user sessions using the Windows quser command (fallback to query user). It parses the output using regex to extract:

For each matching session, logoff.exe <SessionId> /SERVER:localhost is invoked. This triggers the following Windows subsystem behavior:

  1. csrss.exe sends WM_QUERYENDSESSION to all GUI processes in the session.

  2. Applications receive ~5 seconds (controlled by HungAppTimeout) to save data and exit cleanly.

  3. If a process hangs or ignores the message, Windows forcibly terminates it and destroys the session.

  4. All session-bound processes are cleaned up by the OS.

Important: logoff only affects processes inside the target session. Detached background processes, scheduled tasks, or Session 0 processes are NOT affected.

7.2 Stage 2: Residual Process Sweep#

After a configurable delay (default: 3 seconds), the script performs a system-wide process scan:

  1. Get-Process -IncludeUserName retrieves all running processes with owner information.

  2. Each process is filtered by UserName matching the target account.

  3. The current PowerShell host process ($PID) is automatically excluded for safety.

  4. Remaining processes are terminated via Stop-Process -Force (Win32 TerminateProcess).

TerminateProcess provides no grace period — the process is immediately unloaded from memory. This is the correct behavior for stuck or orphaned processes but will result in data loss for any unsaved work.


8. Safety & Security Model#

Windows-LogOff implements multiple layers of protection to prevent accidental self-destruction or unintended system damage:

Safety Measure Description
Current Session Skip The session running the script is never logged off unless -AllowCurrentSession is explicitly provided.
Current Process Skip The PowerShell host process ($PID) is excluded from force-termination.
Elevation Check The script validates administrator privileges at startup and aborts if not elevated.
Platform Check #Requires -Version 7.0 and $IsWindows validation prevent execution on unsupported systems.
Input Validation Username is validated with regex ^[\w.\-]+$ to prevent injection attacks.
WhatIf Support -WhatIf allows full simulation of all actions before any real changes are made.
Structured Results Every action produces an auditable PSCustomObject record with status, timestamp, and exit code.

Warning: This script is destructive by design. When executed, it will:

Always test with -WhatIf first. Never use on a user who may have unsaved documents unless the situation demands it.


9. Troubleshooting#

9.1 Common Issues#

"Session logoff stage failed: You cannot call a method on a null-valued expression"

This typically occurs when quser output cannot be parsed. Causes:

Fix: Run with -SkipLogoff -SkipProcessTermination -Verbose first to see what sessions are detected. Update to the latest script version which uses regex-based parsing.


"Cannot find process with IncludeUserName — access denied"

Get-Process -IncludeUserName requires elevated privileges.

Fix: Run PowerShell as Administrator. Right-click → "Run as Administrator".


"Script execution is disabled on this system"

The default execution policy on some Windows installations blocks scripts.

Fix:

Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Unblock-File -Path ".\logoff.ps1"

"The script skips my own session even though I want it gone"

By default, the current controller session is protected.

Fix: Use -AllowCurrentSession:

.\logoff.ps1 -UserName 'md' -AllowCurrentSession -Confirm:$false -Verbose

"Some processes remain after the script completes"

A small number of system-protected processes may not be terminable even by administrators. This is expected Windows behavior for critical system threads.

Fix: Review the structured output for processes marked Status: Failed with Access Denied messages. These are typically system-protected and safe to ignore.


9.2 Debugging Workflow#

# Step 1: Dry run to see what would happen
.\logoff.ps1 -UserName 'targetuser' -WhatIf -Verbose

# Step 2: Sessions only (safe)
.\logoff.ps1 -UserName 'targetuser' -SkipProcessTermination -Verbose

# Step 3: Full execution with JSON export
.\logoff.ps1 -UserName 'targetuser' -Verbose | ConvertTo-Json -Depth 5

10. Contributing#

Contributions are welcome! To contribute:

  1. Fork this repository.

  2. Create a feature branch: git checkout -b feature/your-feature-name.

  3. Make your changes following the existing code style (PSR-like formatting, script:-scoped helpers, structured output).

  4. Test thoroughly — always include -WhatIf testing before production use.

  5. Submit a Pull Request with a clear description of changes.

Code Style Guidelines#


11. License#

This project is distributed under the PolyForm Noncommercial License 1.0.0. See the LICENSE file for full terms.

In short: