Lockdown a Centralized My Documents

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
}

About aaron

IT is not just a job but also a passion. Everything I have accomplished, both personally and professionally, has been generally entertaining, bordering on fun. Some of my projects, such as working with SharePoint Services workflow actions in Visual Studio or building a custom iSCSI SAN using the OpenSolaris, ZFS and COMSTAR, has been quite rewarding. You may think nerd...I think developing a new trend!