Feb 212018
I plagiarized David Ott’s script for migration of Citrix Profile Manager (UPM) profiles to FSLogix and created it for Local Profiles.
NOTE: This only works between like profile versions. eg. You can’t migrate your 2008R2 profiles to Server 2016 and expect it to work. See this chart.
This requires using frx.exe, which means that FSLogix needs to be installed on the server that contains the profiles. The script will create the folders in the USERNAME_SID format, and set all proper permissions.
Use this script. Edit it. Run it (as administrator) from the Citrix server. It will pop up this screen to select what profiles to migrate.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
#### EDIT ME $newprofilepath = "\\domain.com\share\path" #### Don't edit me $ENV:PATH=”$ENV:PATH;C:\Program Files\fslogix\apps\” $oldprofiles = gci c:\users | ?{$_.psiscontainer -eq $true} | select -Expand fullname | sort | out-gridview -OutputMode Multiple -title "Select profile(s) to convert" # foreach old profile foreach ($old in $oldprofiles) { $sam = ($old | split-path -leaf) $sid = (New-Object System.Security.Principal.NTAccount($sam)).translate([System.Security.Principal.SecurityIdentifier]).Value # set the nfolder path to \\newprofilepath\username_sid $nfolder = join-path $newprofilepath ($sam+"_"+$sid) # if $nfolder doesn't exist - create it with permissions if (!(test-path $nfolder)) {New-Item -Path $nfolder -ItemType directory | Out-Null} & icacls $nfolder /setowner "$env:userdomain\$sam" /T /C & icacls $nfolder /grant $env:userdomain\$sam`:`(OI`)`(CI`)F /T # sets vhd to \\nfolderpath\profile_username.vhdx (you can make vhd or vhdx here) $vhd = Join-Path $nfolder ("Profile_"+$sam+".vhdx") frx.exe copy-profile -filename $vhd -sid $sid } |
But then how do you attached this VHD to the user.
[…] FSLogix Local Profiles Migration Script […]
#########################################################################################
# Setup Parameter first here newprofile oldprofile subfolder1 subfolder2
# Requires -RunAsAdministrator
#########################################################################################
# Example from my UPM Path "\\path_to_your_share\username\2012R2\UPM_Profile"
# fslogix Root profile path
$newprofilepath = "\\path_to_your_share\FSLogix"
# UPM Root profile path
$oldprofilepath = "\\path_to_your_share\UPM\Userprofiles"
# Subfolder 1 - First Path to UPM_Profile Folder in UPM Profiles - see my example above
$subfolder1 = "2012R2"
# Subfolder 2 - First Path to UPM_Profile Folder in UPM Profiles - see my example above
$subfolder2 = "UPM_Profile"
#########################################################################################
# Do not edit here
#########################################################################################
$oldprofiles = gci $oldprofilepath | select -Expand fullname | sort | out-gridview -OutputMode Multiple -title "Select profile(s) to convert"| %{
Join-Path $_ $subfolder1\$subfolder2
}
foreach ($old in $oldprofiles) {
$sam = Split-Path ($old -split $subfolder1)[0] -leaf
$sid = (New-Object System.Security.Principal.NTAccount($sam)).translate([System.Security.Principal.SecurityIdentifier]).Value
$regtext = "Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$sid]
"ProfileImagePath
"="C:\\Users\\$sam
""FSL_OriginalProfileImagePath
"="C:\\Users\\$sam
""Flags
"=dword:00000000"State
"=dword:00000000"ProfileLoadTimeLow
"=dword:00000000"ProfileLoadTimeHigh
"=dword:00000000"RefCount
"=dword:00000000"RunLogonScriptSync
"=dword:00000000"
$nfolder = join-path $newprofilepath ($sam+"\"+$sid+"_"+$sam)
if (!(test-path $nfolder)) {New-Item -Path $nfolder -ItemType directory | Out-Null}
& icacls $nfolder /setowner "$env:userdomain\$sam" /T /C
& icacls $nfolder /grant $env:userdomain\$sam
:
(OI)
(CI)F /T
$vhd = Join-Path $nfolder ("Profile_"+$sam+".vhdx")
$script1 = "create vdisk file="$vhd
" maximum 30720 type=expandable"
"$vhd$script2 = "sel vdisk file=
"
rnattach vdisk"
"$vhd$script3 = "sel vdisk file=
"
rncreate part prim
rnselect part 1
rnformat fs=ntfs quick"
"$vhd$script4 = "sel vdisk file=
"
rnsel part 1
rnassign letter=T"
"$vhd$script5 = "sel vdisk file
"
rndetach vdisk"
"$vhd$script6 = "sel vdisk file=
"
rnattach vdisk readonly
"r
ncompact vdisk"if (!(test-path $vhd)) {
$script1 | diskpart
$script2 | diskpart
Start-Sleep -s 5
$script3 | diskpart
$script4 | diskpart
& label T: Profile-$sam
New-Item -Path T:\Profile -ItemType directory | Out-Null
start-process icacls "T:\Profile /setowner SYSTEM"
Start-Process icacls -ArgumentList "T:\Profile /inheritance:r"
$cmd1 = "T:\Profile /grant $env:userdomain\$sam
:
(OI)
(CI)F"
:Start-Process icacls -ArgumentList "T:\Profile /grant SYSTEM
(OI
)(CI
)F"Start-Process icacls -ArgumentList "T:\Profile /grant Administrators
:
(OI)
(CI`)F"Start-Process icacls -ArgumentList $cmd1
} else {
$script2 | diskpart
Start-Sleep -s 5
$script4 | diskpart
}
"Copying $old to $vhd"
& robocopy $old T:\Profile /E /Purge /r:0 | Out-Null
if (!(Test-Path "T:\Profile\AppData\Local\FSLogix")) {
New-Item -Path "T:\Profile\AppData\Local\FSLogix" -ItemType directory | Out-Null
}
if (!(Test-Path "T:\Profile\AppData\Local\FSLogix\ProfileData.reg")) {$regtext | Out-File "T:\Profile\AppData\Local\FSLogix\ProfileData.reg" -Encoding ascii}
$script5 | diskpart
}
to be sure, our Users where saved only with the SAMAccount Name without Domain
What’s your question?
PS C:\Windows\system32> C:\Users\adm-bart.FSL0\Desktop\fslogix migration.ps1
processed file: \\zvb-file\profilecontainer$\adm-johan
Successfully processed 1 files; Failed processing 0 files
processed file: \\zvb-file\profilecontainer$\adm-johan
Successfully processed 1 files; Failed processing 0 files
.
Formatting volume: \\?\Volume{5b18bc11-649f-4531-806f-b61c2b926584}\
Copying profile for user S-1-5-21-1722534631-683760180-1736066318-1402 to volume \\?\Volume{5b18bc11-649f-4531-80
6f-b61c2b926584}\
Exit code: 9
Error copying profile (0x0000001D): The system cannot write to the specified device.
same problem
[…] Local Profile to Profile Container […]
[…] Local Profile nach Profile Container […]
Same issue here. Getting Error copying profile (0x0000001D)
I’m also getting the 0x0000001D error. I also tried to copy the profile to the local disk (Windows 10 1903 on VMware) but this failed with the same error.
The 0x0000001D error can be ignored.
Within the user’s “AppData\Local\Microsoft\WindowsApps” folder there is a file called MicrosoftEdge.exe and also a subfolder “Microsoft.MicrosoftEdge_…” that also contains a file called MicrosoftEdge.exe, both with the size of 0 bytes.
The frx tool uses robocopy to transfer the profile into the newly created container but fails to read/write one of those exe files mentioned above. This causes the 0x0000001D error.
But aside from that robocopy copies the whole profile. Therefore it should be usable.
If the folder “AppData\Local\Microsoft\WindowsApps” is deleted before using frx to migrate the profile then frx succeeds and the VHDX file is properly created and its security settings can be modified accordingly afterwards.
I logged on with that user’s profile to assure that deleting the “WindowsApps” folder hasn’t any negative effect on the profile. Microsoft Edge starts without any errors, so deleting that folder doesn’t seem to have any negative impactso far.
Maybe this does help you folks to get the migration of the profiles up and running.
How would one execute this command remotely to migrate 100’s of users profiles from their persistent VDI to an FSLogix Share?
I am a Newbie when it comes to Powershell.
Any way we could use this to just migrate a named local user profile to VHD?
Sorry didn’t look at the script very well and see the Out-GridView command – have changed it to set the variable to $ENV:USERNAME and it works for me.
How can we do this from login as the logged in user as a batch?
Was thinking of using
frx create-vhd -filename=
and frx.exe copy-profile -filename $vhd -sid $sid
I’m dealing with Windows roaming profiles, and moving away from Citrix to Windows Virtual Desktop in Azure. The script will mount the Azure Files share and convert all profiles within a given path then place the FS Logix profile in the given Azure Files share.
Modified the script to place the converted profiles in an Azure Files share.
There are some optional areas for the drive mount, etc – edit as needed.
Changed the split on line 43 since I’m dealing with username.v4, etc.
Removed the option to select which profiles to convert
Removed comments regarding the option to choose profiles to convert
Modify lines:
2, 6, 9, 21, 22 and 26 (as needed)
#Requires -RunAsAdministrator
$connectTestResult = Test-NetConnection -ComputerName StorageAccountName.file.core.windows.net -Port 445
if ($connectTestResult.TcpTestSucceeded) {
# Save the password so the drive will persist on reboot
# Modify STORAGEACCOUNTNAME and Storage-Account-Access-Key
cmd.exe /C “cmdkey /add:
"STORAGEACCOUNTNAME.file.core.windows.net
” /user:"Azure\STORAGEACCOUNTNAME
” /pass:"Storage-Account-Access-Key
“”# Mount the drive
# Modify the StorageAccountName & Share-Name
New-PSDrive -Name Z -PSProvider FileSystem -Root “\\StorageAccountName.file.core.windows.net\Share-Name” -Persist
} else {
Write-Error -Message “Unable to reach the Azure storage account via port 445. Check to make sure your organization or ISP is not blocking port 445, or use Azure P2S VPN, Azure S2S VPN, or Express Route to tunnel SMB traffic over a different port.”
}
# fslogix profile path
$newprofilepath = “Z:\Azure-Files-Shared-Folder-Name” ##### FSLogix Root Profile Path
$oldprofiles = get-childitem -path c:\temp\test | select name
# foreach old profile
foreach($old in $oldprofiles)
{
$sam = ($old.Name.Split(“.”))[0]
$sid = (New-Object System.Security.Principal.NTAccount($sam)).translate([System.Security.Principal.SecurityIdentifier]).Value
$regtext = “Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\$sid]
"ProfileImagePath
“="C:\\Users\\$sam
”"FSL_OriginalProfileImagePath
“="C:\\Users\\$sam
”"Flags
“=dword:00000000"State
“=dword:00000000"ProfileLoadTimeLow
“=dword:00000000"ProfileLoadTimeHigh
“=dword:00000000"RefCount
“=dword:00000000"RunLogonScriptSync
“=dword:00000000”
$nfolder = join-path $newprofilepath ($sid+”_”+$sam) ##### See note above
# if $nfolder doesn’t exist – create it
if (!(test-path $nfolder)) {New-Item -Path $nfolder -ItemType directory | Out-Null}
& icacls $nfolder /setowner “$env:userdomain\$sam” /T /C
& icacls $nfolder /grant $env:userdomain\$sam
:
(OI)
(CI)F /T
“$vhd# sets vhd to \\nfolderpath\profile_username.vhd
$vhd = Join-Path $nfolder ("Profile_"+$sam+".vhd")
# diskpart commands
$script1 = "create vdisk file=
" maximum 30720 type=expandable"
“$vhd$script2 = "sel vdisk file=
"
rnattach vdisk"
“$vhd$script3 = "sel vdisk file=
"
rncreate part prim
rnselect part 1
rnformat fs=ntfs quick"
“$vhd$script4 = "sel vdisk file=
"
rnsel part 1
rnassign letter=T"
“$vhd$script5 = "sel vdisk file
"
rndetach vdisk"
“$vhd$script6 = "sel vdisk file=
"
rnattach vdisk readonly
“r
ncompact vdisk”if (!(test-path $vhd)) {
$script1 | diskpart
$script2 | diskpart
Start-Sleep -s 5
$script3 | diskpart
$script4 | diskpart
& label T: Profile-$sam
New-Item -Path T:\Profile -ItemType directory | Out-Null
# set permissions on the profile
start-process icacls “T:\Profile /setowner SYSTEM”
Start-Process icacls -ArgumentList “T:\Profile /inheritance:r”
$cmd1 = “T:\Profile /grant $env:userdomain\$sam
:
(OI)
(CI)F"
:Start-Process icacls -ArgumentList "T:\Profile /grant SYSTEM
(OI
)(CI
)F”Start-Process icacls -ArgumentList “T:\Profile /grant Administrators
:
(OI)
(CI`)F”Start-Process icacls -ArgumentList $cmd1
} else {
# if the vhd does exist then attach, wait 5 seconds, assign letter T
$script2 | diskpart
Start-Sleep -s 5
$script4 | diskpart
}
# copies in the UPM profile to the Profile directory on the vhd /E /Purge – this is so it will update with the latest info
“Copying $old to $vhd”
& robocopy $old T:\Profile /E /Purge /r:0 | Out-Null
# creates the %localappdata%\FSLogix path if it doesnt exist
if (!(Test-Path “T:\Profile\AppData\Local\FSLogix”)) {
New-Item -Path “T:\Profile\AppData\Local\FSLogix” -ItemType directory | Out-Null
}
# creates the profiledata.reg file if it doesn’t exist
if (!(Test-Path “T:\Profile\AppData\Local\FSLogix\ProfileData.reg”)) {$regtext | Out-File “T:\Profile\AppData\Local\FSLogix\ProfileData.reg” -Encoding ascii}
$script5 | diskpart
}
How would one do this from VMware Horizon persistent disks to FSLogix Profiles?
I’m not familiar with Horizon persistent disks, sadly. Jump into the IRC channel at join.citrixirc.com and perhaps one of the other guys knows the answer.
Hey,
really nice scripts. But I need help for the first one, who migrate profiles from Citrix profile manager to vhdx.
I want to migrade 2012r2 profiles to vhdx, but I get everytime that error:
Error copying profile (0x00000002): The system cannot find the file specified.
The newest version of FSlogix is intalled. I run the script on a 2012r2 Citrix server.
Do you have any idea of that?
Thanks
First one? Do you mean this article? http://www.citrixirc.com/?p=848
Thanks for the very fast response.
I talk from that script with the popup:
#### EDIT ME
$newprofilepath = “\\domain.com\share\path”
#### Don’t edit me
$ENV:PATH=”$ENV:PATH;C:\Program Files\fslogix\apps\”
$oldprofiles = gci c:\users | ?{$_.psiscontainer -eq $true} | select -Expand fullname | sort | out-gridview -OutputMode Multiple -title “Select profile(s) to convert”
# foreach old profile
foreach ($old in $oldprofiles) {
$sam = ($old | split-path -leaf)
$sid = (New-Object System.Security.Principal.NTAccount($sam)).translate([System.Security.Principal.SecurityIdentifier]).Value
# set the nfolder path to \\newprofilepath\username_sid
$nfolder = join-path $newprofilepath ($sam+”_”+$sid)
# if $nfolder doesn’t exist – create it with permissions
if (!(test-path $nfolder)) {New-Item -Path $nfolder -ItemType directory | Out-Null}
& icacls $nfolder /setowner “$env:userdomain\$sam” /T /C
& icacls $nfolder /grant $env:userdomain\$sam
:
(OI)
(CI`)F /T# sets vhd to \\nfolderpath\profile_username.vhdx (you can make vhd or vhdx here)
$vhd = Join-Path $nfolder (“Profile_”+$sam+”.vhdx”)
frx.exe copy-profile -filename $vhd -sid $sid
}
It starts to create the vhdx file and at the end the error message comes and the profile disc is gone.
I checked your link and that script.
It works.
Thanks for the nice content 🙂
Glad to hear it. 🙂
Hello,
Script is working well.
We encountered a Problem with Send to One Note Printer. It is not working anymore after Profile Migration.
Any suggestions are welcome.
THX
Hi All,
I am running FSLogix already and profiles are in VHD. Now we are moving from one domain to another. So profile will have to be migrated from domainA.com to domainB.com. Can we do it via any script?
That’s a tough one. It could be, but you’d have to map the users from one domain to another and figure out how to reset the permissions and owner on the files/directories for each user.
[…] There is already a good article posted on this website for those who want to use the original script from David Ott:FSLogix Local Profiles Migration Script […]