|
| 1 | +# Invoke-UserSignOutAndBlock.ps1 |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +Immediately blocks sign-in, revokes all active sessions and refresh tokens, and optionally disables Entra ID-registered devices for one or more Microsoft 365 / Entra ID accounts. Designed for offboarding, incident response, and account compromise scenarios where rapid access revocation is required. |
| 6 | + |
| 7 | +## Features |
| 8 | + |
| 9 | +- **Block sign-in** — Sets `AccountEnabled = $false` so no new authentication attempts succeed |
| 10 | +- **Revoke sessions** — Calls Microsoft Graph to invalidate all refresh tokens and active sessions immediately |
| 11 | +- **Device reporting** — Lists all Entra ID-registered and Entra ID-joined devices owned by each account |
| 12 | +- **Device disablement** — Optionally sets `AccountEnabled = $false` on each owned device in Entra ID (`-DisableDevices`) |
| 13 | +- **Multiple input methods** — CSV file, in-memory array, or single UPN/Object ID |
| 14 | +- **WhatIf support** — Preview all actions without making changes |
| 15 | +- **Timestamped CSV results** — Full per-account report of every action taken |
| 16 | + |
| 17 | +## Prerequisites |
| 18 | + |
| 19 | +### PowerShell Version |
| 20 | +- PowerShell 5.1 or PowerShell 7+ |
| 21 | + |
| 22 | +### Required Modules |
| 23 | + |
| 24 | +```powershell |
| 25 | +Install-Module Microsoft.Graph.Authentication -Scope CurrentUser |
| 26 | +Install-Module Microsoft.Graph.Users -Scope CurrentUser |
| 27 | +Install-Module Microsoft.Graph.Identity.DirectoryManagement -Scope CurrentUser |
| 28 | +``` |
| 29 | + |
| 30 | +### Required Permissions (Microsoft Graph) |
| 31 | + |
| 32 | +| Permission | Purpose | |
| 33 | +|---|---| |
| 34 | +| `User.ReadWrite.All` | Block sign-in, read user details | |
| 35 | +| `Directory.ReadWrite.All` | Revoke sign-in sessions | |
| 36 | +| `Device.ReadWrite.All` | Disable Entra ID-registered devices | |
| 37 | + |
| 38 | +## Parameters |
| 39 | + |
| 40 | +### Input Parameters (Mutually Exclusive) |
| 41 | + |
| 42 | +| Parameter | Type | Description | |
| 43 | +|---|---|---| |
| 44 | +| `-CsvPath` | String | Path to CSV file. Required column: `Identity`. Optional column: `Reason`. | |
| 45 | +| `-UserArray` | Object[] | Array of PSCustomObjects/hashtables with at minimum an `Identity` property. | |
| 46 | +| `-Identity` | String | Single UPN or Entra Object ID for a one-off operation. | |
| 47 | + |
| 48 | +### Behavior Switches |
| 49 | + |
| 50 | +| Parameter | Description | |
| 51 | +|---|---| |
| 52 | +| `-DisableDevices` | Also disable all Entra ID-registered/joined devices owned by each account. Without this switch, devices are reported but not modified. | |
| 53 | +| `-SkipBlockSignIn` | Skip setting `AccountEnabled = $false`. Useful when you only want to revoke sessions. | |
| 54 | +| `-SkipRevokeSession` | Skip session revocation. Useful when you only want to block sign-in or disable devices. | |
| 55 | +| `-WhatIf` | Show what changes would be made without applying them. | |
| 56 | + |
| 57 | +### Output |
| 58 | + |
| 59 | +| Parameter | Default | Description | |
| 60 | +|---|---|---| |
| 61 | +| `-OutputDirectory` | `C:\Reports\CSV_Exports` | Directory where the results CSV is saved. | |
| 62 | +| `-GenerateTemplate` | — | Creates a blank CSV template and exits. | |
| 63 | + |
| 64 | +## CSV Format |
| 65 | + |
| 66 | +### Input CSV |
| 67 | + |
| 68 | +```csv |
| 69 | +Identity,Reason |
| 70 | +jdoe@contoso.com,Offboarding - last day 2026-03-13 |
| 71 | +jsmith@contoso.com,Account compromise - INC0012345 |
| 72 | +``` |
| 73 | + |
| 74 | +| Column | Required | Description | |
| 75 | +|---|---|---| |
| 76 | +| `Identity` | Yes | UPN or Entra Object ID | |
| 77 | +| `Reason` | No | Logged to the results report for audit purposes | |
| 78 | + |
| 79 | +### Output CSV Columns |
| 80 | + |
| 81 | +| Column | Description | |
| 82 | +|---|---| |
| 83 | +| `Identity` | Input identity value | |
| 84 | +| `DisplayName` | Resolved display name from Entra ID | |
| 85 | +| `Reason` | Reason provided in input | |
| 86 | +| `SignInBlocked` | Success / Failed / Skipped / WhatIf | |
| 87 | +| `SessionsRevoked` | Success / Failed / Skipped / WhatIf | |
| 88 | +| `DevicesFound` | Count of Entra ID-registered devices | |
| 89 | +| `DevicesDisabled` | Count of devices disabled (requires `-DisableDevices`) | |
| 90 | +| `DeviceNames` | Pipe-delimited list of device names and OS | |
| 91 | +| `Status` | Success / CompletedWithErrors / Failed | |
| 92 | +| `ErrorDetails` | Error messages if any step failed | |
| 93 | +| `Timestamp` | Time the account was processed | |
| 94 | + |
| 95 | +## Usage Examples |
| 96 | + |
| 97 | +### Single Account — Block and Revoke Sessions |
| 98 | + |
| 99 | +```powershell |
| 100 | +.\Invoke-UserSignOutAndBlock.ps1 -Identity "jdoe@contoso.com" |
| 101 | +``` |
| 102 | + |
| 103 | +### Single Account — Also Disable Devices |
| 104 | + |
| 105 | +```powershell |
| 106 | +.\Invoke-UserSignOutAndBlock.ps1 -Identity "jdoe@contoso.com" -DisableDevices |
| 107 | +``` |
| 108 | + |
| 109 | +### Bulk from CSV |
| 110 | + |
| 111 | +```powershell |
| 112 | +.\Invoke-UserSignOutAndBlock.ps1 -CsvPath "C:\Data\offboard.csv" -DisableDevices |
| 113 | +``` |
| 114 | + |
| 115 | +### From Array (scripted/automation scenarios) |
| 116 | + |
| 117 | +```powershell |
| 118 | +$accounts = @( |
| 119 | + [PSCustomObject]@{ Identity = "jdoe@contoso.com"; Reason = "Offboarding" } |
| 120 | + [PSCustomObject]@{ Identity = "jsmith@contoso.com"; Reason = "Account compromise" } |
| 121 | +) |
| 122 | +.\Invoke-UserSignOutAndBlock.ps1 -UserArray $accounts -DisableDevices |
| 123 | +``` |
| 124 | + |
| 125 | +### Preview Without Making Changes |
| 126 | + |
| 127 | +```powershell |
| 128 | +.\Invoke-UserSignOutAndBlock.ps1 -CsvPath "C:\Data\offboard.csv" -WhatIf |
| 129 | +``` |
| 130 | + |
| 131 | +### Generate CSV Template |
| 132 | + |
| 133 | +```powershell |
| 134 | +.\Invoke-UserSignOutAndBlock.ps1 -GenerateTemplate -OutputDirectory "C:\Data" |
| 135 | +``` |
| 136 | + |
| 137 | +### Revoke Sessions Only (Don't Block Sign-In) |
| 138 | + |
| 139 | +```powershell |
| 140 | +.\Invoke-UserSignOutAndBlock.ps1 -Identity "jdoe@contoso.com" -SkipBlockSignIn |
| 141 | +``` |
| 142 | + |
| 143 | +### Block Sign-In Only (Don't Revoke Sessions) |
| 144 | + |
| 145 | +```powershell |
| 146 | +.\Invoke-UserSignOutAndBlock.ps1 -Identity "jdoe@contoso.com" -SkipRevokeSession |
| 147 | +``` |
| 148 | + |
| 149 | +## Important Behavior Notes |
| 150 | + |
| 151 | +### Session Revocation vs. Access Token Expiry |
| 152 | +Revoking sessions invalidates all **refresh tokens** immediately — the user cannot silently renew access. However, existing short-lived **access tokens** (typically 1-hour lifetime) remain valid until they naturally expire. Blocking sign-in (`AccountEnabled = $false`) prevents any renewal of those tokens, so combining both actions is the most effective approach. |
| 153 | + |
| 154 | +### Device Disablement Scope |
| 155 | +Disabling a device in Entra ID prevents it from authenticating to cloud services. However: |
| 156 | +- The user's **local Windows session** on the device is not immediately terminated |
| 157 | +- The device is **not wiped or retired** from Intune — use the Intune portal or a dedicated script for remote wipe |
| 158 | + |
| 159 | +### What `-DisableDevices` Targets |
| 160 | +Only devices where the user is the **registered owner** in Entra ID are affected. Shared/unowned devices used by the account are not modified. |
| 161 | + |
| 162 | +## Output |
| 163 | + |
| 164 | +``` |
| 165 | +Output file: C:\Reports\CSV_Exports\UserSignOutAndBlock_Results_YYYYMMDD_HHmmss.csv |
| 166 | +``` |
| 167 | + |
| 168 | +## Common Issues & Troubleshooting |
| 169 | + |
| 170 | +### "The property 'Count' cannot be found on this object" |
| 171 | +This was a known issue with PowerShell 5.1 when `Get-MgUserOwnedDevice` returns a single object. Fixed in v1.0 by wrapping the result in `@()`. |
| 172 | + |
| 173 | +### Module Not Found |
| 174 | +```powershell |
| 175 | +Install-Module Microsoft.Graph.Authentication, Microsoft.Graph.Users, Microsoft.Graph.Identity.DirectoryManagement -Scope CurrentUser |
| 176 | +``` |
| 177 | + |
| 178 | +### Insufficient Permissions |
| 179 | +Ensure the connecting account (or app registration) has `User.ReadWrite.All`, `Directory.ReadWrite.All`, and `Device.ReadWrite.All` granted in Entra ID. |
| 180 | + |
| 181 | +### User Not Found |
| 182 | +The script will log a `Failed` status for accounts that cannot be resolved and continue processing remaining accounts. |
| 183 | + |
| 184 | +## Version History |
| 185 | + |
| 186 | +- **v1.0** (2026-03-13): Initial release — block sign-in, revoke sessions, report/disable devices; CSV/array/single input; WhatIf support |
| 187 | + |
| 188 | +## See Also |
| 189 | + |
| 190 | +- [Microsoft Docs: Revoke Sign-In Sessions](https://learn.microsoft.com/en-us/graph/api/user-revokesigninsessions) |
| 191 | +- [Microsoft Docs: Update User (AccountEnabled)](https://learn.microsoft.com/en-us/graph/api/user-update) |
| 192 | +- [Microsoft Docs: Update Device](https://learn.microsoft.com/en-us/graph/api/device-update) |
| 193 | +- [[Set-EmailToSharedAccount]] — Convert offboarded mailboxes to shared and remove licenses |
| 194 | +- [[Set-SMTPForward]] — Configure SMTP forwarding during offboarding or migration |
0 commit comments