If you're working with Microsoft's Terminal Services, you may know about the "Force single session" option. Essentially, turning this option on forces Terminal Services to allow just one session per user. If user Alice is logged on and another users authenticates as Alice, Alice's first session would be disconnected and taken over by "the other" Alice. Concurrent sessions of Alice and Bob are not affected, that's what Terminal Services are for.
The "Force single session" option is has it's right to exist, but what happens if it's turned off? Going back to the example above (Alice is already logged on while the second Alice is authenticating), this would result in two Alice sessions. Imagine a scenario where it's useful to allow certain users to have multiple concurrent sessions but disallow multiple sessions for others: Two administrators could to their job but ordinary users are forced to live in a single session.
It is not possible to achieve these semantics just by leveraging the built-in functionality of Terminal Services. However, you can use Group Policy logon scripts to check if a user is already logged on to another sessions. Here's an example of such a script I've written using Windows PowerShell.
#
# SingleSession.ps1
#
# Logon script for users with limited session count.
#
#
# Global settings.
#
# Array of users with limited session count.
$ScriptUsers = "user1", "user2", "user3"
# Maximum number of sessions for each user above.
$MaxSessions = 1
# Logoff executable.
$LogoffCommand = $env:systemroot + "\system32\logoff.exe"
# Trim the user name.
$CurrentUser = $env:username.Trim()
# Welcome message.
Write-Host "Welcome to this server," $CurrentUser
# Cancel if a user that's not contained in $ScriptUsers logs on.
if ($ScriptUsers -inotcontains $CurrentUser)
{
Write-Host "You do not need to run this script."
return
}
# Get the number of sessions the current user owns.
$WmiUserNameExpression = "*,Name=`"" + $CurrentUser + "`""
$Sessions = get-wmiobject Win32_LoggedOnUser | `
select Antecedent | `
where { $_.Antecedent -ilike $WmiUserNameExpression } | `
measure-object
# Log off the user if $MaxSessions is exceeded.
if ($Sessions.Count -gt $MaxSessions)
{
Write-Host "You are already logged on in another session."
# Show a message box informing the user that he will be logged off.
$MessageBox = New-Object -ComObject WScript.Shell
$MessageBox.Popup( `
"There's another user connected using account " + $CurrentUser + ". Please try to reconnect later.`n`nYour session will be ended now. This window will be closed auomatically after 60 seconds.", `
# Close message box after 60 seconds.
60, `
"Account is being used", `
# Error Icon.
16)
# Force logoff.
&$LogoffCommand
}
You just need to set up the logon script using Group Policy.
- Enable multiple Terminal Services sessions using the Terminal Services Configuration snap-in (tscc.msc).
- Open the Group Policy Editor by running gpedit.msc.
- Assign the user logon script.
Because the PowerShell file extension (ps1) is not linked to the PowerShell runtime, I wrote a little helper cmd file that simply starts the PowerShell script. Enter this cmd file in the Logon Scripts editor.
rem SingleSession.cmd
rem Starts SingleSession.ps1.
@powershell.exe -noprofile -command SingleSession.ps1
Now, at each logon, the script checks if a user contained in $ScriptUsers logs on. If this is the case and the user already owns another session, a message box is displayed saying that he or she will be logged off. After a timeout of 60 seconds the message box closes and the session will be ended.
Basically, I rewrote the SingleSession script that existed as a cmd file for years in PowerShell because I think it's time to switch to the new well thought-out command line. PowerShellIDE, though beta-ish, has done a decent job supporting me during the short development process.