#91533 - 2003-02-18 08:44 PM
Re: Migrating Local User Profiles
|
Howard Bullock
KiX Supporter
   
Registered: 2000-09-15
Posts: 5809
Loc: Harrisburg, PA USA
|
Jim, don't forget that once you copy the profile key non-Admin users will have to be granted permissions inside the ntuser.dat hive and on the NTFS directory structure where the profile resides. This is not an issue when the user has administrative permissions. [ 18. February 2003, 20:48: Message edited by: Howard Bullock ]
|
|
Top
|
|
|
|
#91535 - 2003-02-18 09:10 PM
Re: Migrating Local User Profiles
|
Howard Bullock
KiX Supporter
   
Registered: 2000-09-15
Posts: 5809
Loc: Harrisburg, PA USA
|
When changing user accounts from one domain to another and wanting to have the some access and experience on an existing client computers and Servers (for home share access and other data), a comprehensive security migration needs to be part of the process.
In fact the security migration is the main process and the profile preservation is a part of the overall process.
|
|
Top
|
|
|
|
#91537 - 2003-02-18 09:41 PM
Re: Migrating Local User Profiles
|
Howard Bullock
KiX Supporter
   
Registered: 2000-09-15
Posts: 5809
Loc: Harrisburg, PA USA
|
Yikes. SID history will kill and confuse you later on.
Sending you email.
{edit} If you had an email address. ![[Eek!]](images/icons/shocked.gif) [ 18. February 2003, 21:42: Message edited by: Howard Bullock ]
|
|
Top
|
|
|
|
#91540 - 2003-02-18 10:32 PM
Re: Migrating Local User Profiles
|
Howard Bullock
KiX Supporter
   
Registered: 2000-09-15
Posts: 5809
Loc: Harrisburg, PA USA
|
Due to popular demand...
The article deals with my first venture into the domain migration business. My current process is more robust and has a proven track record over hundreds of servers and thousands of accounts and client computers. Even thought some changes have occurred since the writing of the article (includes code), the basic fundamentals are the same.
The new process which I can discuss in a limited manner can process a complete security migration while a client or server is not connected to the network. DumpAcl is no longer used.
Article: Cost Effective Domain Migration. [ 18. February 2003, 22:37: Message edited by: Howard Bullock ]
|
|
Top
|
|
|
|
#91541 - 2003-02-19 12:24 AM
Re: Migrating Local User Profiles
|
rclarke
Starting to like KiXtart
   
Registered: 2001-06-08
Posts: 178
Loc: Oxfordshire, United Kingdom.
|
Wow Guys, thank you for all the help. I will read all of this good stuff in the morning when I will be awake and it will hopefully make more sense
I just find it amazing that M$ have made it is so hard to automate this somewhat trivial (and possibly common) task. I was aware of the reg hack and the subsequent permissions tweak, however I was hoping that a more elegant solution could be found.
Jim, if you do come up with a KiXforms based User Profile Copier, I would very much like to add it to the KiXforms web site. Chris, unfortunately integrating the old NT domains is out of the question, we are already running natively. Howard, thank you for sharing your hard earned findings, it is appreciated.
Kind regards,
Rod.
|
|
Top
|
|
|
|
#91543 - 2003-02-19 03:31 PM
Re: Migrating Local User Profiles
|
Howard Bullock
KiX Supporter
   
