Link to home
Start Free TrialLog in
Avatar of Member_2_241474
Member_2_241474

asked on

Move Existing Users Documents and Settings from c: to d: on XPSP3 computers in 2003 Domain

I told a client, "Sure, no problem!" when asked if I could do this because I did it all the time when setting up NEW workstations.  oops.

I have an installation of 30 XPSP3 workstations in a domain with a Windows Server 2003 domain controller.  All of the workstations have at least 2 user profiles, sometimes 5 or 6.  I want to move all of the profiles to the local d: drive so that c: can be ghosted with no user info lost.

I have looked at Q20253777,  and KB article #Q236621.  The stumper on the "Move Entire Folder" plan is "You must complete this change for every instance in the registry..." which is a very time consuming task for 30 workstations with multiple users.  I have tried a number of registry search and replace tools, but have had issues with all of them.

Has anyone actually done this successfully with minimal labor at each workstation?
Avatar of Member_2_241474
Member_2_241474

ASKER

Additional:  Group policy is not implemented.  I know I can user Folder redirection for some of the user folders, but I don't know of anyplace I can set the whole "Documents and Settings" folder to a different LOCAL drive.
You need to develop a small utility that recurses through the registry and modifies all occurences of the C:\Documents and Settings. That should be quite easy task with vb.net.
Avatar of Don
Take a look at


How to redirect user shell folders to a specified path by using Profile Maker


http://support.microsoft.com/kb/931087


Or try a tool like regsitry agent

http://theos.in/windows-server/windows-xpserver-2003-search-and-replace-registry-effectively/
Thanks for the suggestions, but there are problems with them.

Developing a vb.net application that modifies the registry may be "quite easy" for some, but unfortunately I don't fall into that category.  I've played with a few vbs scripts I've found on ee and elsewhere, but have not been able to make them work.

KB931087 requires manually changing many hundreds of registry entries.  Registry Agent (now Creativelement Power Tools) requires installation on the target machine, so that won't work either.

This may be of better use then


http://bladesdev.com/regreplace.htm
Tried that one, too -- here's the support email:

Sent: Wednesday, February 16, 2011 11:50 PM
To: 'mreggio@bladesdev.com'
Subject: Registry Replacer Issue
Importance: High


Hi, Michael.  Looks like a nice product.

I would be more than happy to pay for it if I could get it to work.  With 1.2.9.0 (Beta) it seems stuck in simulation mode.  It says it replaced 69 entries, but if I bounce the machine they are all back and it will find and claim to have replaced them again.

With 1.2.8.5 it says, "Unable to Replace String" for each match.

OS is XP SP3

What am I doing wrong?
I'm not sure if it will work, but you might try modifying the reg setting hklm\software\microsoft\windows nt\currentversion\profilelist to point to another drive besides the
%systemdrive%.  This seems to be where the %userprofile% variable is set.

 I would make the change, after exporting the existing setting so you can get it back if it blows up, then log in to the workstation as an existing user and see if the get a blank profile.  If they do, you've found it and should be able to move the existing profiles to the new drive while logged in as the administrator on the local machine.

A couple of other places for system variables are HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment and HKEY_CURRENT_USER\Environment.

Hope this helps.
Thanks, guys.  I really appreciate your input, but I have searched EE and every other place I could find and tried all these solutions before posting the question:

"Has anyone actually done this successfully with minimal labor at each workstation?"

I would consider having used a script/program that would search and replace "C:\Documents" with "D:\Documents"  as "actually done this."   The best script I've seen is from Q24792324 written by RobSampson, but I have not been able to make it work,
There's a more recent version of that script here:

https://www.experts-exchange.com/questions/26774524/Delete-multiple-registry-keys.html

so try that one out.

But, you say you want to move the profile to another drive so it can be ghosted.

