A better way to log off users remotely using PowerShell

5 minute read

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