Copying User Profile Data from Physical to Virtual Machine

Can you help me review & improve the below code?
I don’t have much scripting experience, and am very much still learning.

My end goal is to transfer User Profile data files from one PC to a virtual desktop, excluding some directories/files, logging errors, to account for stale user data in the profile in the from the virtual desktop being created for them from the physical PC.

Ideally it would identify & modify the Virtual Desktop user profile only if files after a certain date on the Physical PC are not more recently modified on the Virtual Desktop. (but at this point I haven’t worked on that at all, but was looking at compare-object.)

the below results in permssion errors

Function LogWrite
   Param ([string]$logstring)

   Add-content $Logfile -value $logstring

#import csv of users and computers to be moved
$SourceFile = import-csv C:\Scripts\VDI\source.csv

#Operate line by line through CSV SourceFile
ForEach ($Line in $SourceFile) {
    #Assign Variables for each User
    $UserID = $Line.UserID
    $PhysicalPCName = $Line.OldPC
    $VDPCName = $Line.NewPC
    $IncludePath = ("\\$PhysicalPCName" + "\c$\Users\" + $UserID + "\*")
    $ExcludePath = ("\\$PhysicalPCName" + "\c$\Users\" + $UserID + "\AppData\ntuser.*", "\\$PhysicalPCName" + "\c$\Users\" + $UserID + "\AppData\Local\*", "\\$PhysicalPCName" + "\c$\Users\" + $UserID + "\AppData\LocalLow\*")
    #Create Logfile
    $Logfile= "\\hqemcnas2\vdi_staging$\Log\$userid.txt"
    LogWrite -Message "Starting ProfileCopy for $UserID"
    LogWrite -Message "Source is $PhysicalPCName, Destination is $VDPCName"
    LogWrite -Message "Logfile for $UserID is $Logfile"
    #Define User Profile Path locations per User
    $OldProfilePath = "\\$PhysicalPCName" + "\c$\Users\" + $UserID + "\*"
    $NewProfilePath = "\\$VDPCName" + "\c$\Users\" + $UserID + "\*"

    LogWrite -Message "Old Profile Path is $OldProfilePath"
    LogWrite -Message "VW7 Profile Destination is $NewProfilePath"
    LogWrite -Message "Old Profile Path is $IncludePath"
    LogWrite -Message "Excluded Paths are $ExcludePath"

    #Collect Folder Hierarchy from OldPC
    $oldProfileData = Get-ChildItem -path $OldProfilePath -Recurse -Force -exclude $ExcludePath
    $NewProfileData = Get-ChildItem -path $NewProfilePath -Recurse -Force

    #Copy Folder Structure
    ForEach ($Object in $oldProfileData) {
        #reset copy count
        Copy-Item $oldProfileData -Destination $NewProfilePath -Recurse
        #Test copy existence and Log
        if (test-Path -Path "$NewProfilePath\$($" -eq true)
        {LogWrite -Message "The File $NewProfilePath\$($ was copied to $NewProfilePath"}
        {LogWrite -Message "The File $NewProfilePath\$($ was not copied to $NewProfilePath"}
        LogWrite -Message "Files Copied is $i"
        if ($i -eq 0) {
            LogWrite -Message "All Files failed to copy"
            } else {
            LogWrite -Message "$i files were copied for $userid"


How many people are you doing this for? It appears you are trying to move user profile data to persistent VDI’s for users? Depending on the VDI implementation (Citrix, VMWare, etc.), most solutions have a profile management solution of some sort. There are also solutions like RES Workspace Manager and other 3rd party tools to help “virtualize” profiles. Even outside of these solutions there are free tools like Microsoft User State Migration Tool (USMT) that I’ve used for physical 2 physical profile migrations, but would work for a persistent VDI.

As far as your script, there are a couple of gotchas. First, there is not guarantee that a user profile is going to be named the username. If the user had profile corruption it could be 001 or if there was a local profile with the same name it could be user.domain or if the user was married the username could be jsmith but their profile jjohnson…just to name a few. You’d be better using Win32_UserProfile to enumerate the profiles on the box used within X days and copy those.

Next you’re loop is incorrect:

ForEach ($Object in $oldProfileData) {
Copy-Item $oldProfileData -Destination $NewProfilePath -Recurse

The Copy-Item should reference $Object

Thanks for the reply Rob, and the catch on the Copy-Item variable.

I’ve got about 80-100 users to do this for. We’re using the MS HyperV for the VDI, and have created a vm based on their original PC, using disk2VHD. Some users haven’t used the VM for weeks, and some changes to their profile might be expected since they have been active on their PC.

I was interested in doing this purely in PS as a learning opportunity, but can see calling USMT or other applicable tools. (robocopy, etc.)

Appreciate the calling out of Gotchas, and will look into IDing the profile via Win32_UserProfile.

What do you think of implementing Compare-Object? I think I could use it as a filter of sorts to limit the number of files and directories needing to be copied and pipe the results to copy-childitem?