Are you talking about "taking an image" of the machine, or "re-imaging" the machine?
Do you mean a temporary move, as in a backup? Then after (I'll assume) re-imaging, restore the user profile?

I have written a completely different tool for use in my own environment, that I use to back up profiles, and then either re-image or place a new PC, then restore the profile.  If that's what you're after, I can share that.

Regards,

Rob.
I usually leave C:\Documents and Settings ALONE, keep them on C, for Default User, LocalService, and NetworkService, as those are somewhat machine installed software specific and part of the Windows OS.

I have done it by doing a search and replace in registry to move a single user or two.

Some old Win9x type programs keep their MRUs in an INI file not in the registry, but it's usually easy enough to point to the new location.  Sometimes some non-conforming programs keep full filepath of certain things in a database or something like that, that would be an issue to solve in that app..

I usually use ROBOCOPY command with the command-line options to copy the ACLs as well as the datetime of not only the files /COPYALL but also the folders /DCOPY:T.  Otherwise it can be annoying and even a loss of key information to the user if all their folders datetimes change to the day you moved it all to the other disk.  http://en.wikipedia.org/wiki/Robocopy
Then I make the change in registry for that user.  Satisfied that everything is copied identically (right click porperties, same number of files folders and bytes to the byte) I can then delete off the drive of origin.  Log back in to prove it works.

I have not automated it however, so succesfully, and fairly minimal, but not near zero labor.
Thanks, Rob.

This is for an outsourcing call center that often requires software provided by or created for clients to be installed on operator work stations.  These operators send email, download documents from the web and do all sorts of stuff on the client's behalf from their workstation.  The that program ends and that operator needs to be placed on a different program that requires a different software set.  

My overall purpose is to be able to image drive C: with a new prototype image that has different software installed, but keep all the documents and settings files for the operator intact.  The prototype images would be configured to look a d: for profiles, which is easy on a new install, and be prepared with sysprep with the -mini installation flag. I plan to have a sysprep.ini file on the d: drive customized for minisetup per MS kb302577 (which says the ini file can be "on a floppy disk" (do we still have those?) so I assume it can be configured for D: but I haven't gotten that far yet.

If you have some magic for this, I would love it.  Meanwhile I'll check out Q_26774524.

Ocanada, thanks for you suggestion but as you can see from the above keeping the profiles on c: does not work for me.
Perhaps you could use one of ForensiT's tools to both first migrate all to a server and then back again to each D:\Users instead of C:\Documents...
http://www.forensit.com/domain-migration.html
Um, I didn't mean to say keep them "all" on C, I meant the Default User and Local and NetworkService
and by using Group Policy Preferences to change the users %Homepath% environment variable.

(You only need 1 Vista/Windows 7 client on the network to use GPP by using RSAT)


http://trycatch.be/blogs/roggenk/archive/2009/05/11/group-policy-preferences-tips-amp-tricks-1-variables-list.aspx
I'm thinking for this, if we can modify the key here:
HKLM\Software\Microsoft\Windows NT\CurrentVersion\ProfileList\<USERSID>\ProfileImagePath

to change it to the D drive reference instead of C drive.  It's binary, so we'll have to change that.  The only thing that worries me is that a lot of registry keys probably won't use %USERPROFILE% instead of the hard-coded C:\Documents and Settings\<Username>

I'll have to play around with it to test it out.

The method I have used to backup, then restore a profile, backed up this registry key, then re-imported it so the profile would work even before the user logged on.  This was a restore to the exact same file location though.

Regards,

Rob.
Hmmm, I don't why I said it was a binary value, it's clearly an expanded string value ;-)

Anyway, Try this code to move one specified user profile (specified as an Input prompt).  It will move the profile to D:\Documents and Settings

In the script, you'll see these lines:
            objShell.Run "cmd /c rename """ & strProfileImagePath & """ """ & strUsername & "_old""", 0, True
            'objShell.Run "cmd /c rd /s /q """ & strProfileImagePath & """", 0, True

Currently, the "old" profile is just renamed to <username>_old.  If all works well, you can have the script delete the old profile instead, by changing the commenting of those lines.

I'm not sure if this will work for all settings, notably those hardcoded inthe registry to C:\Documents and Settings

Alternatively, I wonder if you can use the User State Migration Tool (USMT) to migrate the account settings to a server, then migrate it back, to the different folder, to the same account on the same domain.
http://technet.microsoft.com/en-us/library/cc721893(WS.10).aspx

I'm not sure if it can be configured to work that way....but that would make it work similar to the ForensiT tool that ocanada_techguy pointed out.

Regards,

Rob.
Set objShell = CreateObject("WScript.Shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")

strUsername = InputBox("Enter username of the profile to move on this machine:", "Username")
strBackupLocation = "D:\Documents and Settings\"

strUserADsPath = Get_LDAP_User_Properties("user", "samAccountName", strUsername, "adsPath")
If Left(strUserADsPath, 7) = "LDAP://" Then
	If objFSO.FolderExists(strBackupLocation) = False Then objFSO.CreateFolder(strBackupLocation)
	Set objUser = GetObject(strUserADsPath)
	arrSid = objUser.objectSid
	strSidHex = OctetToHexStr(arrSid)
	strSidDec = HexStrToDecStr(strSidHex)
	strRegKey = "HKLM\Software\Microsoft\Windows NT\CurrentVersion\ProfileList\" & strSidDec
	On Error Resume Next
	' Test if the SID root key is there
	strProfileImagePath = objShell.RegRead(strRegKey & "\ProfileImagePath")
	If Err.Number <> 0 Then
		Err.Clear
		On Error GoTo 0
		MsgBox "Could not find " & strRegKey
	Else
		strProfileImagePath = Replace(LCase(strProfileImagePath), "%systemdrive%", objShell.ExpandEnvironmentStrings("%SYSTEMDRIVE%"))
		On Error GoTo 0
		If Right(strBackupLocation, 1) <> "\" Then
			strBackupLocation = strBackupLocation & "\" & strUsername & "\"
		Else
			strBackupLocation = strBackupLocation & strUsername & "\"
		End If
		strExcludeFile = Left(strHTAPath, InStrRev(strHTAPath, "\")) & "ExcludedFolders.txt"
		Set objExclude = objFSO.CreateTextFile(strExcludeFile, True)
		objExclude.WriteLine "\local settings\temporary internet files\"
		objExclude.WriteLine "\local settings\temp\"
		objExclude.Close
		Set objExclude = Nothing
		strCommand = "cmd /c xcopy " & objFSO.GetFolder(strProfileImagePath).ShortPath & " """ & strBackupLocation & """ /exclude:" & objFSO.GetFile(strExcludeFile).ShortPath & " /i /e /c /h /r /k /x /y"
		objShell.Run strCommand, 1, True
		If objFSO.FileExists(strExcludeFile) = True Then objFSO.DeleteFile strExcludeFile, True

		strNewImagePath = strBackupLocation
		If Right(strNewImagePath, 1) = "\" Then strNewImagePath = Left(strNewImagePath, Len(strNewImagePath) - 1)
		objShell.RegWrite strRegKey & "\ProfileImagePath", strNewImagePath, "REG_EXPAND_SZ"
		
		objShell.Run "cmd /c rename """ & strProfileImagePath & """ """ & strUsername & "_old""", 0, True
		'objShell.Run "cmd /c rd /s /q """ & strProfileImagePath & """", 0, True
		
		MsgBox "Profile for " & strUsername & " has been moved to " & strBackupLocation
	End If
End If

'Working VBScript Active Directory Binary SID conversion to String SID
' Source: http://forums.techarena.in/showthread.php?t=588078
'Function to convert OctetString (byte array) to Hex string.
Function OctetToHexStr(arrbytOctet)
Dim k
OctetToHexStr = ""
For k = 1 To Lenb(arrbytOctet)
OctetToHexStr = OctetToHexStr & Right("0" & Hex(Ascb(Midb(arrbytOctet, k, 1))), 2)
Next
End Function
 
Function HexStrToDecStr(strSid)
' Function to convert Hex string Sid to Decimal string (SDDL) Sid.
 
 
' SID anatomy:
' Byte Position
' 0 : SID Structure Revision Level (SRL)
' 1 : Number of Subauthority/Relative Identifier
' 2-7 : Identifier Authority Value (IAV) [48 bits]
' 8-x : Variable number of Subauthority or Relative Identifier (RID) [32 bits]
'
' Example:
'
' <Domain/Machine>\Administrator
' Pos : 0 | 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 26 27
' Value: 01 | 05 | 00 00 00 00 00 05 | 15 00 00 00 | 06 4E 7D 7F | 11 57 56 7A | 04 11 C5 20 | F4 01 00 00
' str : S- 1 | | -5 | -21 | -2138918406 | -2052478737 | -549785860 | -500
 
 
Const BYTES_IN_32BITS = 4
Const SRL_BYTE = 0
Const IAV_START_BYTE = 2
Const IAV_END_BYTE = 7
Const RID_START_BYTE = 8
Const MSB = 3 'Most significant byte
Const LSB = 0 'Least significant byte
 
 
Dim arrbytSid, lngTemp, base, offset, i
 
 
ReDim arrbytSid(Len(strSid)/2 - 1)
 
 
' Convert hex string into integer Array
For i = 0 To UBound(arrbytSid)
      arrbytSid(i) = CInt("&H" & Mid(strSid, 2 * i + 1, 2))
Next
 
 
' Add SRL number
HexStrToDecStr = "S-" & arrbytSid(SRL_BYTE)
 
 
' Add Identifier Authority Value
lngTemp = 0
For i = IAV_START_BYTE To IAV_END_BYTE
      lngTemp = lngTemp * 256 + arrbytSid(i)
Next
HexStrToDecStr = HexStrToDecStr & "-" & CStr(lngTemp)
 
 
' Add a variable number of 32-bit subauthority or
' relative identifier (RID) values.
' Bytes are in reverse significant order.
' i.e. HEX 01 02 03 04 => HEX 04 03 02 01
' = (((0 * 256 + 04) * 256 + 03) * 256 + 02) * 256 + 01
' = DEC 67305985
For base = RID_START_BYTE To UBound(arrbytSid) Step BYTES_IN_32BITS
      lngTemp = 0
      For offset = MSB to LSB Step -1
            lngTemp = lngTemp * 256 + arrbytSid(base + offset)
      Next
      HexStrToDecStr = HexStrToDecStr & "-" & CStr(lngTemp)
Next
End Function ' HexStrToDecStr
 
Function Get_LDAP_User_Properties(strObjectType, strSearchField, strObjectToGet, strCommaDelimProps)
      
      ' This is a custom function that connects to the Active Directory, and returns the specific
      ' Active Directory attribute value, of a specific Object.
      ' strObjectType: usually "User" or "Computer"
      ' strSearchField: the field by which to seach the AD by. This acts like an SQL Query's WHERE clause.
      '				It filters the results by the value of strObjectToGet
      ' strObjectToGet: the value by which the results are filtered by, according the strSearchField.
      '				For example, if you are searching based on the user account name, strSearchField
      '				would be "samAccountName", and strObjectToGet would be that speicific account name,
      '				such as "jsmith".  This equates to "WHERE 'samAccountName' = 'jsmith'"
      '	strCommaDelimProps: the field from the object to actually return.  For example, if you wanted
      '				the home folder path, as defined by the AD, for a specific user, this would be
      '				"homeDirectory".  If you want to return the ADsPath so that you can bind to that
      '				user and get your own parameters from them, then use "ADsPath" as a return string,
      '				then bind to the user: Set objUser = GetObject("LDAP://" & strReturnADsPath)
      
      ' Now we're checking if the user account passed may have a domain already specified,
      ' in which case we connect to that domain in AD, instead of the default one.
      If InStr(strObjectToGet, "\") > 0 Then
            arrGroupBits = Split(strObjectToGet, "\")
            strDC = arrGroupBits(0)
            strDNSDomain = strDC & "/" & "DC=" & Replace(Mid(strDC, InStr(strDC, ".") + 1), ".", ",DC=")
            strObjectToGet = arrGroupBits(1)
      Else
      ' Otherwise we just connect to the default domain
            Set objRootDSE = GetObject("LDAP://RootDSE")
            strDNSDomain = objRootDSE.Get("defaultNamingContext")
      End If
 
      strBase = "<LDAP://" & strDNSDomain & ">"
      ' Setup ADO objects.
      Set adoCommand = CreateObject("ADODB.Command")
      Set adoConnection = CreateObject("ADODB.Connection")
      adoConnection.Provider = "ADsDSOObject"
      adoConnection.Open "Active Directory Provider"
      adoCommand.ActiveConnection = adoConnection
 
 
      ' Filter on user objects.
      'strFilter = "(&(objectCategory=person)(objectClass=user))"
      strFilter = "(&(objectClass=" & strObjectType & ")(" & strSearchField & "=" & strObjectToGet & "))"
 
      ' Comma delimited list of attribute values to retrieve.
      strAttributes = strCommaDelimProps
      arrProperties = Split(strCommaDelimProps, ",")
 
      ' Construct the LDAP syntax query.
      strQuery = strBase & ";" & strFilter & ";" & strAttributes & ";subtree"
      adoCommand.CommandText = strQuery
      ' Define the maximum records to return
      adoCommand.Properties("Page Size") = 100
      adoCommand.Properties("Timeout") = 30
      adoCommand.Properties("Cache Results") = False
 
      ' Run the query.
      Set adoRecordset = adoCommand.Execute
      ' Enumerate the resulting recordset.
      strReturnVal = ""
      Do Until adoRecordset.EOF
          ' Retrieve values and display.    
          For intCount = LBound(arrProperties) To UBound(arrProperties)
                If strReturnVal = "" Then
                      strReturnVal = adoRecordset.Fields(intCount).Value
                Else
                      strReturnVal = strReturnVal & VbCrLf & adoRecordset.Fields(intCount).Value
                End If
          Next
          ' Move to the next record in the recordset.
          adoRecordset.MoveNext
      Loop
 
      ' Clean up.
      adoRecordset.Close
      adoConnection.Close
      Get_LDAP_User_Properties = strReturnVal
 
End Function

Open in new window

Although, I just stumbled across another method, which uses Junction points:

How to Move your Windows User Profile to another Drive
http://www.starkeith.net/coredump/2009/05/18/how-to-move-your-windows-user-profile-to-another-drive/

On Windows XP, use the Junction tool here:
http://technet.microsoft.com/en-us/sysinternals/bb896768.aspx

and this command:
junction "C:\Documents and Settings\<username>" "D:\Documents and Settings\<UserName>"

Therefore, you could use my code to move the profile, then execute the junction command, and things should work better.

Regards,

Rob.
I dont think the problem here is "Moving the Data", but the fact that the registry needs to be edited to account for the move.
ASKER CERTIFIED SOLUTION
Avatar of RobSampson
RobSampson
Flag of Australia image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Simplest method would be to modify the %Homepath% environment variable with GPP as I mentioned here

http:#a34929536
But if HomePath is set by AD to a network drive that has little to do with the location of the profile itself?
Where did "network drive" come from ?
Well, in my environment, we have the homedrive set to a network drive, so the DOS variable points to that as HomeDrive...
Yes, but that would be a different scenario since the OP idoesnt appear to be redirecting folders or using roaming profiles(Which probably would have eliminated the need to move profiles from C: to D:)

My environment also has the "Homedrive" set in the users profile tab
Thanks, everyone.

Setting %HOMEPATH% does not change the myriad of registry references pointing to "C:\Documents and Settings" when the workstation already has multiple active profiles.  

The junction trick seems like it would work, though.  I'm going to experiment with that and a more recent version of Rob's registry search and replace.  

Copying the complete Documents and Settings folder from C:\ to D:\ is not problem.  I boot the box from [Admin Edit]  then there are no open files in c:\Documents and Settings so the whole folder can be copied to D:\ in one shot.  If I then restore an image to C: that was already setup with an unattended installation config pointed at D:\Documents and Settings, everything seems to work (more testing needed though).  If it was all going to be done in 1 operation, no problem.  But then the client wants this in two phases: 1. Move profiles to D: leaving the existing registry on C:  then 2. Come back and image the C: drives with images still under development.  I thought that would be easy, so I said, "No problem" and now it is hard to talk with this big foot in my mouth.  

If I can't make one of these solutions work today, I'm going to confess that there is a problem with my "No Problem" operations and we'll have to go back to the plan of doing the whole operation in one trip.
Why dont you just suggest to this client redirected folders or roaming profiles ?

If they redirect their "My Documents", "Desktop", and any other folders they feel necessary to back up to a network location...then all their important files will be in a central location that you can easily back up. Then if any PC crashes you replace and they will still have everything when they log back on.

This will be alot quicker and easier to set up.
oops! didn't know about the "sirens" thing.  Booting from a live Linux cd with samba could accomplish the same thing.

Thanks, dstewartir, but raming profiles don't meet the client's requirements.

And thanks, Rob!
Hi, in reference to your "no open files", I always reboot the machine before running the profile copy script I provided, and only log in as an Admin account that is NOT to be moved.  That way, the profiles being moved haven't been opened yet.

Rob.