Easy user to user shadowing in RD 2012 R2

The problem: After migrating some users to RDS servers running Windows Server 2012 R2, they realised that they are no longer able to shadow each others’ sessions. Research indicated that this would be difficult to get around for a couple of reasons;

  1. Shadowing users can only be done via Server Manager – there is no ‘tsadmin.msc’ which can be given to users as a shortcut
  2. Some hard to grant permissions are required in order to query the RD Connection Broker without being granted Domain Admin privileges

The solution: A security group, a command to grant the required permission on the RDP-Tcp connector, and a Powershell script the staff can use to initiate the shadowing connection. The script ended up being more user friendly that the original method, and does not require users to be granted any permissions on the RD connection broker.

In RDP v8.1, user shadowing is handled quite differently. Server Manager really only serves to help you find where a user is logged in, and what their session ID is on that server. It then passes the information to a simple ‘mstsc’ command line to connect to the user;

mstsc /v:<ServerName> /shadow:<SessionID> /control

This means if we can get the information to pass to the command through other means (eg. Powershell), we can avoid using Server Manager altogether. You can find some more information on the differences here.

Going from the top, here’s what you need to do:

  1. Create a new security group, adding to it all of the users who need to be able to shadow other users. We’ll call it ‘shadow users’ in this example.
  2. Grant shadowing permissions to the group on each of your RDS servers. To do this, you will need to run the below command from an elevated command prompt on each RDS server (replace the group name ‘shadow users’ at the end of the command with your own group name):
    • wmic /namespace:\\root\CIMV2\TerminalServices PATH Win32_TSPermissionsSetting WHERE (TerminalName=”RDP-Tcp”) CALL AddAccount “domain\shadow users”,2
  3. Customise the below script as per the instructions in the comments, save it as a .ps1 file and move it to a shared location (eg. mapped drive or network share)
    • You can download the latest version of the script in a ZIP file here

Note: The original script has been updated to fix two issues;

  • Short usernames could be matched inside another username (eg. ‘BLo’ inside of ‘JBloggs’) causing it to connect to the wrong user.
  • If the user’s session ID was above 100, the script would connect to the wrong user.
# First off we define the 'CheckServer' function which will be run against each server
# This will check if the user has been found yet by the script, if not, it will continue through each server
# If found, edit string to isolate session ID (cut off first 44 characters, remove all but the first 4 characters, remove any spaces on either side)

Function CheckServer
{
if ($script:sessiontemp -eq $null){
$script:sessiontemp = query session /server:$args | Select-String $script:shadowuser
if($script:sessiontemp -ne $null){
$script:sessiontemp = $script:sessiontemp | Out-String
$script:sessionid = $script:sessiontemp.Substring(44).Remove(4).Replace(" ","")
$script:sessionhost = $args
}
}
}

# Nice fancy header

Write-Host "=========================`nRemote Desktop User Shadow`n=========================`n"

# Prompt user for the username to shadow
# Add a space to each side so the string is not incorrectly found inside another person's username

$shadowuser = Read-Host "Enter the username you wish to shadow (eg. jsmith) and press Enter"
$shadowuser = " " + $shadowuser + " "

Write-Host "`nSearching for user...`n"

# The function below will need to be run against each of the RDS servers in your farm
# Edit the list below to reflect the FQDN of each of your RDS servers, add extra entries if you have more servers

CheckServer rds01.domain.com
CheckServer rds02.domain.com
CheckServer rds03.domain.com

# If user not found on any of the above servers, exit the script

if ($sessiontemp -eq $null)
{
Write-Host "`nUsername incorrect or user is not logged in`n"
Pause
Exit
}

# Execute shadow command

mstsc /shadow:$sessionid /v:$sessionhost /control

exit
  1. Create a shortcut pointing to your script and distribute to your users (I used a Group Policy Preferences GPO with item level targeting, giving it only to the users in the required security group)
    • Target: C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe “P:\ScriptLocation\ScriptName.ps1”
    • Start In: C:\Windows\System32\WindowsPowerShell\v1.0
  2. Your users should then be able to double-click the icon to launch the script, enter the username for whom they wish to shadow and be on their merry way

Note: Your Powershell execution policy will need to be set to at least ‘RemoteSigned’ on your RDS servers for your users to execute the script. This guide / script is given with no guarantees of any kind.

Download link

