Recently a coworker and I were tasked with migrating the My Documents folder of all users to a central server. The obvious comes to mind for migrating users data; data backup. However, the true reason we were migrating our users files was to simplify workstation management, particularly when we needed to replace a user’s workstation. If the files are stored on the server, there is no need for us to copy data from one workstation to another. You might be thinking, what about the Desktop and Favorites? I’ll have an article on that topic soon.
So, why write this article? Well, we wanted to make sure that user data was not accessible by anyone in the organization except that user and the backup process. This required custom permissions for the user folder, permissions that are not typical.
We used the GPO settings for My Documents redirection, setting the My Documents location to %HOMEPATH%. Obviously this required that the home path must be set for each and every user in Active Directory. The common way to accomplish this is to modify the users and set the home path to the desired server path. This would create the folder automatically for each user and assign full permissions to the user. It also sets Administrators with full permission. Bah.
PowerShell is, well, powerful
The solution that we came up with was to write a PowerShell script that would accomplish the same task as adding a home path in Active Directory. The only difference is that the script sets the folder permissions that we wanted.
The script accomplishes three tasks based on only providing it an Active Directory account, such as domain\user. The first task is to create the user folder on the server defined in the script. The second sets the permissions on that folder, removing Administrators and then adding SYSTEM and the user as Full Control. It also adds the user as the Owner of the folder. Third, it updates Active Directory, modifying the user’s home path to the newly created folder on the server.
The Script
Before I share the script, it comes with a disclaimer. Use at your own risk, the script is merely for informational purposes. You are responsible for your own server and the files and folders on it, and therefore I am not responsible if this script causes any harm.
##################################################################################
#
#
# Script name: SetHomeFolder.ps1
# Author: Aaron and John
#
#
##################################################################################
param ([string]$User, [switch]$help)
function GetHelp()
{
$HelpText = @"
DESCRIPTION:
NAME: SetHomeFolder.ps1
Creates folder and sets permissions for the specified user.
Creates folder if it does not exist. Removes "Administrators" default permission.
PARAMETERS:
-User User to create folder for and who should have access (Required)
-help Prints the HelpFile (Optional)
SYNTAX:
./SetHomeFolder.ps1 -User Domain\UserName
Creates \\[server]\Users\[UserName] and sets new permissions for that folder.
"@
$HelpText
[system.enum]::getnames([System.Security.AccessControl.FileSystemRights])
}
function CreateFolder ([string]$Path)
{
#check if the folder Exists
Write-Host "$Path..." -Foregroundcolor Green
if (Test-Path $Path) {
Write-Host "...Already Exists" -ForeGroundColor Yellow
} else {
Write-Host "...Created"
New-Item -Path $Path -type directory | Out-Null
}
}
function SetAcl ([string]$Path, [string]$User, [string]$Permission)
{
#this trap prevents ugly errors on the screen when attempting to update the ACL for the user.
trap [Exception]
{
write-host " ERROR: $($_.Exception.Message)" -ForeGroundColor Magenta
continue
}
Write-Host "Setting Permissions..." -ForeGroundColor Green
#get ACL on folder and assign as object
$GetACL = Get-Acl $Path
#set up the access rules
$Allinherit = [system.security.accesscontrol.InheritanceFlags]"ContainerInherit, ObjectInherit"
$Allpropagation = [system.security.accesscontrol.PropagationFlags]"None"
$AccessRule = New-Object system.security.AccessControl.FileSystemAccessRule($User, $Permission, $AllInherit, $Allpropagation, "Allow")
$SystemAccessRule = New-Object system.security.AccessControl.FileSystemAccessRule("SYSTEM", "FullControl", $AllInherit, $Allpropagation, "Allow")
$AdminAccessRule = New-Object system.security.AccessControl.FileSystemAccessRule("Administrators", "ReadAndExecute", $AllInherit, $Allpropagation, "Allow")
#check if Access for Administrators already exists - and REMOVE
Write-Host "...Removing Permission For: Administrators" -ForeGroundColor DarkGray
$GetACL.RemoveAccessRuleAll($AdminAccessRule) | Out-Null
#end of Administrators permissions
#check if Access for User Already Exists - and ADD
if ($GetACL.Access | Where { $_.IdentityReference -eq $User}) {
Write-Host "...Modifying Permissions For: $User" -ForeGroundColor Yellow
$AccessModification = New-Object system.security.AccessControl.AccessControlModification
$AccessModification.value__ = 2
$Modification = $False
#modify the ACL rule
$GetACL.ModifyAccessRule($AccessModification, $AccessRule, [ref]$Modification) | Out-Null
} else {
Write-Host "...Adding Permission: $Permission For: $User"
# add the ACL rule
$GetACL.AddAccessRule($AccessRule)
}
#end of User permissions
#check if Access for SYSTEM already exists - and ADD
if ($GetACL.Access | Where { $_.IdentityReference -eq "SYSTEM"}) {
Write-Host "...Modifying Permissions For: SYSTEM" -ForeGroundColor Yellow
$SystemAccessModification = New-Object system.security.AccessControl.AccessControlModification
$SystemAccessModification.value__ = 2
$SystemModification = $False
# modify the ACL rule
$GetACL.ModifyAccessRule($SystemAccessModification, $SystemAccessRule, [ref]$SystemModification) | Out-Null
} else {
Write-Host "...Adding Permission: FullControl For: SYSTEM"
#add the ACL rule
$GetACL.AddAccessRule($SystemAccessRule)
}
#end of SYSTEM permissions
#set the owner of the folder to the specified user
$GetACL.SetOwner((new-object System.Security.Principal.NTAccount("$User")))
#this command performs the actual update using the objects as defined above
Set-Acl -aclobject $GetACL -Path $Path
}
function Get-ADUser( [string]$samid=$env:username)
{
#find the user object in active directory and returns the user object to the calling line
$searcher=New-Object DirectoryServices.DirectorySearcher
$searcher.Filter="(&(objectcategory=person)(objectclass=user)(sAMAccountname=$samid))"
$aduser=$searcher.FindOne()
if ($aduser -ne $null )
{
$aduser.getdirectoryentry()
}
}
if ($help)
{
#the user requested help, so let us display that help.
GetHelp
}
#set some global variables per our environment.
$Permission = "FullControl"
$username = $User.substring($User.indexof("\") + 1)
$Path = "\\[server]\Users\$username"
#LET'S BEGIN (sub Main()) - Process folder creation and security changes
if ($Path -AND $User -AND $Permission)
{
#1. create the folder
CreateFolder $Path
#2. set the ACL permissions and ownership for that folder
SetAcl $Path $User $Permission
#3. update active directory with the new path location
Write-Host "Updating Active Directory Home Folder..."
$aduser = Get-ADUser $username
$aduser.homeDirectory = "$Path"
$aduser.homeDrive = "P:"
$aduser.SetInfo()
Write-Host "...$username Updated with: $Path" -ForeGroundColor Green
}
Found this from doing a Google search for “centralized my documents” and I see where you’re going with this. We have a Desktop & Laptop Option (DLO) with our current backup solution but come to find out that when I tried to move the backup solution to a different server along with the DLO option, it’s an annual subscription that’s long since lapsed. So now I’m wondering if I want to renew the DLO for a somewhat outdated version of the backup solution, upgrade everything along with the latest and greatest version of the DLO, or just home everyone’s personal files and settings somewhere on a server and move forward.
When I look at things, the DLO option simply keeps a copy of everyone’s “My Documents” folder (along with their “Favorites”) on the server in an encrypted store and said store is backed up with all of our servers each night to media. The RESTORE process using this method has never been pretty and nine times out of ten I’ve been fortunate enough to have the hard drive not completely fried and was able to somehow recover their data and move it to their replacement machine. So the question still remains… is it really worth it?
I think I’m going to try your method on my own machine with my own files and see if I can either get it working or break it. Did you ever post information on doing something like this with their “Desktop” and “Favorites” folder?
Thank you for the contribution of knowledge…
Mark,
I haven’t shared my experiences with the Desktop and Favorites script. Its a little rusty as we never implemented it at my current place of employment. Most of the migration happens by way of a VBScript, with the only issue being the authority to reboot the computer when the script is ran under User credentials. I’ll try to get it up this weekend.
-Aaron
Mark,
Check out my article on migrating your desktop, Centralized Desktop and Favorites with a Centralized My Documents?
-Aaron
Aaron,
I used the script but I am unable to remove the inherited permissions from the domain user group. All I changed was administrator group to domain users group. No matter what I try this group is not removed. Do you have any clue or idea why this is failing?
At the user share (not the individual user folder, but the base folder) remove the inherited permissions. Because the permission is inherited, I doubt this script would remove that permission. The local Administrators group was not inherited in my environment, it was just something that was automatically added by Microsoft.
-Aaron