A better way to log off users remotely using PowerShell
If you read my last post, you probably caught onto some of my nostalgia. Well, since I cut my PowerShell teeth in the service desk, I’ve got a lot of tools I’ve written that I’d like to continue sharing.
Remember how I wrote about finding logged on users remotely? I promised to also share its partner in crime, Close-ActiveSessions
. You can finally relax because today is the day that I do! Check it out.
rwinsta
Much like Get-ActiveSessions
, Close-ActiveSessions
depends on a Windows executable. In this case, rwinsta
.
Prereqs
This has the same requirement for a registry key:
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server]
"AllowRemoteRPC"=dword:00000001
And to set that with PowerShell, we simply:
$ComputerName = 'Computer' #replace with the computer name
$LMtype = [Microsoft.Win32.RegistryHive]::LocalMachine
$LMkey = "SYSTEM\CurrentControlSet\Control\Terminal Server"
$LMRegKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($LMtype,$ComputerName)
$regKey = $LMRegKey.OpenSubKey($LMkey,$true)
If($regKey.GetValue("AllowRemoteRPC") -ne 1)
{
$regKey.SetValue("AllowRemoteRPC",1)
Start-Sleep -Seconds 1
}
$regKey.Dispose()
$LMRegKey.Dispose()
Syntax
This is going to look really familiar if you read the previous post:
rwinsta /server:ServerName ID /V
The difference here is that we have to specify a session ID, which is ID
in the above example. And, since this isn’t natively PowerShell, we can’t pipe qwinsta
output to rwinsta
like:
qwinsta /server:ServerName | rwinsta /server:ServerName
But we can make that work! It just takes a little function writing.
Wrapping rwinsta
Before we dive into building another function, you should know that functions are amazing and I love them. I even wrote a blog post about them!
Output is easy to deal with
Unlike Get-ActiveSessions
where we had to deal with output from qwinsta
, rwinsta
only outputs a string like the following:
Resetting session ID 1
Session ID 1 has been reset
So we`ll skip parsing the output and simply deal with formatting the input.
Input is easy to deal with
There are only 2 things we need to make Close-ActiveSessions
work, the name of the remote computer and the ID of the session. So as long as we’ve got that in the param()
block, we can run the command like:
rwinsta /server:$ComputerName $ID /V
The final product
I’m going to skip all the function stuff and refer you to my post on building functions. Here’s what the above steps look like wrapped up as a function that can take pipeline input, test for connectivity, fix the registry if needed, and log off users:
Function Close-ActiveSessions {
Param(
[Parameter(
Mandatory = $true,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
Position = 1
)]
[string]$ComputerName,
[Parameter(
Mandatory = $true,
ValueFromPipeline = $true,
ValueFromPipelineByPropertyName = $true,
Position = 2
)]
[int]$ID
)
Begin {}
Process {
If (!(Test-Connection $ComputerName -Quiet -Count 1)) {
Write-Error -Message "Unable to contact $ComputerName. Please verify its network connectivity and try again." -Category ObjectNotFound -TargetObject $ComputerName
Return
}
If([bool](([System.Security.Principal.WindowsIdentity]::GetCurrent()).groups -match "S-1-5-32-544")){ #check if user is admin, otherwise no registry work can be done
#the following registry key is necessary to avoid the error 5 access is denied error
$LMtype = [Microsoft.Win32.RegistryHive]::LocalMachine
$LMkey = "SYSTEM\CurrentControlSet\Control\Terminal Server"
$LMRegKey = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($LMtype,$Name)
$regKey = $LMRegKey.OpenSubKey($LMkey,$true)
If($regKey.GetValue("AllowRemoteRPC") -ne 1){
$regKey.SetValue("AllowRemoteRPC",1)
Start-Sleep -Seconds 1
}
$regKey.Dispose()
$LMRegKey.Dispose()
}
rwinsta.exe /server:$ComputerName $ID /V
}
End {}
}
Close-ActiveSessions
If the above block doesn’t copy/paste for you very well, be sure to check out my Utilities repo on Github. This will be there as well as others that I’ve shared.
Simple usage
If you already know the ID of the session that you want to logoff, you can simply:
Close-ActiveSessions ComputerName -ID 1
And, if successful, it will return:
Resetting session ID 1
Session ID 1 has been reset
But the whole point of PowerShell is to do much cooler things than that!
Advanced usage
We’ve got the power of the pipeline in PowerShell to leverage! So lets do some cooler commands.
How about finding all servers in your environment that have a particular user logged into them, and log that user off?
Get-ADComputer -Filter {OperatingSystem -like '*Server*'} | Get-ActiveSessions | Where-Object UserName -eq 'Anthony' | Close-ActiveSessions
(Notice that was simply an extension of an example from Get-ActiveSessions?)
Or maybe you’ve got some users leaving sessions open on a RDS or Citrix server and you’d like to log off all of them?
Get-ADComputer -Filter {Name -like 'RDS*'} | Get-ActiveSessions | Close-ActiveSessions
You can do some really powerful things with PowerShell by simply wrapping an executable like rwinsta.
Conclusion
I hope you found this as useful as I have over the years. I’ve definitely save myself enough time with this cmdlet to easily justify sharing it through a blog post!
If you liked it, let me know! Comment, tweet at me, or drop me an email if you are having some trouble with it.
Leave a Comment