Reverse Folder Redirection - OneDrive files on terminal servers without OneDrive
Have you ever tried to setup OneDrive on a terminal server? No? Good. Don't. It's a bad idea all around. While OneDrive is great when deployed on local workstation, it's problems, problems and more problems when on a terminal server.
If you have a large amount of synced files, and a lot of users using the server, you'll quickly find out that OneDrive will gobble up all your CPU time. Not only that, but it's not the most stable piece of software on terminal servers, it tends to crash, a lot.
Like me, you've probably wondered "what can I do?" and like me, spent hours googling potential solutions, to no help. Everywhere you'll find the same answers, suggesting mapped user drives, or that the user not save stuff on there. In some cases, it might be "good enough", but it wasn't good enough for me.
You're going to need some background to understand why and how this works, read on.
I was doing this project for one of our customers, a total overhaul. They had an onsite ESXI host. On it was an exchange server, a domain controller, a terminal server and a data server. This is fine for most SMBs, but they were growing rapidly and wanted to go "enterprise 4.0". Not only that but the host showed signs of fatigue, the exchange was a massive walking vulnerability and the terminal server was awfully slow, which is a lot of lost productivity.
We went the way of hosted VMs in our private cloud, specifically, one domain controller and one "application" VM. Full Office 365, SharePoint, OneDrive, Teams, etc.
They have 2 "legacy" applications that really only work well on terminal servers, we went with Parallels Remote Application Server, it's fantastic you'll see.
Now picture this: we have everyone on brand new desktops/laptops, everything is fully migrated to Office 365, no more data server for the file shares, synced SharePoint sites to the desktops by GPO. But, those applications, they have to import and export data yes? How do you do it? OneDrive on that terminal server? That terminal server that the users actually never see the desktop of?
No sir, here comes reverse folder redirection.
We make the following assumptions:
- Every end user's desktop has C:\Users shared under the share %hostname%\usr$ (true, this share gets created on every new desktop according to the new user/hardware playbook). You can do this from command line: net share usr$=C:\Users /Grant:"Domain Users", FULL"
- There's always a VPN connection to the corporate network (we use Barracuda Cloudgen Firewalls, the VPN client is configured to SSO at windows logon)
- Every user has Desktop + Documents + a SharePoint document library synced (true, we have a GPO)
So, now that all that is setup, what do we do?
We run the ReverseFolderRedirection.ps1 script at logon on the terminal server. This is a script that's basically this gist, which adds a command to redirect folders in PowerShell, with some other things tacked on.
What this scripts does is:
- Find out what's the name of the RDS Client -> $ClientName (this is the name of the MACHINE)
- Redirects Desktop to \\$ClientName\\usr$\$env:Username\OneDrive - $OrgName\Desktop
- Redirects Documents to \\$ClientName\\usr$\$env:Username\OneDrive - $OrgName\Documents
- Creates a mapped drive that points to \\$ClientName\\usr$\$env:Username\$OrgName\$SiteName - Documents
That's all that's required, really. There's a few important parts, let's review them.
First, we need to get the RDS Session ID, we do this by getting the SessionId from the process in which the script is running.
function Get-RDSSessionId
{
[CmdletBinding()]
Param
(
# Identifies a user name (default: current user)
[Parameter(ValueFromPipeline = $true)]
[System.String]
$UserName = $env:USERNAME
)
$returnValue = $null
try
{
# $pid is powershell's automatic variable for its own pid
return (Get-Process -pid $pid).SessionId
}
catch
{
$_.Exception | Write-Error
}
New-Object psobject $returnValue
}
Now that we've got this, we can get the RDS Client Name, which is the client's computer hostname.
We do this with this function, which is arguably magic:
function Get-RDSClientName
{
[CmdletBinding()]
Param
(
# Identifies a RDS session ID
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[System.String]
$SessionId
)
$returnValue = $null
$regKey = 'HKCU:\Volatile Environment\{0}' -f $SessionId
try
{
$ErrorActionPreference = 'Stop'
$regKeyValues = Get-ItemProperty $regKey
$sessionName = $regKeyValues | ForEach-Object {$_.SESSIONNAME}
if ($sessionName -ne 'Console')
{
$returnValue = $regKeyValues | ForEach-Object {$_.CLIENTNAME}
}
else
{
Write-Warning 'Console session'
# $returnValue = $env:COMPUTERNAME
}
}
catch
{
$_.Exception | Write-Error
}
New-Object psobject $returnValue
}
This functions gets the hostname from the registry, returns an error if it's a console session, and that's it.
What's left is easy, it's redirecting the actual folders:
## Get the client's hostname
$ClientName = Get-RDSSessionId | Get-RDSClientName
## Organisation Name, as seen on a computer
$OrgName = "YOUR ORG NAME"
$RootPath = "\\$ClientName\usr$\$env:Username\OneDrive - $OrgName"
## Test for English and French, we speak french here
$DesktopName = "Bureau"
if (Test-Path -Path "$RootPath\Desktop") {
$DesktopName = "Desktop"
}
Set-KnownFolderPath -KnownFolder 'Desktop' -Path "$RootPath\$DesktopName" #Redirect it
Set-KnownFolderPath -KnownFolder 'Documents' -Path "$RootPath\Documents" #Redirect it
#Map the document library
Get-PSDrive Y | Remove-PSDrive
New-PSDrive -Name "Y" -PSProvider "FileSystem" -Root "\\$ClientName\usr$\$env:Username\$OrgName\SITE NAME - Documents" -Persist
That's it, that's all there is to it.
This is very stable, it's been running for months at this customer now, with 0 issue. Well documented, it's very easy to support too.
"What about security?" you might ask. As long as your permissions are properly set on your usr$ shares, there's no problems. The most someone could do is create folders and files and fill up some drives, but if an attacker is already that far in your network, you've got bigger things to worry about.
Antoine Lecompte