Migrate home directory to a new location
Recently, I have been involved in some larger XenApp projects where one of the objects have been to migrate home directories when users change environment. People tend to lock themselves to the idea that IT must help migrate home directory when the user is given access to the new environment. A better way to approach this is to publish a script to the start menu and inform users to run the script when first logging in to the new environment. In one of the projects it were a bit more complicated because the home directory structure was different from the structure in the new XenApp environment (see below).
Old structure:
- Links
- Favorites
- Downloads
- Documents
- My Music
- My Videos
- My Pictures
- Desktop
- Contacts
- AppData
New structure:
- Downloads
- Documents
- Pictures
- Music
- Videos
- Desktop
To solve this we had to create a PowerShell script with a bit more logic. Basically what we do is the following;
- Copy everything from old home directory to the new home directory, except the excluded folders and files
- Copy ”\Documents\Music” to ”\Music”
- Copy ”\Documents\Videos” to ”\Videos”
- Copy ”\Documents\Pictures” to ”\Pictures”
- Copy ”\Favorites” to ”\%USERPROFILE%\Favorites” (It’s bad practice to redirect Favorites. Read more here)
- Copy necessary items from ”\AppData\~” to ”\%USERPROFILE%\AppData\~” (We use Citrix Profile Management instead of redirecting AppData to the home share. Redirecting AppData to the home share is also bad practice. Same reason as why it’s bad practice to redirect Favorites.)
<#
.NOTES
Name: CTX-VDA-PROD-WS16-A-Copy-HomeDirectory.ps1
Author: Måns Hurtigh, Xenit AB
Date Created: 2018-10-15
Version History:
2018-10-15
-Initial Creation
#>
[cmdletbinding()]
Param(
[string]$src = '\\server\homecatalogs\' + $env:USERNAME,
[string]$dst = '\\newserver\Home\' + $env:USERNAME,
[array]$exclusionList = @('\Appdata','\Contacts','\Documents\My Music','\Documents\My Pictures','\Documents\My Videos','\Favorites','\Links','\WINDOWS','\*\$RECYCLE.BIN','\*\desktop.ini')
)
Begin {
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null
Function Copy-SpecialFolders {
[cmdletbinding()]
Param(
[string]$src,
[string]$dst,
[string]$OldStruct,
[string]$NewStruct,
[string[]]$Exclude
)
Get-ChildItem -LiteralPath "$src$OldStruct" -Force -Recurse -Exclude $Exclude | ForEach-Object{
$destination = "$dst$($_.FullName.Replace($src,'').Replace($OldStruct,$NewStruct))"
If(Test-Path $_.FullName -Type Leaf){
$destination = ($destination.Split('\')[0..($destination.Split('\').Count-2)]) -join '\'
Copy-Item -LiteralPath $_.FullName -Destination $destination -Force
}
Else{
New-Item -Path $destination -ItemType Container -Force
}
}
}
}
Process {
if (Test-Path $src -PathType Container) {
$MSGBoxTitle = "Copy the entire home directory from the old environment"
$MSGBoxText = "Press Yes to copy the home directory from the old environment or press No to cancel"
$PopupObject = [System.Windows.Forms.MessageBox]::Show($MSGBoxText, $MSGBoxTitle , 4)
if ($PopupObject -eq "YES") {
#Copy Root files
Get-childItem $src -File | ForEach-Object{Copy-Item $_.FullName $dst -Force}
#Copy rest of folders with exclusionlist
$ToCopy = Get-ChildItem $src -Force -Recurse | ForEach-Object{
$thisFolder = $_.FullName
If(-not($exclusionList | Where-Object{$thisFolder -like "$($src)$($_)*"})){
$thisFolder
}
}
$ToCopy | ForEach-Object{
$destination = "$dst$($_.Replace($src,''))"
If(Test-Path $_ -Type Leaf){
$destination = ($destination.Split('\')[0..($destination.Split('\').Count-2)]) -join '\'
Copy-Item -LiteralPath $_ -Destination $destination -Force
}
Else{
New-Item -Path $destination -ItemType Container -Force
}
}
Copy-SpecialFolders -src $src -dst $dst -OldStruct '\Documents\My Music' -NewStruct '\Music' -Exclude '*$RECYCLE.BIN','*desktop.ini'
Copy-SpecialFolders -src $src -dst $dst -OldStruct '\Documents\My Videos' -NewStruct '\Videos' -Exclude '*$RECYCLE.BIN','*desktop.ini'
Copy-SpecialFolders -src $src -dst $dst -OldStruct '\Documents\My Pictures' -NewStruct '\Pictures' -Exclude '*$RECYCLE.BIN','*desktop.ini'
Copy-Item -LiteralPath "$src\Favorites" -Destination $env:USERPROFILE -Recurse -Force
New-Item -ItemType directory -Path "$env:APPDATA\Microsoft\Signatures"
Get-ChildItem -LiteralPath "$src\AppData\Roaming\Microsoft\Signaturer" -Force -Recurse | ForEach-Object{
$destination = "$($_.FullName.Replace($src,$env:USERPROFILE).Replace('Signaturer','Signatures'))"
If(Test-Path $_.FullName -Type Leaf){
$destination = ($destination.Split('\')[0..($destination.Split('\').Count-2)]) -join '\'
Copy-Item -LiteralPath $_.FullName -Destination $destination -Force
}
Else{
New-Item -Path $destination -ItemType Container -Force
}
}
$MSGBoxTitle = "Finished"
$MSGBoxText = "Your old home directory has now been copied."
$PopupObject = [System.Windows.Forms.MessageBox]::Show($MSGBoxText, $MSGBoxTitle , 0)
}
}
else {
$MSGBoxTitle = "Could not find your old home directory"
$MSGBoxText = "Your old home directory could not be found. Please contact the IT department."
$PopupObject = [System.Windows.Forms.MessageBox]::Show($MSGBoxText, $MSGBoxTitle , 0)
}
}
End {
}
The end result looks like this:
User runs ”Copy Home Directory” from Start Menu as instructed
A box pops up
Another box pops up when the script is finished copying the old home directory