17 thoughts on “Easy user to user shadowing in RD 2012 R2

  1. Hi, personaly i still use tsadmin.msc in Windows server 2012 R2 RDS but when i try to shadow a session i have an error “access denied”. I have the same thing with your script. Have you got any idea ?
    Anyway, you can use tsadmin.msc on Windows Server 2012 R2. You have to copy, from a Windows Server 2008 R2 to your Windows Server 2012 R2 RDS (you can create a directory in system32), some files : tsadmin.dll, tsadmin.msc umcRes.dll wts.dll.
    Then, in a .reg file, copy the following and execute it. Now you can use the tsadmin.msc.

    Windows Registry Editor Version 5.00

    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MMC\SnapIns\FX:{3FCE72B6-A31B-43ac-ADDA-120E1E56EB0F}]

    “ApplicationBase”=hex(2):43,00,3a,00,5c,00,57,00,69,00,6e,00,64,00,6f,00,77,00,\

    73,00,5c,00,53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,00,00

    “About”=”{00000000-0000-0000-0000-000000000000}”

    “VersionStringIndirect”=”@C:\\Windows\\System32\\umcRes.dll,-106”

    “ProviderStringIndirect”=hex(2):40,00,43,00,3a,00,5c,00,57,00,69,00,6e,00,64,\

    00,6f,00,77,00,73,00,5c,00,53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,\

    5c,00,75,00,6d,00,63,00,52,00,65,00,73,00,2e,00,64,00,6c,00,6c,00,2c,00,2d,\

    00,31,00,30,00,32,00,00,00

    “NameString”=”Remote Desktop Services Manager”

    “HelpTopic”=hex(2):25,00,73,00,79,00,73,00,74,00,65,00,6d,00,72,00,6f,00,6f,00,\

    74,00,25,00,5c,00,68,00,65,00,6c,00,70,00,5c,00,74,00,73,00,5f,00,6d,00,61,\

    00,6e,00,61,00,67,00,65,00,72,00,2e,00,63,00,68,00,6d,00,00,00

    “AssemblyName”=”tsadmin”

    “RuntimeVersion”=”v2.0.50215”

    “Description”=”Manage Remote Desktop Services sessions”

    “DescriptionStringIndirect”=hex(2):40,00,43,00,3a,00,5c,00,57,00,69,00,6e,00,\

    64,00,6f,00,77,00,73,00,5c,00,53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,\

    00,5c,00,75,00,6d,00,63,00,52,00,65,00,73,00,2e,00,64,00,6c,00,6c,00,2c,00,\

    2d,00,31,00,30,00,34,00,00,00

    “LinkedHelpTopics”=hex(2):25,00,73,00,79,00,73,00,74,00,65,00,6d,00,72,00,6f,\

    00,6f,00,74,00,25,00,5c,00,68,00,65,00,6c,00,70,00,5c,00,74,00,73,00,5f,00,\

    6d,00,61,00,6e,00,61,00,67,00,65,00,72,00,2e,00,63,00,68,00,6d,00,00,00

    “NameStringIndirect”=hex(2):40,00,43,00,3a,00,5c,00,57,00,69,00,6e,00,64,00,6f,\

    00,77,00,73,00,5c,00,53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,00,\

    75,00,6d,00,63,00,52,00,65,00,73,00,2e,00,64,00,6c,00,6c,00,2c,00,2d,00,31,\

    00,30,00,33,00,00,00

    “IconIndirect”=hex(2):40,00,43,00,3a,00,5c,00,57,00,69,00,6e,00,64,00,6f,00,77,\

    00,73,00,5c,00,53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,5c,00,75,00,\

    6d,00,63,00,52,00,65,00,73,00,2e,00,64,00,6c,00,6c,00,2c,00,2d,00,31,00,31,\

    00,31,00,00,00

    “FxVersion”=”2.0.1.7”

    “Type”=”Microsoft.TerminalServices.Monitor.SnapIn.TSManagerSnapIn, tsadmin, Version=6.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35”

    “FolderBitmapsColorMask”=dword:00000000

    “ModuleName”=”tsadmin.dll”

    “Provider”=”Microsoft Corporation”

    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MMC\SnapIns\FX:{3FCE72B6-A31B-43ac-ADDA-120E1E56EB0F}\NodeTypes]

    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MMC\SnapIns\FX:{3FCE72B6-A31B-43ac-ADDA-120E1E56EB0F}\Standalone]

    Bye !

    1. Wow, didn’t know you could do that. Thanks!

      Have you completed step 2 – running the wmic command to add the shadowing permission for your users / security group?

      You may also want to check your remote control settings in Group Policy; I’ve got the following setting configured on the RDSH servers:

      Computer Configuration > Policies > Administrative Templates > Windows Components > Remote Desktop Services > Remote Desktop Session Host > Connections
      Set rules for remote control of Remote Desktop Services user sessions: Full control with user’s permission

      Ryan

  2. Hi. Thank you for this tool!
    I have been working on getting this script to work on our server because i have a user which needs to shadow others users to give instructions.
    Everything you wrote above is working. But i seem to have an issue with user initials.
    When want to shadow the user “Domain\KB” it finds the user “Domain\KBO” and asks for for a session. I have also tried where the user “Domain\SB” finds “Domain\ASB”.
    How can i fix this ?

    1. Hi Jacob,

      Yes it looks like an issue with the ‘findstr’ command – if the same consecutive letters exist in more than one username, it will connect to the first match it comes across.

      I’ll try and tweak it tonight and let you know if I can come up with a fix.

      Ryan

        1. Hi Jacob,

          I’ve written a new version of the script to work around this issue, however I just realised it will have the same problem if both of the matches for the given username are logged into the same server.

          You’re welcome to try the new script as it might alleviate the issue a bit, however I’ll need to work on it a bit longer to fully fix the problem.

          Here’s the link: http://puters.net/downloads/ShadowUser2012_v3.zip

          Regards,
          Ryan

          1. Hi Jacob,

            I think I have a fixed version which should work for you. I have changed out the ‘findstr’ for a ‘Select-String’ command, and changed the script so it adds spaces to either side of the username when searching for it. That way it cannot find the given username inside another username.

            Please give it a test and let me know if it works OK. Link is below;

            http://puters.net/downloads/ShadowUser2012_v4.zip

            Cheers,
            Ryan

    1. Or translated from my language it says: There is no connection to the specified session.
      But i believe it is the same error as mentioned above.

  3. Hello,

    When I try to use the command “wmic /namespace:\\root\CIMV2\TerminalServices PATH Win32_TSPermissionsSetting WHERE (TerminalName=”RDP-Tcp”) CALL AddAccount “domain\shadow users”,2”

    it gives me an error:

    ERROR:
    Description: Invalid query

    I have changed domain\group and I run it in an elevated prompt.
    Server is 2012R2.

    Any ideas?

    Thx

    1. If you’ve copied and pasted the command, you might want to try deleting and re-entering the quote (“) symbols in notepad first. Sometimes these can become invalid when copied from a website.

Leave a Reply

Your email address will not be published. Required fields are marked *