in managementnode/lib/VCL/Module/OS/Windows/Version_6.pm [1642:1900]
sub run_sysprep {
my $self = shift;
if (ref($self) !~ /windows/i) {
notify($ERRORS{'CRITICAL'}, 0, "subroutine was called as a function, it must be called as a class method");
return;
}
my $computer_node_name = $self->data->get_computer_node_name();
my $system32_path = $self->get_system32_path() || return;
my $node_configuration_directory = $self->get_node_configuration_directory();
my $time_zone_name = $self->get_time_zone_name();
if (!$time_zone_name) {
notify($ERRORS{'WARNING'}, 0, "time zone name could not be retrieved");
return;
}
my $product_key = $self->get_kms_client_product_key();
if (!$product_key) {
notify($ERRORS{'WARNING'}, 0, "KMS client product key could not be retrieved");
return;
}
# Set the processorArchitecture to either amd64 or x86 in the XML depending on whether or not the OS is 64-bit
my $architecture = $self->is_64_bit() ? 'amd64' : 'x86';
my $unattend_xml_file_path = "$system32_path/sysprep/Unattend.xml";
my $unattend_xml_contents = <<EOF;
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
<settings pass="generalize">
<component name="Microsoft-Windows-PnpSysprep" processorArchitecture="$architecture" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<PersistAllDeviceInstalls>false</PersistAllDeviceInstalls>
<DoNotCleanUpNonPresentDevices>false</DoNotCleanUpNonPresentDevices>
</component>
<component name="Microsoft-Windows-Security-SPP" processorArchitecture="$architecture" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SkipRearm>1</SkipRearm>
</component>
</settings>
<settings pass="specialize">
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="$architecture" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Display>
<ColorDepth>32</ColorDepth>
<DPI>120</DPI>
<HorizontalResolution>1024</HorizontalResolution>
<VerticalResolution>768</VerticalResolution>
<RefreshRate>72</RefreshRate>
</Display>
<ComputerName>*</ComputerName>
<TimeZone>$time_zone_name</TimeZone>
<ProductKey>$product_key</ProductKey>
</component>
<component name="Microsoft-Windows-Deployment" processorArchitecture="$architecture" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<RunSynchronous>
<RunSynchronousCommand wcm:action="add">
<Path>C:\\Cygwin\\home\\root\\VCL\\Scripts\\sysprep_cmdlines.cmd > C:\\cygwin\\home\\root\\VCL\\Logs\\sysprep_cmdlines.log 2>&1</Path>
<Order>1</Order>
</RunSynchronousCommand>
</RunSynchronous>
</component>
<component name="Microsoft-Windows-Security-SPP-UX" processorArchitecture="$architecture" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SkipAutoActivation>true</SkipAutoActivation>
</component>
<component name="Microsoft-Windows-UnattendedJoin" processorArchitecture="$architecture" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Identification>
<JoinWorkgroup>VCL</JoinWorkgroup>
</Identification>
</component>
</settings>
<settings pass="auditSystem">
<component name="Microsoft-Windows-PnpCustomizationsNonWinPE" processorArchitecture="$architecture" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<DriverPaths>
<PathAndCredentials wcm:action="add" wcm:keyValue="1">
<Path>C:\\Cygwin\\home\\root\\VCL\\Drivers</Path>
</PathAndCredentials>
</DriverPaths>
</component>
</settings>
<settings pass="oobeSystem">
<component name="Microsoft-Windows-Shell-Setup" processorArchitecture="$architecture" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<OOBE>
<HideEULAPage>true</HideEULAPage>
<NetworkLocation>Work</NetworkLocation>
<ProtectYourPC>3</ProtectYourPC>
</OOBE>
<UserAccounts>
<AdministratorPassword>
<Value>$WINDOWS_ROOT_PASSWORD</Value>
<PlainText>true</PlainText>
</AdministratorPassword>
<LocalAccounts>
<LocalAccount wcm:action="add">
<Password>
<Value>$WINDOWS_ROOT_PASSWORD</Value>
<PlainText>true</PlainText>
</Password>
<Group>Administrators</Group>
<Name>root</Name>
<DisplayName>root</DisplayName>
<Description>VCL root account</Description>
</LocalAccount>
</LocalAccounts>
</UserAccounts>
</component>
<component name="Microsoft-Windows-International-Core" processorArchitecture="$architecture" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<InputLocale>en-US</InputLocale>
<SystemLocale>en-US</SystemLocale>
<UILanguage>en-US</UILanguage>
<UserLocale>en-US</UserLocale>
</component>
</settings>
</unattend>
EOF
notify($ERRORS{'DEBUG'}, 0, "'$unattend_xml_file_path' contents:\n$unattend_xml_contents");
if (!$self->create_text_file($unattend_xml_file_path, $unattend_xml_contents)) {
return;
}
# Delete existing Panther directory, contains Sysprep log files
$self->delete_file('C:/Windows/Panther');
# Delete existing sysprep/Panther directory, contains Sysprep log files
$self->delete_file("$system32_path/sysprep/Panther");
# Delete existing setupapi files
$self->delete_file('C:/Windows/inf/setupapi*');
# Delete existing INFCACHE files
$self->delete_file('C:/Windows/inf/INFCACHE*');
# Delete existing INFCACHE files
$self->delete_file('C:/Windows/inf/oem*.inf');
# Delete existing Sysprep_succeeded.tag file
$self->delete_file("$system32_path/sysprep/Sysprep*.tag");
# Delete existing MSDTC.LOG file
$self->delete_file("$system32_path/MsDtc/MSTTC.LOG");
# Delete existing VCL log files
$self->delete_file("C:/Cygwin/home/root/VCL/Logs/*");
# Delete legacy Sysprep directory
$self->delete_file("C:/Cygwin/home/root/VCL/Utilities/Sysprep");
# Grant permissions to the SYSTEM user - this is needed or else Sysprep fails
$self->execute("cmd.exe /c \"$system32_path/icacls.exe $node_configuration_directory /grant SYSTEM:(OI)(CI)(F) /C\"");
# Uninstall and reinstall MsDTC
my $msdtc_command = "$system32_path/msdtc.exe -uninstall ; $system32_path/msdtc.exe -install";
my ($msdtc_status, $msdtc_output) = $self->execute($msdtc_command);
if (defined($msdtc_status) && $msdtc_status == 0) {
notify($ERRORS{'DEBUG'}, 0, "reinstalled MsDtc");
}
elsif (defined($msdtc_status)) {
notify($ERRORS{'WARNING'}, 0, "failed to reinstall MsDtc, exit status: $msdtc_status, output:\n@{$msdtc_output}");
}
else {
notify($ERRORS{'WARNING'}, 0, "unable to run ssh command to reinstall MsDtc");
}
# Get the node drivers directory and convert it to DOS format
my $drivers_directory = "$node_configuration_directory/Drivers";
$drivers_directory =~ s/\//\\\\/g;
# Set the Installation Sources registry key
# Must use reg_add because the type is REG_MULTI_SZ
my $setup_key = 'HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup';
if ($self->reg_add($setup_key, 'Installation Sources', 'REG_MULTI_SZ', $drivers_directory)) {
notify($ERRORS{'DEBUG'}, 0, "added Installation Sources registry key");
}
else {
notify($ERRORS{'WARNING'}, 0, "failed to add Installation Sources registry key");
}
# Set the DevicePath registry key
# This is used to locate device drivers
if (!$self->set_device_path_key()) {
notify($ERRORS{'WARNING'}, 0, "failed to set the DevicePath registry key");
return;
}
# Reset the Windows setup registry keys
# If Sysprep fails it will set keys which make running Sysprep again impossible
# These keys never get reset, Microsoft instructs you to reinstall the OS
# Clearing out these keys before running Sysprep allows it to be run again
# Also enable verbose Sysprep logging
my $registry_string .= <<"EOF";
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup]
"LogLevel"=dword:0000FFFF
[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State]
"ImageState"="IMAGE_STATE_COMPLETE"
[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\Sysprep\\Generalize]
"{82468857-ad9b-1a37-533f-7db889fff253}"=-
[-HKEY_LOCAL_MACHINE\\SYSTEM\\Setup\\Status]
EOF
# Import the string into the registry
if ($self->import_registry_string($registry_string)) {
notify($ERRORS{'OK'}, 0, "reset Windows setup state in the registry");
}
else {
notify($ERRORS{'WARNING'}, 0, "failed to reset the Windows setup state in the registry");
return 0;
}
# Kill the screen saver process, it occasionally prevents reboots and shutdowns from working
$self->kill_process('logon.scr');
# Run Sysprep.exe, use cygstart to lauch the .exe and return immediately
my $sysprep_command = "/bin/cygstart.exe \$SYSTEMROOT/system32/cmd.exe /c \"";
# Run Sysprep.exe
$sysprep_command .= "$system32_path/sysprep/sysprep.exe /generalize /oobe /shutdown /quiet /unattend:\$SYSTEMROOT/System32/sysprep/Unattend.xml";
$sysprep_command .= "\"";
# Run Sysprep.exe, use cygstart to lauch the .exe and return immediately
my ($sysprep_status, $sysprep_output) = $self->execute($sysprep_command);
if (defined($sysprep_status) && $sysprep_status == 0) {
notify($ERRORS{'OK'}, 0, "initiated Sysprep.exe, waiting for $computer_node_name to become unresponsive");
}
elsif (defined($sysprep_status)) {
notify($ERRORS{'OK'}, 0, "failed to initiate Sysprep.exe, exit status: $sysprep_status, output:\n@{$sysprep_output}");
return 0;
}
else {
notify($ERRORS{'WARNING'}, 0, "unable to run ssh command to initiate Sysprep.exe");
return 0;
}
# Wait maximum of 30 minutes for the computer to become unresponsive
if (!$self->wait_for_no_ping(1800)) {
# Computer never stopped responding to ping
notify($ERRORS{'WARNING'}, 0, "$computer_node_name never became unresponsive to ping");
return;
}
# Wait maximum of 15 minutes for computer to power off
my $power_off = $self->provisioner->wait_for_power_off(900);
if (!defined($power_off)) {
# wait_for_power_off result will be undefined if the provisioning module doesn't implement a power_status subroutine
notify($ERRORS{'OK'}, 0, "unable to determine power status of $computer_node_name from provisioning module, sleeping 5 minutes to allow computer time to shutdown");
sleep 300;
}
elsif (!$power_off) {
notify($ERRORS{'WARNING'}, 0, "$computer_node_name never powered off");
return;
}
return 1;
}