Registered: 2000-09-15
Posts: 5809
Loc: Harrisburg, PA USA
|
Below is some of the newer Perl code that has been included in the current approach of migrating profiles and security. I do not have time to write it in KiXtart so I will provide the Perl source to those of you that might want to understand the process. You may be able to translate it to KiXtart. I will try to answer any questions.
Here is how I modify the registry to preserve the profile. I load ALL the profiles that are listed in my migration scope as defined by my SID map of old SID to new SID.
code:
sub LoadProfileHives { # Open HKEY_USERS key my $HKEY_Users = $Registry->Open("Users/"); my @oldOpts= $HKEY_Users->SetOptions( AllowLoad=>1 );
# Open ProfileList key my $ProfileList = $Registry->Open("LMachine/Software/Microsoft/Windows NT/CurrentVersion/ProfileList/"); my @ProfileKeys = $ProfileList->SubKeyNames;
my %LoadedKey; foreach my $Pkey (@ProfileKeys) { #&LogText($LogFile, "Reg: $Pkey"); if (exists $SidMap{$Pkey}) { &LogText($LogFile, "Profile Match: $Pkey"); # Load user registry hives into HKEY_USERS if they are not currently loaded. if (! exists $HKEY_Users->{$Pkey . "/"}) { my $file = $ProfileList->{"$Pkey//ProfileImagePath"} . "\\NTUSER.DAT";
# Correct path for both NT and W2K $file =~s/\%(SystemRoot|SystemDrive)\%/$ENV{$1}/i;
$LoadedKey{$Pkey} = $HKEY_Users->Load($file, $Pkey); if (exists $LoadedKey{$Pkey}) { &LogText($LogFile, "Loaded: $file, $Pkey"); } else { &LogText($LogFile, "Failed to load: $file, $Pkey"); } } else { &LogText($LogFile, "Reg: $Pkey already loaded in HKEY_USERS"); } # Copy old OldSid key to NewSid key @oldOpts= $ProfileList->SetOptions( ArrayValues=>1 ); $ProfileList->{"$SidMap{$Pkey}/"} = $ProfileList->{"$Pkey/"}; &LogText($LogFile, "Copy $Pkey key to $SidMap{$Pkey} key"); # Set NewBinarySid to Sid value in new key $ProfileList->{"$SidMap{$Pkey}//Sid"} = [Win32::Lanman::StringToSid($SidMap{$Pkey}), "REG_BINARY" ]; @oldOpts= $ProfileList->SetOptions( ArrayValues=>0 ); } } # Call SubInACL &CallSubInACL;
The current version of SubInACL is a must. The resource kit does not contain the current version.
Subinacl command line: code:
sub CallSubInACL { my $cmdline = "$PerlServer\\subinacl\\subinacl.exe " . "/offlinesam=$ENV{'TEMP'}\\OfflineSAM.txt " . "/errorlog=$ENV{'TEMP'}\\subinaclerror.log " . "/playfile $ENV{'TEMP'}\\playfile.txt"; &LogText($LogFile,"EXEC: $cmdline"); system $cmdline; }
The playfile is a list of commands that subinacl will execute.
code:
sub BuildSubinaclPlayfile { unless (open(PLAYFILE, ">$ENV{'TEMP'}\\playfile.txt" )) { &LogText($LogFile, "Error opening $ENV{'TEMP'}\\playfile.txt"); exit 1; }
my $options = "\n/noverbose\n"; my $migratelines; my @ObjArray = ( "+subkeyreg HKEY_USERS\*", "+share *", "+printer *" );
my @drives = Win32API::File::getLogicalDrives(); my %Types = ( 0 => 'DRIVE_UNKNOWN', 1 => 'DRIVE_NO_ROOT_DIR', 2 => 'DRIVE_REMOVABLE', 3 => 'DRIVE_FIXED', 4 => 'DRIVE_REMOTE', 5 => 'DRIVE_CDROM', 6 => 'DRIVE_RAMDISK', ); foreach my $drive ( @drives ) { my $DriveType = Win32API::File::GetDriveType($drive); next if($Types{$DriveType} ne 'DRIVE_FIXED'); push(@ObjArray, "+subdirectories $drive\*\.\*"); push(@ObjArray, "+onlyfile $drive"); }
# Build array of lines for /migratetodomain option. # Copy Mapping file to %temp% so that subinacl can access it locally. if (-e "$DataPath\\$MapCFG") { unless (open(MAPCFG, "<$DataPath\\$MapCFG")) { &LogText($LogFile, "Fatal Error: Failed to open $DataPath\\$MapCFG: " . $^E); exit 1; } while (<MAPCFG>) { chomp; my ($MapFile, $OldDomain, $NewDomain) = split /\t/; if (Win32API::File::CopyFile("$DataPath\\$MapFile", "$ENV{'TEMP'}\\$MapFile", 0)) { &LogText($LogFile, "$DataPath\\$MapFile copied to $ENV{'TEMP'}"); } else { &LogText($LogFile, "Error copying $DataPath\\$MapFile " . $^E); } $migratelines .= "/migratetodomain=$OldDomain=$NewDomain=$ENV{'TEMP'}\\$MapFile\n"; } } else { &LogText($LogFile, "Fatal Error: Require file ( $DataPath\\$MapCFG ) could not be found."); exit 1; }
foreach my $obj (@ObjArray) { print PLAYFILE $obj . $options . $migratelines . "\n"; } close PLAYFILE; }
This code replaces the use of DumpACL and directly manipulate the local group memberships. code:
sub ProcessLocalGroups { #--------------------------------------------------------------------------------------------------------- # Perform security migration on local groups utilizing the SidMap hash. # Enumerate local groups. # Get members of each group. # Check to see if the Sid for the member is in the SidMap hash. # If it is, add the NewSid as a member to the group. #--------------------------------------------------------------------------------------------------------- my @Groups; my $Error; if (!Win32::Lanman::NetLocalGroupEnum("", \@Groups)) { $^E = $Error = Win32::Lanman::GetLastError(); &LogText($LogFile, "Error (Lanman::NetLocalGroupEnum(\"\"): $Error :" . $^E); &LogText($LogFile, "Exiting program: ABEND."); exit 1; } foreach my $Group (@Groups) { &LogText($LogFile, "Processing localgroup: ${$Group}{'name'}"); my @members; if(!Win32::Lanman::NetLocalGroupGetMembers("", ${$Group}{'name'}, \@members)) { $^E = $Error = Win32::Lanman::GetLastError(); &LogText($LogFile, "Error (Lanman::NetLocalGroupGetMembers(\"\", ${$Group}{'name'}): $Error :" . $^E ); &LogText($LogFile, "Exiting program: ABEND."); exit 1; } foreach my $member (@members) { my $OldTextSid = Win32::Lanman::SidToString(${$member}{'sid'}); if (exists $SidMap{$OldTextSid}) { my $NewBinarySid = Win32::Lanman::StringToSid($SidMap{$OldTextSid}); if(!Win32::Lanman::NetLocalGroupAddMember("", ${$Group}{'name'}, $NewBinarySid)) { $^E = $Error = Win32::Lanman::GetLastError(); &LogText($LogFile, "Error adding:\t$SidMap{$OldTextSid}\tOldMember: ${$member}{'domainandname'} $Error :" . $^E ); } else { &LogText($LogFile, "Added:\t$SidMap{$OldTextSid}\tOldMember: ${$member}{'domainandname'}"); } } else { #&LogText($LogFile, "Not in SidMap:\t${$Group}{'name'} Member: ${$member}{'domainandname'}\t$OldTextSid"); } } } undef @Groups; }
|
|
Top
|
|
|
|
Moderator: Shawn, ShaneEP, Ruud van Velsen, Arend_, Jochen, Radimus, Glenn Barnas, Allen, Mart
|
0 registered
and 693 anonymous users online.
|
|
|