You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@vcl.apache.org by ar...@apache.org on 2008/12/30 20:26:51 UTC

svn commit: r730217 [2/3] - in /incubator/vcl/trunk/managementnode/lib/VCL: DataStructure.pm Module/OS/Windows/Desktop/Vista.pm Module/Provisioning/xCAT.pm new.pm reclaim.pm reserved.pm utils.pm

Modified: incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows/Desktop/Vista.pm
URL: http://svn.apache.org/viewvc/incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows/Desktop/Vista.pm?rev=730217&r1=730216&r2=730217&view=diff
==============================================================================
--- incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows/Desktop/Vista.pm (original)
+++ incubator/vcl/trunk/managementnode/lib/VCL/Module/OS/Windows/Desktop/Vista.pm Tue Dec 30 11:26:51 2008
@@ -49,6 +49,8 @@
 # Specify the version of Perl to use
 use 5.008000;
 
+no warnings 'redefine';
+
 use strict;
 use warnings;
 use diagnostics;
@@ -94,7 +96,7 @@
 
 #/////////////////////////////////////////////////////////////////////////////
 
-=head2 post_reload
+=head2 post_load
 
  Parameters  :
  Returns     :
@@ -102,7 +104,7 @@
 
 =cut
 
-sub post_reload {
+sub post_load {
 	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");
@@ -112,60 +114,93 @@
 	my $management_node_keys     = $self->data->get_management_node_keys();
 	my $image_name               = $self->data->get_image_name();
 	my $computer_node_name       = $self->data->get_computer_node_name();
-	
+
 	notify($ERRORS{'OK'}, 0, "beginning Windows Vista post-reload preparation tasks: $image_name on $computer_node_name");
+
+	# Set KMS licensing
+	if (!$self->set_kms_licensing('kms.unity.ad.ncsu.edu')) {
+		notify($ERRORS{'WARNING'}, 0, "OS post-reload configuration failed, failed to configure node for KMS licensing");
+		return 0;
+	}
 	
-	# Run NewSID
-	if (!$self->run_newsid()) {
-		notify($ERRORS{'WARNING'}, 0, "OS post-reload configuration failed, unable to run newsid.exe on $computer_node_name");
+	# Activate Microsoft Vista software licensing
+	if (!$self->activate_licensing()) {
+		notify($ERRORS{'WARNING'}, 0, "OS post-reload configuration failed, failed to activate licensing");
+		return 0;
+	}
+
+	# Disable Vista's defrag scheduled task
+	if (!$self->disable_scheduled_task('\Microsoft\Windows\Defrag\ScheduledDefrag')) {
+		notify($ERRORS{'WARNING'}, 0, "capture preparation failed, unable to disable defrag scheduled task");
 		return 0;
 	}
 	
-	# Reboot the computer in order for the newsid changes to take effect
-	if (!$self->reboot()) {
-		notify($ERRORS{'WARNING'}, 0, "OS post-reload configuration failed, failed to reboot computer after disabling pagefile");
+	# Disable system restore scheduled task
+	if (!$self->disable_scheduled_task('\Microsoft\Windows\SystemRestore\SR')) {
+		notify($ERRORS{'WARNING'}, 0, "capture preparation failed, unable to disable system restore scheduled task");
 		return 0;
 	}
 	
-	# Set KMS licensing
-	if (!$self->set_kms_licensing()) {
-		notify($ERRORS{'WARNING'}, 0, "OS post-reload configuration failed, failed to configure node for KMS licensing");
+	# Disable customer improvement program consolidator scheduled task
+	if (!$self->disable_scheduled_task('\Microsoft\Windows\Customer Experience Improvement Program\Consolidator')) {
+		notify($ERRORS{'WARNING'}, 0, "capture preparation failed, unable to disable customer improvement program consolidator scheduled task");
 		return 0;
 	}
 	
-	# Activate
-	if (!$self->activate_licensing()) {
-		notify($ERRORS{'WARNING'}, 0, "OS post-reload configuration failed, failed to activate licensing");
+	# Disable customer improvement program opt-in notification scheduled task
+	if (!$self->disable_scheduled_task('\Microsoft\Windows\Customer Experience Improvement Program\OptinNotification')) {
+		notify($ERRORS{'WARNING'}, 0, "capture preparation failed, unable to disable customer improvement program opt-in notification scheduled task");
+		return 0;
+	}
+
+	# Set the computer description to the image pretty name and image name
+	if (!$self->set_computer_description()) {
+		notify($ERRORS{'WARNING'}, 0, "capture preparation failed, unable to set the computer description");
 		return 0;
 	}
 	
-	## Randomize root password
-	#my $root_random_password = getpw();
-	#if (!$self->set_password('root', $root_random_password)) {
-	#	notify($ERRORS{'WARNING'}, 0, "OS post-reload configuration failed, failed to set random root password");
-	#	return 0;
-	#}
+	# Set the "My Computer" description to the image pretty name
+	if (!$self->rename_my_computer()) {
+		notify($ERRORS{'WARNING'}, 0, "capture preparation failed, unable to rename my computer");
+		return 0;
+	}
 	
-	# Randomize Administrator password
-	my $administrator_random_password = getpw();
-	if (!$self->set_password('Administrator', $administrator_random_password)) {
-		notify($ERRORS{'WARNING'}, 0, "OS post-reload configuration failed, failed to set random Administrator password");
+	# Run NewSID, this initiates a reboot
+	# The run_newsid subroutine monitors the reboot and returns when it has completed
+	if (!$self->run_newsid()) {
+		notify($ERRORS{'WARNING'}, 0, "OS post-reload configuration failed, unable to run newsid.exe on $computer_node_name");
 		return 0;
 	}
 	
-	# Disable RDP
+	# ********* node reboots *********
+
+	# Disable RDP firewall exceptions from all addresses
 	if (!$self->firewall_disable_rdp()) {
 		notify($ERRORS{'WARNING'}, 0, "OS post-reload configuration failed, failed to disable RDP");
 		return 0;
 	}
-	
+
 	# Set sshd service startup to auto
 	if (!$self->set_service_startup_mode('sshd', 'auto')) {
 		notify($ERRORS{'WARNING'}, 0, "OS post-reload configuration failed, failed to set sshd service startup mode to auto");
 		return 0;
 	}
+
+	# Randomize root password
+	my $root_random_password = getpw();
+	if (!$self->set_password('root', $root_random_password)) {
+		notify($ERRORS{'WARNING'}, 0, "OS post-reload configuration failed, failed to set random root password");
+		return 0;
+	}
+
+	# Randomize Administrator password
+	my $administrator_random_password = getpw();
+	if (!$self->set_password('Administrator', $administrator_random_password)) {
+		notify($ERRORS{'WARNING'}, 0, "OS post-reload configuration failed, failed to set random Administrator password");
+		return 0;
+	}
 	
-	notify($ERRORS{'WARNING'}, 0, "OS post-reload configuration successful, returning 1");
+	notify($ERRORS{'OK'}, 0, "Vista OS post-reload configuration successful, returning 1");
 	return 1;
 }
 
@@ -235,8 +270,6 @@
 		return 0;
 	}
 	
-$self->firewall_enable_rdp('152.14.52.0/24');
-	
 	# Enable SSH access from private IP addresses by adding a firewall exception
 	if (!$self->firewall_enable_ssh_private()) {
 		notify($ERRORS{'WARNING'}, 0, "capture preparation failed, unable to enable SSH from private IP addresses");
@@ -248,12 +281,6 @@
 		notify($ERRORS{'WARNING'}, 0, "capture preparation failed, unable to enable ping from private IP addresses");
 		return 0;
 	}
-
-	# Create startup scheduled task to prepare computer
-	if (!$self->create_startup_scheduled_task('VCL Startup Configuration', $NODE_CONFIGURATION_DIRECTORY . '/Scripts/VCLPrepare.cmd  >> ' . $NODE_CONFIGURATION_DIRECTORY . '/Logs/VCLPrepare.log')) {
-		notify($ERRORS{'WARNING'}, 0, "capture preparation failed, unable to create startup scheduled task");
-		return 0;
-	}
 	
 	# Remove old configuration files if they exist
 	notify($ERRORS{'OK'}, 0, "attempting to remove old configuration directory if it exists: $NODE_CONFIGURATION_DIRECTORY");
@@ -287,9 +314,15 @@
 		notify($ERRORS{'WARNING'}, 0, "capture preparation failed, failed to copy $CONFIGURATION_DIRECTORY to $computer_node_name");
 		return 0;
 	}
-	
-	# Disagle autoadminlogon before disabling the pagefile and rebooting
-	# There is no need to automatically logon after the reboot
+
+	# Create startup scheduled task to prepare computer
+	if (!$self->create_startup_scheduled_task('VCL Startup Configuration', $NODE_CONFIGURATION_DIRECTORY . '/Scripts/vcl_startup.cmd  >> ' . $NODE_CONFIGURATION_DIRECTORY . '/Logs/vcl_startup.log')) {
+		notify($ERRORS{'WARNING'}, 0, "capture preparation failed, unable to create startup scheduled task");
+		return 0;
+	}
+
+	# Disable autoadminlogon before disabling the pagefile and rebooting
+	# There is no longer a need to automatically logon after the reboot
 	if (!$self->disable_autoadminlogon()) {
 		notify($ERRORS{'WARNING'}, 0, "capture preparation failed, unable to disable autoadminlogon");
 		return 0;
@@ -307,8 +340,14 @@
 		return 0;
 	}
 	
+	# Disable dynamic dns
+	if (!$self->disable_dynamic_dns()) {
+		notify($ERRORS{'WARNING'}, 0, "capture preparation failed, unable to disable dynamic dns");
+		return 0;
+	}
+	
 	# Call script to clean up the hard drive
-	my $cleanup_command = $NODE_CONFIGURATION_DIRECTORY . '/Scripts/cleanup_hard_drive.cmd > ' . $NODE_CONFIGURATION_DIRECTORY . '/logs/cleanup_hard_drive.log';
+	my $cleanup_command = $NODE_CONFIGURATION_DIRECTORY . '/Scripts/cleanup_hard_drive.cmd > ' . $NODE_CONFIGURATION_DIRECTORY . '/Logs/cleanup_hard_drive.log';
 	my ($cleanup_status, $cleanup_output) = run_ssh_command($computer_node_name, $management_node_keys, $cleanup_command);
 	if (defined($cleanup_status) && $cleanup_status == 0) {
 		notify($ERRORS{'OK'}, 0, "successfully ran cleanup script");
@@ -322,11 +361,11 @@
 		return 0;
 	}
 	
-## Defragment the hard drive
-#if (!$self->defragment_hard_drive()) {
-#	notify($ERRORS{'WARNING'}, 0, "capture preparation failed, unable to defragment the hard drive");
-#	return 0;
-#}
+	## Defragment the hard drive
+	#if (!$self->defragment_hard_drive()) {
+	#	notify($ERRORS{'WARNING'}, 0, "capture preparation failed, unable to defragment the hard drive");
+	#	return 0;
+	#}
 
 	# Disable and delete the pagefile
 	# This will set the registry key to disable the pagefile, reboot, then delete pagefile.sys
@@ -409,18 +448,6 @@
 		return 0;
 	}
 
-	## Enable autoadminlogon
-	#if (!$self->enable_autoadminlogon()) {
-	#	notify($ERRORS{'WARNING'}, 0, "capture preparation failed, unable to enable autoadminlogon");
-	#	return 0;
-	#}
-
-	## Add a RunOnce registry key to run VCLprepare.cmd when the captured image comes up
-	#if (!$self->add_runonce_registry_value("VCL Prepare", 'C:\\\\VCL\\\\Scripts\\\\VCLprepare.cmd')) {
-	#	notify($ERRORS{'WARNING'}, 0, "capture preparation failed, unable to add runonce registry value to call VCLprepare.cmd");
-	#	return 0;
-	#}
-
 	# Set sshd service startup mode to manual
 	if (!$self->set_service_startup_mode('sshd', 'manual')) {
 		notify($ERRORS{'WARNING'}, 0, "capture preparation failed, unable to set sshd service startup mode to manual");
@@ -478,52 +505,6 @@
 
 #/////////////////////////////////////////////////////////////////////////////
 
-=head2 user_exists
-
- Parameters  :
- Returns     :
- Description :
-
-=cut
-
-sub user_exists {
-	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 $management_node_keys     = $self->data->get_management_node_keys();
-	my $computer_node_name       = $self->data->get_computer_node_name();
-	
-	# Attempt to get the user login id from the arguments
-	# If not specified, use DataStructure value
-	my $user_login_id = shift;
-	if (!defined($user_login_id)) {
-		$user_login_id = $self->data->get_user_login_id();
-	}
-
-	my ($net_user_exit_status, $net_user_output) = run_ssh_command($computer_node_name, $management_node_keys, "net user $user_login_id");
-	if (defined($net_user_exit_status) && $net_user_exit_status == 0) {
-		notify($ERRORS{'OK'}, 0, "user $user_login_id exists on $computer_node_name");
-		return 1;
-	}
-	elsif ($net_user_exit_status == 2) {
-		notify($ERRORS{'OK'}, 0, "user $user_login_id does NOT exist on $computer_node_name");
-		return 0;
-	}
-	elsif ($net_user_exit_status) {
-		notify($ERRORS{'WARNING'}, 0, "failed to determine if user $user_login_id exists on $computer_node_name, exit status: $net_user_exit_status, output:\n@{$net_user_output}");
-		return;
-	}
-	else {
-		notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to determine if user $user_login_id exists on $computer_node_name");
-		return;
-	}
-}
-
-#/////////////////////////////////////////////////////////////////////////////
-
 =head2 logoff_users
 
  Parameters  :
@@ -579,15 +560,15 @@
 
 #/////////////////////////////////////////////////////////////////////////////
 
-=head2 delete_user
+=head2 add_users
 
- Parameters  : $node, $user, $type, $osname
- Returns     : 1 success 0 failure
- Description : removes user account and profile directory from specificed node
+ Parameters  : 
+ Returns     : 
+ Description : 
 
 =cut
 
-sub delete_user {
+sub add_users {
 	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");
@@ -597,63 +578,58 @@
 	my $management_node_keys     = $self->data->get_management_node_keys();
 	my $computer_node_name       = $self->data->get_computer_node_name();
 	
-	# Attempt to get the username from the arguments
-	# If no argument was supplied, use the user specified in the DataStructure
-	my $username = shift;
-	if (!(defined($username))) {
-		$username = $self->data->get_user_login_id();
-	}
-	
-	notify($ERRORS{'OK'}, 0, "attempting to delete user $username from $computer_node_name");
-	
-	# Attempt to delete the user account
-	my $delete_user_command = "net user $username /DELETE";
-	my ($delete_user_exit_status, $delete_user_output) = run_ssh_command($computer_node_name, $management_node_keys, $delete_user_command);
-	if (defined($delete_user_exit_status) && $delete_user_exit_status == 0) {
-		notify($ERRORS{'OK'}, 0, "deleted user $username from $computer_node_name");
-	}
-	elsif (defined($delete_user_exit_status) && $delete_user_exit_status == 2) {
-		notify($ERRORS{'OK'}, 0, "user $username was not deleted, user does not exist");
-		return 1;
-	}
-	elsif (defined($delete_user_exit_status)) {
-		notify($ERRORS{'WARNING'}, 0, "failed to delete user $username from $computer_node_name, exit status: $delete_user_exit_status, output:\n@{$delete_user_output}");
-		return 0;
+	# Attempt to get the user array from the arguments
+	# If no argument was supplied, use the users specified in the DataStructure
+	my $user_array_ref = shift;
+	my @users;
+	if ($user_array_ref) {
+		$user_array_ref = $self->data->get_imagemeta_usergroupmembers();
+		@users = @{$user_array_ref};
 	}
 	else {
-		notify($ERRORS{'WARNING'}, 0, "failed to run ssh command delete user $username from $computer_node_name");
-		return;
-	}
-
-	# Delete the user's home directory
-	my $delete_profile_command = "/bin/rm -rf /cygdrive/c/Users/$username";
-	my ($delete_profile_exit_status, $delete_profile_output) = run_ssh_command($computer_node_name, $management_node_keys, $delete_profile_command);
-	if (defined($delete_profile_exit_status) && $delete_profile_exit_status == 0) {
-		notify($ERRORS{'OK'}, 0, "deleted profile for user $username from $computer_node_name");
+		# User list was not specified as an argument
+		# Use the imagemeta group members and the primary reservation user
+		my $user_login_id      = $self->data->get_user_login_id();
+		my $user_group_members = $self->data->get_imagemeta_usergroupmembers();
+		
+		push @users, $user_login_id;
+		
+		foreach my $user_group_member_uid (keys(%{$user_group_members})) {
+			my $user_group_member_login_id = $user_group_members->{$user_group_member_uid};
+			push @users, $user_group_member_login_id;
+		}
 	}
-	elsif (defined($delete_profile_exit_status)) {
-		notify($ERRORS{'WARNING'}, 0, "failed to delete profile for user $username from $computer_node_name, exit status: $delete_profile_exit_status, output:\n@{$delete_profile_output}");
-		return 0;
+	
+	# Attempt to get the password from the arguments
+	# If no argument was supplied, use the password specified in the DataStructure
+	my $password = shift;
+	if (!$password) {
+		$password = $self->data->get_reservation_password();
 	}
-	else {
-		notify($ERRORS{'WARNING'}, 0, "failed to run ssh command delete profile for user $username from $computer_node_name");
-		return;
+	
+	# Loop through the users in the imagemeta group and attempt to add them
+	for my $username (@users) {
+		if (!$self->create_user($username, $password)) {
+			notify($ERRORS{'WARNING'}, 0, "failed to add users to $computer_node_name");
+			return 0;
+		}
 	}
 	
+	notify($ERRORS{'OK'}, 0, "successfully added " . @users . " users to $computer_node_name");
 	return 1;
-} ## end sub del_user
+}
 
 #/////////////////////////////////////////////////////////////////////////////
 
-=head2 set_password
+=head2 delete_users
 
- Parameters  : $username, $password
+ Parameters  : 
  Returns     : 
  Description : 
 
 =cut
 
-sub set_password {
+sub delete_users {
 	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");
@@ -663,44 +639,51 @@
 	my $management_node_keys     = $self->data->get_management_node_keys();
 	my $computer_node_name       = $self->data->get_computer_node_name();
 	
-	# Attempt to get the username from the arguments
-	my $username = shift;
-	my $password = shift;
-	
-	# If no argument was supplied, use the user specified in the DataStructure
-	if (!defined($username) && !defined($password)) {
-		$username = $self->data->get_user_logon_id();
-		$password = $self->data->get_reservation_password();
+	# Attempt to get the user array from the arguments
+	# If no argument was supplied, use the users specified in the DataStructure
+	my $user_array_ref = shift;
+	my @users;
+	if ($user_array_ref) {
+		$user_array_ref = $self->data->get_imagemeta_usergroupmembers();
+		@users = @{$user_array_ref};
 	}
-	elsif (defined($username) && !defined($password)) {
-		notify($ERRORS{'WARNING'}, 0, "password set failed, username $username was passed as an argument but the password was not");
-		return 0;
+	else {
+		# User list was not specified as an argument
+		# Use the imagemeta group members and the primary reservation user
+		my $user_login_id      = $self->data->get_user_login_id();
+		my $user_group_members = $self->data->get_imagemeta_usergroupmembers();
+		
+		push @users, $user_login_id;
+		
+		foreach my $user_group_member_uid (keys(%{$user_group_members})) {
+			my $user_group_member_login_id = $user_group_members->{$user_group_member_uid};
+			push @users, $user_group_member_login_id;
+		}
 	}
 	
-	# Attempt to set the password
-	notify($ERRORS{'OK'}, 0, "setting password of $username to $password on $computer_node_name");
-	my ($set_password_exit_status, $set_password_output) = run_ssh_command($computer_node_name, $management_node_keys, "net user $username '$password'");
-	if ($set_password_exit_status == 0) {
-		notify($ERRORS{'OK'}, 0, "$username password changed to $password on $computer_node_name");
-		return 1;
-	}
-	else {
-		notify($ERRORS{'WARNING'}, 0, "failed to set password of $username to $password on $computer_node_name, exit status: $set_password_exit_status, output:\n@{$set_password_output}");
-		return 0;
+	# Loop through the users and attempt to delete them
+	for my $username (@users) {
+		if (!$self->delete_user($username)) {
+			notify($ERRORS{'WARNING'}, 0, "failed to delete user $username from $computer_node_name");
+			return 0;
+		}
 	}
+	
+	notify($ERRORS{'OK'}, 0, "successfully deleted " . @users . " users from $computer_node_name");
+	return 1;
 }
 
 #/////////////////////////////////////////////////////////////////////////////
 
-=head2 disable_pagefile
+=head2 user_exists
 
- Parameters  :
- Returns     :
- Description :
+ Parameters  : 
+ Returns     : 
+ Description : 
 
 =cut
 
-sub disable_pagefile {
+sub user_exists {
 	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");
@@ -710,60 +693,47 @@
 	my $management_node_keys     = $self->data->get_management_node_keys();
 	my $computer_node_name       = $self->data->get_computer_node_name();
 	
-	my $registry_string .= <<"EOF";
-Windows Registry Editor Version 5.00
-
-[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management]
-"PagingFiles"=""
-EOF
-	
-	# Import the string into the registry
-	if ($self->import_registry_string($registry_string)) {
-		notify($ERRORS{'OK'}, 0, "successfully set the registry key to disable the pagefile");
-	}
-	else {
-		notify($ERRORS{'WARNING'}, 0, "failed to set the registry key to disable the pagefile");
-		return 0;
-	}
-
-	# Attempt to reboot the computer in order to delete the pagefile
-	if ($self->reboot()) {
-		notify($ERRORS{'OK'}, 0, "computer was rebooted after disabling pagefile in the registry");
-	}
-	else {
-		notify($ERRORS{'WARNING'}, 0, "failed to reboot computer after disabling pagefile");
-		return;
+	# Attempt to get the username from the arguments
+	# If no argument was supplied, use the user specified in the DataStructure
+	my $username = shift;
+	if (!$username) {
+		$username = $self->data->get_user_login_id();
 	}
 	
-	# Attempt to delete the pagefile
-	my $delete_pagefile_command = "attrib.exe -S -H -R C:/pagefile.sys";
-	$delete_pagefile_command .= " && /usr/bin/rm.exe -rfv C:/pagefile.sys";
-	my ($delete_pagefile_exit_status, $delete_pagefile_output) = run_ssh_command($computer_node_name, $management_node_keys, $delete_pagefile_command);
-	if (defined($delete_pagefile_exit_status) && $delete_pagefile_exit_status == 0) {
-		notify($ERRORS{'OK'}, 0, "pagefile.sys was deleted");
+	notify($ERRORS{'OK'}, 0, "checking if user $username exists on $computer_node_name");
+	
+	# Attempt to query the user account
+	my $query_user_command = "net user \"$username\"";
+	my ($query_user_exit_status, $query_user_output) = run_ssh_command($computer_node_name, $management_node_keys, $query_user_command);
+	if (defined($query_user_exit_status) && $query_user_exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "user $username exists on $computer_node_name");
 		return 1;
 	}
-	elsif ($delete_pagefile_exit_status) {
-		notify($ERRORS{'WARNING'}, 0, "failed to delete pagefile.sys, exit status: $delete_pagefile_exit_status, output:\n@{$delete_pagefile_output}");
+	elsif (defined($query_user_exit_status) && $query_user_exit_status == 2) {
+		notify($ERRORS{'OK'}, 0, "user $username does not exist on $computer_node_name");
+		return 0;
+	}
+	elsif (defined($query_user_exit_status)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to determine if user $username exists on $computer_node_name, exit status: $query_user_exit_status, output:\n@{$query_user_output}");
 		return;
 	}
 	else {
-		notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to delete pagefile.sys");
+		notify($ERRORS{'WARNING'}, 0, "failed to run ssh command to determine if user $username exists on $computer_node_name");
 		return;
 	}
-}
+} ## end sub del_user
 
 #/////////////////////////////////////////////////////////////////////////////
 
-=head2 disable_pagefile
+=head2 create_user
 
- Parameters  :
- Returns     :
- Description :
+ Parameters  : 
+ Returns     : 
+ Description : 
 
 =cut
 
-sub enable_pagefile {
+sub create_user {
 	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");
@@ -773,36 +743,78 @@
 	my $management_node_keys     = $self->data->get_management_node_keys();
 	my $computer_node_name       = $self->data->get_computer_node_name();
 	
-	my $registry_string .= <<'EOF';
-Windows Registry Editor Version 5.00
-
-[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management]
-"PagingFiles"="?:\\\\pagefile.sys"
-EOF
-	
-	# Import the string into the registry
-	if ($self->import_registry_string($registry_string)) {
-		notify($ERRORS{'OK'}, 0, "successfully set the registry key to enable the pagefile");
-	}
-	else {
-		notify($ERRORS{'WARNING'}, 0, "failed to set the registry key to enable the pagefile");
+	# Attempt to get the username from the arguments
+	# If no argument was supplied, use the user specified in the DataStructure
+	my $username = shift;
+	my $password = shift;
+	if (!$username) {
+		$username = $self->data->get_user_login_id();
+	}
+	if (!$password) {
+		$password = $self->data->get_user_password();
+	}
+	
+	# Check if user already exists
+	if ($self->user_exists($username)) {
+		notify($ERRORS{'OK'}, 0, "user $username already exists on $computer_node_name, attempting to delete user");
+		
+		# Attempt to delete the user
+		if (!$self->delete_user($username)) {
+			notify($ERRORS{'WARNING'}, 0, "failed to add user $username to $computer_node_name, user already exists and could not be deleted");
+			return 0;
+		}
+	}
+	
+	notify($ERRORS{'OK'}, 0, "attempting to add user $username to $computer_node_name");
+	
+	# Attempt to add the user account
+	my $add_user_command = "net user \"$username\" \"$password\" /ADD  /EXPIRES:NEVER /COMMENT:\"Account created by VCL\"";
+	$add_user_command .= " && net localgroup \"Administrators\" \"$username\" /ADD";
+	$add_user_command .= " && net localgroup \"Remote Desktop Users\" \"$username\" /ADD";
+	
+	my ($add_user_exit_status, $add_user_output) = run_ssh_command($computer_node_name, $management_node_keys, $add_user_command);
+	if (defined($add_user_exit_status) && $add_user_exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "added user $username from $computer_node_name");
+	}
+	elsif (defined($add_user_exit_status) && $add_user_exit_status == 2) {
+		notify($ERRORS{'OK'}, 0, "user $username was not added, user already exists");
+		return 1;
+	}
+	elsif (defined($add_user_exit_status)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to add user $username to $computer_node_name, exit status: $add_user_exit_status, output:\n@{$add_user_output}");
 		return 0;
 	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to run ssh command add user $username to $computer_node_name");
+		return;
+	}
+	
+	## Add the user to the Administrators group
+	#if (!$self->add_user_to_group($username, 'Administrators')) {
+	#	notify($ERRORS{'WARNING'}, 0, "added user $username but failed to add user to Administrators group");
+	#	return 0;
+	#}
+	#
+	## Add the user to the Remote Desktop Users group
+	#if (!$self->add_user_to_group($username, 'Remote Desktop Users')) {
+	#	notify($ERRORS{'WARNING'}, 0, "added user $username but failed to add user to Remote Desktop Users group");
+	#	return 0;
+	#}
 	
 	return 1;
-}
+} ## end sub del_user
 
 #/////////////////////////////////////////////////////////////////////////////
 
-=head2 disable_ipv6
+=head2 add_user_to_group
 
- Parameters  :
- Returns     :
- Description :
+ Parameters  : 
+ Returns     : 
+ Description : 
 
 =cut
 
-sub disable_ipv6 {
+sub add_user_to_group {
 	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");
@@ -812,39 +824,58 @@
 	my $management_node_keys     = $self->data->get_management_node_keys();
 	my $computer_node_name       = $self->data->get_computer_node_name();
 	
-	my $registry_string .= <<'EOF';
-Windows Registry Editor Version 5.00
-
-; This registry file contains the entries to disable all IPv6 components 
-; http://support.microsoft.com/kb/929852
-
-[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters]
-"DisabledComponents"=dword:ffffffff
-EOF
+	# Attempt to get the username from the arguments
+	# If no argument was supplied, use the user specified in the DataStructure
+	my $username = shift;
+	my $group = shift;
+	if (!$username || !$group) {
+		notify($ERRORS{'WARNING'}, 0, "unable to add user to group, arguments were not passed correctly");
+		return;
+	}
 	
-	# Import the string into the registry
-	if ($self->import_registry_string($registry_string)) {
-		notify($ERRORS{'OK'}, 0, "successfully set the registry keys to disable IPv6");
+	# Attempt to add the user to the group using net localgroup
+	my $localgroup_user_command = "net localgroup \"$group\" $username /ADD";
+	my ($localgroup_user_exit_status, $localgroup_user_output) = run_ssh_command($computer_node_name, $management_node_keys, $localgroup_user_command);
+	if (defined($localgroup_user_exit_status) && $localgroup_user_exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "added user $username to \"$group\" group on $computer_node_name");
+	}
+	elsif (defined($localgroup_user_exit_status) && $localgroup_user_exit_status == 2) {
+		# Exit status is 2, this could mean the user is already a member or that the group doesn't exist
+		# Check the output to determine what happened
+		if (grep(/error 1378/, @{$localgroup_user_output})) {
+			# System error 1378 has occurred.
+			# The specified account name is already a member of the group.
+			notify($ERRORS{'OK'}, 0, "user $username was not added to $group group, user already a member");
+			return 1;
+		}
+		else {
+			notify($ERRORS{'WARNING'}, 0, "failed to add user $username to $group group on $computer_node_name, exit status: $localgroup_user_exit_status, output:\n@{$localgroup_user_output}");
+			return 0;
+		}
 	}
-	else {
-		notify($ERRORS{'WARNING'}, 0, "failed to set the registry keys to disable IPv6");
+	elsif (defined($localgroup_user_exit_status)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to add user $username to $group group on $computer_node_name, exit status: $localgroup_user_exit_status, output:\n@{$localgroup_user_output}");
 		return 0;
 	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to run ssh command to add user $username to $group group on $computer_node_name");
+		return;
+	}
 	
 	return 1;
 }
 
 #/////////////////////////////////////////////////////////////////////////////
 
-=head2 import_registry_file
+=head2 delete_user
 
- Parameters  :
- Returns     :
- Description :
+ Parameters  : $node, $user, $type, $osname
+ Returns     : 1 success 0 failure
+ Description : removes user account and profile directory from specificed node
 
 =cut
 
-sub import_registry_file {
+sub delete_user {
 	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");
@@ -854,63 +885,56 @@
 	my $management_node_keys     = $self->data->get_management_node_keys();
 	my $computer_node_name       = $self->data->get_computer_node_name();
 	
-	my $registry_file_path = shift;
-	if (!defined($registry_file_path) || !$registry_file_path) {
-		notify($ERRORS{'WARNING'}, 0, "registry file path was not passed correctly as an argument");
-		return;
+	# Attempt to get the username from the arguments
+	# If no argument was supplied, use the user specified in the DataStructure
+	my $username = shift;
+	if (!(defined($username))) {
+		$username = $self->data->get_user_login_id();
 	}
 	
-	my $registry_file_contents = `cat $registry_file_path`;
-	notify($ERRORS{'DEBUG'}, 0, "registry file contents:\n$registry_file_contents");
-	$registry_file_contents =~ s/([\"])/\\$1/gs;
-	
-	# Specify where on the node the temporary registry file will reside
-	my $temp_registry_file_path = 'C:/Cygwin/tmp/vcl_import.reg';
+	notify($ERRORS{'OK'}, 0, "attempting to delete user $username from $computer_node_name");
 	
-	# Echo the registry string to a file on the node	
-	my $echo_registry_command = "/usr/bin/echo.exe -E \"$registry_file_contents\" > " . $temp_registry_file_path;
-	my ($echo_registry_exit_status, $echo_registry_output) = run_ssh_command($computer_node_name, $management_node_keys, $echo_registry_command, '', '', 1);
-	if (defined($echo_registry_exit_status) && $echo_registry_exit_status == 0) {
-		notify($ERRORS{'OK'}, 0, "registry file contents echoed to $temp_registry_file_path");
+	# Attempt to delete the user account
+	my $delete_user_command = "net user $username /DELETE";
+	my ($delete_user_exit_status, $delete_user_output) = run_ssh_command($computer_node_name, $management_node_keys, $delete_user_command);
+	if (defined($delete_user_exit_status) && $delete_user_exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "deleted user $username from $computer_node_name");
 	}
-	elsif ($echo_registry_exit_status) {
-		notify($ERRORS{'WARNING'}, 0, "failed to echo registry file contents to $temp_registry_file_path, exit status: $echo_registry_exit_status, output:\n@{$echo_registry_output}");
-		return;
+	elsif (defined($delete_user_exit_status) && $delete_user_exit_status == 2) {
+		notify($ERRORS{'OK'}, 0, "user $username was not deleted, user does not exist");
+	}
+	elsif (defined($delete_user_exit_status)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to delete user $username from $computer_node_name, exit status: $delete_user_exit_status, output:\n@{$delete_user_output}");
+		return 0;
 	}
 	else {
-		notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to echo registry file contents to $temp_registry_file_path");
+		notify($ERRORS{'WARNING'}, 0, "failed to run ssh command delete user $username from $computer_node_name");
 		return;
 	}
-	
-	# Run reg.exe IMPORT
-	my $import_registry_command .= '"$SYSTEMROOT/System32/reg.exe" IMPORT ' . $temp_registry_file_path;
-	my ($import_registry_exit_status, $import_registry_output) = run_ssh_command($computer_node_name, $management_node_keys, $import_registry_command, '', '', 1);
-	if (defined($import_registry_exit_status) && $import_registry_exit_status == 0) {
-		notify($ERRORS{'OK'}, 0, "registry file contents imported from $temp_registry_file_path");
-	}
-	elsif ($import_registry_exit_status) {
-		notify($ERRORS{'WARNING'}, 0, "failed to import registry file contents from $temp_registry_file_path, exit status: $import_registry_exit_status, output:\n@{$import_registry_output}");
-		return;
+
+	# Delete the user's home directory
+	if ($self->delete_directory("C:/Users/$username")) {
+		notify($ERRORS{'OK'}, 0, "deleted profile for user $username from $computer_node_name");
 	}
 	else {
-		notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to import registry file contents from $temp_registry_file_path");
-		return;
+		notify($ERRORS{'WARNING'}, 0, "failed to delete profile for user $username from $computer_node_name");
+		return 0;
 	}
 	
 	return 1;
-}
+} ## end sub del_user
 
 #/////////////////////////////////////////////////////////////////////////////
 
-=head2 import_registry_string
+=head2 set_password
 
- Parameters  :
- Returns     :
- Description :
+ Parameters  : $username, $password
+ Returns     : 
+ Description : 
 
 =cut
 
-sub import_registry_string {
+sub set_password {
 	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");
@@ -920,52 +944,41 @@
 	my $management_node_keys     = $self->data->get_management_node_keys();
 	my $computer_node_name       = $self->data->get_computer_node_name();
 	
-	my $registry_string = shift;
-	if (!defined($registry_string) || !$registry_string) {
-		notify($ERRORS{'WARNING'}, 0, "registry file path was not passed correctly as an argument");
-		return;
-	}
-	
-	# Escape special characters with a backslash:
-	# \
-	# "
-	#notify($ERRORS{'DEBUG'}, 0, "registry string:\n$registry_string");
-	$registry_string =~ s/([\"])/\\$1/gs;
-	
-	# Replace regular newlines with Windows newlines
-	$registry_string =~ s/\n/\r\n/gs;
-	
-	# Specify where on the node the temporary registry file will reside
-	my $temp_registry_file_path = 'C:/Cygwin/tmp/vcl_import.reg';
+	# Attempt to get the username from the arguments
+	my $username = shift;
+	my $password = shift;
 	
-	# Echo the registry string to a file on the node	
-	my $echo_registry_command = "/usr/bin/echo.exe -E \"$registry_string\" > " . $temp_registry_file_path;
-	my ($echo_registry_exit_status, $echo_registry_output) = run_ssh_command($computer_node_name, $management_node_keys, $echo_registry_command, '', '', 1);
-	if (defined($echo_registry_exit_status) && $echo_registry_exit_status == 0) {
-		notify($ERRORS{'OK'}, 0, "registry string contents echoed to $temp_registry_file_path");
-	}
-	elsif ($echo_registry_exit_status) {
-		notify($ERRORS{'WARNING'}, 0, "failed to echo registry string contents to $temp_registry_file_path, exit status: $echo_registry_exit_status, output:\n@{$echo_registry_output}");
-		return;
+	# If no argument was supplied, use the user specified in the DataStructure
+	if (!defined($username) && !defined($password)) {
+		$username = $self->data->get_user_logon_id();
+		$password = $self->data->get_reservation_password();
 	}
-	else {
-		notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to echo registry string contents to $temp_registry_file_path");
-		return;
+	elsif (defined($username) && !defined($password)) {
+		notify($ERRORS{'WARNING'}, 0, "password set failed, username $username was passed as an argument but the password was not");
+		return 0;
 	}
 	
-	# Run reg.exe IMPORT
-	my $import_registry_command .= '"$SYSTEMROOT/System32/reg.exe" IMPORT ' . $temp_registry_file_path;
-	my ($import_registry_exit_status, $import_registry_output) = run_ssh_command($computer_node_name, $management_node_keys, $import_registry_command, '', '', 1);
-	if (defined($import_registry_exit_status) && $import_registry_exit_status == 0) {
-		notify($ERRORS{'OK'}, 0, "registry string contents imported from $temp_registry_file_path");
-	}
-	elsif ($import_registry_exit_status) {
-		notify($ERRORS{'WARNING'}, 0, "failed to import registry string contents from $temp_registry_file_path, exit status: $import_registry_exit_status, output:\n@{$import_registry_output}");
-		return;
+	# Attempt to set the password
+	notify($ERRORS{'OK'}, 0, "setting password of $username to $password on $computer_node_name");
+	my ($set_password_exit_status, $set_password_output) = run_ssh_command($computer_node_name, $management_node_keys, "net user $username '$password'");
+	if ($set_password_exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "$username password changed to $password on $computer_node_name");
 	}
 	else {
-		notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to import registry string contents from $temp_registry_file_path");
-		return;
+		notify($ERRORS{'WARNING'}, 0, "failed to set password of $username to $password on $computer_node_name, exit status: $set_password_exit_status, output:\n@{$set_password_output}");
+		return 0;
+	}
+	
+	# Check if root user, must set sshd service password too
+	if ($username eq 'root') {
+		notify($ERRORS{'OK'}, 0, "root account password changed, must also change sshd service credentials");
+		if ($self->set_service_credentials('sshd', $username, $password)) {
+			notify($ERRORS{'OK'}, 0, "sshd service credentials changed to $username ($password)");
+		}
+		else {
+			notify($ERRORS{'WARNING'}, 0, "failed to set sshd service credentials to $username ($password)");
+			return 0;
+		}
 	}
 	
 	return 1;
@@ -973,7 +986,7 @@
 
 #/////////////////////////////////////////////////////////////////////////////
 
-=head2 add_runonce_registry_value
+=head2 disable_pagefile
 
  Parameters  :
  Returns     :
@@ -981,7 +994,7 @@
 
 =cut
 
-sub add_runonce_registry_value {
+sub disable_pagefile {
 	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");
@@ -991,39 +1004,52 @@
 	my $management_node_keys     = $self->data->get_management_node_keys();
 	my $computer_node_name       = $self->data->get_computer_node_name();
 	
-	my $command_name = shift;
-	my $command = shift;
-	
-	# Escape backslashes, can never have enough...
-	$command =~ s/\\/\\\\/g;
-	
-	# Make sure arguments were supplied
-	if (!defined($command_name) && !defined($command)) {
-		notify($ERRORS{'WARNING'}, 0, "runonce registry key not added, arguments were not passed correctly");
-		return 0;
-	}
-
 	my $registry_string .= <<"EOF";
 Windows Registry Editor Version 5.00
 
-[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce]
-"$command_name"="$command"
+[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management]
+"PagingFiles"=""
 EOF
 	
 	# Import the string into the registry
 	if ($self->import_registry_string($registry_string)) {
-		notify($ERRORS{'OK'}, 0, "successfully added runonce registry value, name: $command_name, command: $command");
-		return 1;
+		notify($ERRORS{'OK'}, 0, "successfully set the registry key to disable the pagefile");
 	}
 	else {
-		notify($ERRORS{'WARNING'}, 0, "failed to add runonce registry value, name: $command_name, command: $command");
+		notify($ERRORS{'WARNING'}, 0, "failed to set the registry key to disable the pagefile");
 		return 0;
 	}
+
+	# Attempt to reboot the computer in order to delete the pagefile
+	if ($self->reboot()) {
+		notify($ERRORS{'OK'}, 0, "computer was rebooted after disabling pagefile in the registry");
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to reboot computer after disabling pagefile");
+		return;
+	}
+	
+	# Attempt to delete the pagefile
+	my $delete_pagefile_command = "attrib.exe -S -H -R C:/pagefile.sys";
+	$delete_pagefile_command .= " && /usr/bin/rm.exe -rfv C:/pagefile.sys";
+	my ($delete_pagefile_exit_status, $delete_pagefile_output) = run_ssh_command($computer_node_name, $management_node_keys, $delete_pagefile_command);
+	if (defined($delete_pagefile_exit_status) && $delete_pagefile_exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "pagefile.sys was deleted");
+		return 1;
+	}
+	elsif ($delete_pagefile_exit_status) {
+		notify($ERRORS{'WARNING'}, 0, "failed to delete pagefile.sys, exit status: $delete_pagefile_exit_status, output:\n@{$delete_pagefile_output}");
+		return;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to delete pagefile.sys");
+		return;
+	}
 }
 
 #/////////////////////////////////////////////////////////////////////////////
 
-=head2 create_startup_scheduled_task
+=head2 disable_pagefile
 
  Parameters  :
  Returns     :
@@ -1031,7 +1057,7 @@
 
 =cut
 
-sub create_startup_scheduled_task {
+sub enable_pagefile {
 	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");
@@ -1041,229 +1067,28 @@
 	my $management_node_keys     = $self->data->get_management_node_keys();
 	my $computer_node_name       = $self->data->get_computer_node_name();
 	
-	my $task_name = shift;
-	my $task_command = shift;
-	
-	# Escape backslashes, can never have enough...
-	$task_command =~ s/\\/\\\\/g;
-	
-	# Replace forward slashes with backslashes
-	$task_command =~ s/\//\\\\/g;
-	
-	# Make sure arguments were supplied
-	if (!defined($task_name) && !defined($task_command)) {
-		notify($ERRORS{'WARNING'}, 0, "startup scheduled task not added, arguments were not passed correctly");
-		return;
-	}
-	
-	#SCHTASKS /Create [/S system [/U username [/P [password]]]]
-	#    [/RU username [/RP password]] /SC schedule [/MO modifier] [/D day]
-	#    [/M months] [/I idletime] /TN taskname /TR taskrun [/ST starttime]
-	#    [/RI interval] [ {/ET endtime | /DU duration} [/K] [/XML xmlfile] [/V1]]
-	#    [/SD startdate] [/ED enddate] [/IT | /NP] [/Z] [/F]
-	#
-	#Description:
-	#    Enables an administrator to create scheduled tasks on a local or
-	#    remote system.
-	#
-	#Parameter List:
-	#    /S   system        Specifies the remote system to connect to. If omitted
-	#                       the system parameter defaults to the local system.
-	#
-	#    /U   username      Specifies the user context under which SchTasks.exe 
-	#                       should execute.
-	#
-	#    /P   [password]    Specifies the password for the given user context.
-	#                       Prompts for input if omitted.
-	#
-	#    /RU  username      Specifies the "run as" user account (user context)
-	#                       under which the task runs. For the system account,
-	#                       valid values are "", "NT AUTHORITY\SYSTEM"
-	#                       or "SYSTEM".
-	#                       For v2 tasks, "NT AUTHORITY\LOCALSERVICE" and 
-	#                       "NT AUTHORITY\NETWORKSERVICE" are also available as well 
-	#                       as the well known SIDs for all three. 
-	#
-	#    /RP  [password]    Specifies the password for the "run as" user. 
-	#                       To prompt for the password, the value must be either
-	#                       "*" or none. This password is ignored for the 
-	#                       system account. Must be combined with either /RU or
-	#                       /XML switch.
-	#
-	#    /SC   schedule     Specifies the schedule frequency.
-	#                       Valid schedule types: MINUTE, HOURLY, DAILY, WEEKLY, 
-	#                       MONTHLY, ONCE, ONSTART, ONLOGON, ONIDLE, ONEVENT.
-	#
-	#    /MO   modifier     Refines the schedule type to allow finer control over
-	#                       schedule recurrence. Valid values are listed in the 
-	#                       "Modifiers" section below.
-	#
-	#    /D    days         Specifies the day of the week to run the task. Valid 
-	#                       values: MON, TUE, WED, THU, FRI, SAT, SUN and for
-	#                       MONTHLY schedules 1 - 31 (days of the month). 
-	#                       Wildcard "*" specifies all days.
-	#
-	#    /M    months       Specifies month(s) of the year. Defaults to the first 
-	#                       day of the month. Valid values: JAN, FEB, MAR, APR, 
-	#                       MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC. Wildcard "*" 
-	#                       specifies all months.
-	#
-	#    /I    idletime     Specifies the amount of idle time to wait before 
-	#                       running a scheduled ONIDLE task.
-	#                       Valid range: 1 - 999 minutes.
-	#
-	#    /TN   taskname     Specifies a name which uniquely
-	#                       identifies this scheduled task.
-	#
-	#    /TR   taskrun      Specifies the path and file name of the program to be 
-	#                       run at the scheduled time.
-	#                       Example: C:\windows\system32\calc.exe
-	#
-	#    /ST   starttime    Specifies the start time to run the task. The time 
-	#                       format is HH:mm (24 hour time) for example, 14:30 for 
-	#                       2:30 PM. Defaults to current time if /ST is not 
-	#                       specified.  This option is required with /SC ONCE.
-	#
-	#    /RI   interval     Specifies the repetition interval in minutes. This is 
-	#                       not applicable for schedule types: MINUTE, HOURLY,
-	#                       ONSTART, ONLOGON, ONIDLE, ONEVENT.
-	#                       Valid range: 1 - 599940 minutes.
-	#                       If either /ET or /DU is specified, then it defaults to 
-	#                       10 minutes.
-	#
-	#    /ET   endtime      Specifies the end time to run the task. The time format
-	#                       is HH:mm (24 hour time) for example, 14:50 for 2:50 PM.
-	#                       This is not applicable for schedule types: ONSTART, 
-	#                       ONLOGON, ONIDLE, ONEVENT.
-	#
-	#    /DU   duration     Specifies the duration to run the task. The time 
-	#                       format is HH:mm. This is not applicable with /ET and
-	#                       for schedule types: ONSTART, ONLOGON, ONIDLE, ONEVENT.
-	#                       For /V1 tasks, if /RI is specified, duration defaults 
-	#                       to 1 hour.
-	#
-	#    /K                 Terminates the task at the endtime or duration time. 
-	#                       This is not applicable for schedule types: ONSTART, 
-	#                       ONLOGON, ONIDLE, ONEVENT. Either /ET or /DU must be
-	#                       specified.
-	#
-	#    /SD   startdate    Specifies the first date on which the task runs. The 
-	#                       format is mm/dd/yyyy. Defaults to the current 
-	#                       date. This is not applicable for schedule types: ONCE, 
-	#                       ONSTART, ONLOGON, ONIDLE, ONEVENT.
-	#
-	#    /ED   enddate      Specifies the last date when the task should run. The 
-	#                       format is mm/dd/yyyy. This is not applicable for 
-	#                       schedule types: ONCE, ONSTART, ONLOGON, ONIDLE, ONEVENT.
-	#
-	#    /EC   ChannelName  Specifies the event channel for OnEvent triggers.
-	#
-	#    /IT                Enables the task to run interactively only if the /RU 
-	#                       user is currently logged on at the time the job runs.
-	#                       This task runs only if the user is logged in.
-	#
-	#    /NP                No password is stored.  The task runs non-interactively
-	#                       as the given user.  Only local resources are available.
-	#
-	#    /Z                 Marks the task for deletion after its final run.
-	#
-	#    /XML  xmlfile      Creates a task from the task XML specified in a file.
-	#                       Can be combined with /RU and /RP switches, or with /RP 
-	#                       alone, when task XML already contains the principal.
-	#
-	#    /V1                Creates a task visible to pre-Vista platforms.
-	#                       Not compatible with /XML.
-	#
-	#    /F                 Forcefully creates the task and suppresses warnings if 
-	#                       the specified task already exists.
-	#
-	#    /RL   level        Sets the Run Level for the job. Valid values are 
-	#                       LIMITED and HIGHEST. The default is LIMITED.
-	#
-	#    /DELAY delaytime   Specifies the wait time to delay the running of the 
-	#                       task after the trigger is fired.  The time format is
-	#                       mmmm:ss.  This option is only valid for schedule types
-	#                       ONSTART, ONLOGON, ONEVENT.
-	#
-	#    /?                 Displays this help message.
-	#
-	#Modifiers: Valid values for the /MO switch per schedule type:
-	#    MINUTE:  1 - 1439 minutes.
-	#    HOURLY:  1 - 23 hours.
-	#    DAILY:   1 - 365 days.
-	#    WEEKLY:  weeks 1 - 52.
-	#    ONCE:    No modifiers.
-	#    ONSTART: No modifiers.
-	#    ONLOGON: No modifiers.
-	#    ONIDLE:  No modifiers.
-	#    MONTHLY: 1 - 12, or 
-	#             FIRST, SECOND, THIRD, FOURTH, LAST, LASTDAY.
-	#
-	#    ONEVENT:  XPath event query string.
-
-	# Run schtasks.exe to add the task
-	my $create_task_command = 'schtasks.exe /Create /RU SYSTEM /SC ONSTART /TN "' . $task_name . '" /TR "' . $task_command . '" /F';
-	my ($create_task_exit_status, $create_task_output) = run_ssh_command($computer_node_name, $management_node_keys, $create_task_command);
-	if (defined($create_task_exit_status)  && $create_task_exit_status == 0) {
-		notify($ERRORS{'OK'}, 0, "successfully created scheduled task on $computer_node_name");
-	}
-	elsif (defined($create_task_exit_status)) {
-		notify($ERRORS{'WARNING'}, 0, "failed to create scheduled task on $computer_node_name, exit status: $create_task_exit_status, output:\n@{$create_task_output}");
-		return 0;
-	}
-	else {
-		notify($ERRORS{'WARNING'}, 0, "failed to execute ssh command created scheduled task on $computer_node_name");
-		return;
-	}
-	
-	return 1;
-}
-
-#/////////////////////////////////////////////////////////////////////////////
-
-=head2 enable_autoadminlogon
-
- Parameters  :
- Returns     :
- Description :
-
-=cut
-
-sub enable_autoadminlogon {
-	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 $management_node_keys     = $self->data->get_management_node_keys();
-	my $computer_node_name       = $self->data->get_computer_node_name();
-
-	my $registry_string .= <<"EOF";
+	my $registry_string .= <<'EOF';
 Windows Registry Editor Version 5.00
 
-; This file enables autoadminlogon for the root account
-
-[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon]
-"AutoAdminLogon"="1"
-"DefaultUserName"="root"
-"DefaultPassword"= "$ROOT_PASSWORD"
+[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management]
+"PagingFiles"="?:\\\\pagefile.sys"
 EOF
 	
 	# Import the string into the registry
 	if ($self->import_registry_string($registry_string)) {
-		notify($ERRORS{'OK'}, 0, "successfully enabled autoadminlogon");
-		return 1;
+		notify($ERRORS{'OK'}, 0, "successfully set the registry key to enable the pagefile");
 	}
 	else {
-		notify($ERRORS{'WARNING'}, 0, "failed to enable autoadminlogon");
+		notify($ERRORS{'WARNING'}, 0, "failed to set the registry key to enable the pagefile");
 		return 0;
 	}
+	
+	return 1;
 }
 
 #/////////////////////////////////////////////////////////////////////////////
 
-=head2 disable_autoadminlogon
+=head2 disable_ipv6
 
  Parameters  :
  Returns     :
@@ -1271,7 +1096,7 @@
 
 =cut
 
-sub disable_autoadminlogon {
+sub disable_ipv6 {
 	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");
@@ -1280,40 +1105,40 @@
 	
 	my $management_node_keys     = $self->data->get_management_node_keys();
 	my $computer_node_name       = $self->data->get_computer_node_name();
-
-	my $registry_string .= <<EOF;
+	
+	my $registry_string .= <<'EOF';
 Windows Registry Editor Version 5.00
 
-; This file disables autoadminlogon for the root account
+; This registry file contains the entries to disable all IPv6 components 
+; http://support.microsoft.com/kb/929852
 
-[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon]
-"AutoAdminLogon"="0"
-"DefaultUserName"=""
-"DefaultPassword"= ""
+[HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\Tcpip6\\Parameters]
+"DisabledComponents"=dword:ffffffff
 EOF
 	
 	# Import the string into the registry
 	if ($self->import_registry_string($registry_string)) {
-		notify($ERRORS{'OK'}, 0, "successfully disabled autoadminlogon");
-		return 1;
+		notify($ERRORS{'OK'}, 0, "successfully set the registry keys to disable IPv6");
 	}
 	else {
-		notify($ERRORS{'WARNING'}, 0, "failed to disable autoadminlogon");
+		notify($ERRORS{'WARNING'}, 0, "failed to set the registry keys to disable IPv6");
 		return 0;
 	}
+	
+	return 1;
 }
 
 #/////////////////////////////////////////////////////////////////////////////
 
-=head2 set_network_location
+=head2 import_registry_file
 
  Parameters  :
  Returns     :
- Description : 
+ Description :
 
 =cut
 
-sub set_network_location {
+sub import_registry_file {
 	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");
@@ -1323,29 +1148,55 @@
 	my $management_node_keys     = $self->data->get_management_node_keys();
 	my $computer_node_name       = $self->data->get_computer_node_name();
 	
-	#Category key: Home/Work=00000000, Public=00000001
+	my $registry_file_path = shift;
+	if (!defined($registry_file_path) || !$registry_file_path) {
+		notify($ERRORS{'WARNING'}, 0, "registry file path was not passed correctly as an argument");
+		return;
+	}
 	
-	my $registry_string .= <<"EOF";
-Windows Registry Editor Version 5.00
-
-[HKEY_LOCAL_MACHINE\\SOFTWARE\\Policies\\Microsoft\\Windows NT\\CurrentVersion\\NetworkList\\Signatures\\FirstNetwork]
-"Category"=dword:00000001
-EOF
+	my $registry_file_contents = `cat $registry_file_path`;
+	notify($ERRORS{'DEBUG'}, 0, "registry file contents:\n$registry_file_contents");
+	$registry_file_contents =~ s/([\"])/\\$1/gs;
 	
-	# Import the string into the registry
-	if ($self->import_registry_string($registry_string)) {
-		notify($ERRORS{'OK'}, 0, "successfully set network location");
-		return 1;
+	# Specify where on the node the temporary registry file will reside
+	my $temp_registry_file_path = 'C:/Cygwin/tmp/vcl_import.reg';
+	
+	# Echo the registry string to a file on the node	
+	my $echo_registry_command = "/usr/bin/echo.exe -E \"$registry_file_contents\" > " . $temp_registry_file_path;
+	my ($echo_registry_exit_status, $echo_registry_output) = run_ssh_command($computer_node_name, $management_node_keys, $echo_registry_command, '', '', 1);
+	if (defined($echo_registry_exit_status) && $echo_registry_exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "registry file contents echoed to $temp_registry_file_path");
+	}
+	elsif ($echo_registry_exit_status) {
+		notify($ERRORS{'WARNING'}, 0, "failed to echo registry file contents to $temp_registry_file_path, exit status: $echo_registry_exit_status, output:\n@{$echo_registry_output}");
+		return;
 	}
 	else {
-		notify($ERRORS{'WARNING'}, 0, "failed to set network location");
-		return 0;
+		notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to echo registry file contents to $temp_registry_file_path");
+		return;
+	}
+	
+	# Run reg.exe IMPORT
+	my $import_registry_command .= '"$SYSTEMROOT/System32/reg.exe" IMPORT ' . $temp_registry_file_path;
+	my ($import_registry_exit_status, $import_registry_output) = run_ssh_command($computer_node_name, $management_node_keys, $import_registry_command, '', '', 1);
+	if (defined($import_registry_exit_status) && $import_registry_exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "registry file contents imported from $temp_registry_file_path");
+	}
+	elsif ($import_registry_exit_status) {
+		notify($ERRORS{'WARNING'}, 0, "failed to import registry file contents from $temp_registry_file_path, exit status: $import_registry_exit_status, output:\n@{$import_registry_output}");
+		return;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to import registry file contents from $temp_registry_file_path");
+		return;
 	}
+	
+	return 1;
 }
 
 #/////////////////////////////////////////////////////////////////////////////
 
-=head2 set_kms_licensing
+=head2 import_registry_string
 
  Parameters  :
  Returns     :
@@ -1353,7 +1204,7 @@
 
 =cut
 
-sub set_kms_licensing {
+sub import_registry_string {
 	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");
@@ -1363,77 +1214,60 @@
 	my $management_node_keys     = $self->data->get_management_node_keys();
 	my $computer_node_name       = $self->data->get_computer_node_name();
 	
-	my $kms_server = shift;
-	my $kms_port = shift;
-	
-	# Make sure the KMS server address was passed as an argument
-	if (!defined($kms_server)) {
-		notify($ERRORS{'WARNING'}, 0, "failed to set kms server, server address was not passed correctly as an argument");
-		return 0;
+	my $registry_string = shift;
+	if (!defined($registry_string) || !$registry_string) {
+		notify($ERRORS{'WARNING'}, 0, "registry file path was not passed correctly as an argument");
+		return;
 	}
 	
-	# Set the default KMS port if it wasn't specified.
-	$kms_port = 1688 if !$kms_port;
+	# Escape special characters with a backslash:
+	# \
+	# "
+	#notify($ERRORS{'DEBUG'}, 0, "registry string:\n$registry_string");
+	$registry_string =~ s/([\"])/\\$1/gs;
 	
-	# Run slmgr.vbs -skms
-	my $kms_command = '$SYSTEMROOT/System32/cscript.exe $SYSTEMROOT/System32/slmgr.vbs -skms ' . "$kms_server:$kms_port";
-	my ($kms_exit_status, $kms_output) = run_ssh_command($computer_node_name, $management_node_keys, $kms_command);
-	if (defined($kms_exit_status)  && $kms_exit_status == 0) {
-		notify($ERRORS{'OK'}, 0, "successfully set kms server to $kms_server:$kms_port on $computer_node_name");
-	}
-	elsif (defined($kms_exit_status)) {
-		notify($ERRORS{'WARNING'}, 0, "failed to set kms server to $kms_server:$kms_port on $computer_node_name, exit status: $kms_exit_status, output:\n@{$kms_output}");
-		return 0;
+	# Replace regular newlines with Windows newlines
+	$registry_string =~ s/\n/\r\n/gs;
+	
+	# Specify where on the node the temporary registry file will reside
+	my $temp_registry_file_path = 'C:/Cygwin/tmp/vcl_import.reg';
+	
+	# Echo the registry string to a file on the node	
+	my $echo_registry_command = "/usr/bin/echo.exe -E \"$registry_string\" > " . $temp_registry_file_path;
+	my ($echo_registry_exit_status, $echo_registry_output) = run_ssh_command($computer_node_name, $management_node_keys, $echo_registry_command, '', '', 1);
+	if (defined($echo_registry_exit_status) && $echo_registry_exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "registry string contents echoed to $temp_registry_file_path");
 	}
-	else {
-		notify($ERRORS{'WARNING'}, 0, "failed to execute ssh command to set kms server to $kms_server:$kms_port on $computer_node_name");
+	elsif ($echo_registry_exit_status) {
+		notify($ERRORS{'WARNING'}, 0, "failed to echo registry string contents to $temp_registry_file_path, exit status: $echo_registry_exit_status, output:\n@{$echo_registry_output}");
 		return;
 	}
-
-	return 1;
-}
-
-#/////////////////////////////////////////////////////////////////////////////
-
-=head2 activate_licensing
-
- Parameters  :
- Returns     :
- Description :
-
-=cut
-
-sub activate_licensing {
-	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");
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to echo registry string contents to $temp_registry_file_path");
 		return;
 	}
 	
-	my $management_node_keys     = $self->data->get_management_node_keys();
-	my $computer_node_name       = $self->data->get_computer_node_name();
-	
-	# Run slmgr.vbs -ato
-	my $activate_command = '$SYSTEMROOT/System32/cscript.exe $SYSTEMROOT/System32/slmgr.vbs -ato';
-	my ($activate_exit_status, $activate_output) = run_ssh_command($computer_node_name, $management_node_keys, $activate_command);
-	if (defined($activate_exit_status)  && $activate_exit_status == 0) {
-		notify($ERRORS{'OK'}, 0, "successfully activated licensing on $computer_node_name");
+	# Run reg.exe IMPORT
+	my $import_registry_command .= '"$SYSTEMROOT/System32/reg.exe" IMPORT ' . $temp_registry_file_path;
+	my ($import_registry_exit_status, $import_registry_output) = run_ssh_command($computer_node_name, $management_node_keys, $import_registry_command, '', '', 1);
+	if (defined($import_registry_exit_status) && $import_registry_exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "registry string contents imported from $temp_registry_file_path");
 	}
-	elsif (defined($activate_exit_status)) {
-		notify($ERRORS{'WARNING'}, 0, "failed to activated licensing on $computer_node_name, exit status: $activate_exit_status, output:\n@{$activate_output}");
-		return 0;
+	elsif ($import_registry_exit_status) {
+		notify($ERRORS{'WARNING'}, 0, "failed to import registry string contents from $temp_registry_file_path, exit status: $import_registry_exit_status, output:\n@{$import_registry_output}");
+		return;
 	}
 	else {
-		notify($ERRORS{'WARNING'}, 0, "failed to activated licensing on $computer_node_name");
+		notify($ERRORS{'WARNING'}, 0, "failed to run SSH command to import registry string contents from $temp_registry_file_path");
 		return;
 	}
-
+	
 	return 1;
 }
 
 #/////////////////////////////////////////////////////////////////////////////
 
-=head2 create_eventlog_entry
+=head2 add_runonce_registry_value
 
  Parameters  :
  Returns     :
@@ -1441,7 +1275,7 @@
 
 =cut
 
-sub create_eventlog_entry {
+sub add_runonce_registry_value {
 	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");
@@ -1451,43 +1285,47 @@
 	my $management_node_keys     = $self->data->get_management_node_keys();
 	my $computer_node_name       = $self->data->get_computer_node_name();
 	
-	my $message = shift;
+	my $command_name = shift;
+	my $command = shift;
 	
-	# Make sure the message was passed as an argument
-	if (!defined($message)) {
-		notify($ERRORS{'WARNING'}, 0, "failed to create eventlog entry, message was passed as an argument");
+	# Escape backslashes, can never have enough...
+	$command =~ s/\\/\\\\/g;
+	
+	# Make sure arguments were supplied
+	if (!defined($command_name) && !defined($command)) {
+		notify($ERRORS{'WARNING'}, 0, "runonce registry key not added, arguments were not passed correctly");
 		return 0;
 	}
+
+	my $registry_string .= <<"EOF";
+Windows Registry Editor Version 5.00
+
+[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce]
+"$command_name"="$command"
+EOF
 	
-	# Run eventcreate.exe to create an event log entry
-	my $eventcreate_command = '$SYSTEMROOT/System32/eventcreate.exe /T INFORMATION /L APPLICATION /SO VCL /ID 555 /D "' . $message . '"';
-	my ($eventcreate_exit_status, $eventcreate_output) = run_ssh_command($computer_node_name, $management_node_keys, $eventcreate_command);
-	if (defined($eventcreate_exit_status)  && $eventcreate_exit_status == 0) {
-		notify($ERRORS{'OK'}, 0, "successfully created event log entry on $computer_node_name: $message");
-	}
-	elsif (defined($eventcreate_exit_status)) {
-		notify($ERRORS{'WARNING'}, 0, "failed to create event log entry on $computer_node_name: $message, exit status: $eventcreate_exit_status, output:\n@{$eventcreate_output}");
-		return 0;
+	# Import the string into the registry
+	if ($self->import_registry_string($registry_string)) {
+		notify($ERRORS{'OK'}, 0, "successfully added runonce registry value, name: $command_name, command: $command");
+		return 1;
 	}
 	else {
-		notify($ERRORS{'WARNING'}, 0, "failed to run ssh command to create event log entry on $computer_node_name: $message");
-		return;
+		notify($ERRORS{'WARNING'}, 0, "failed to add runonce registry value, name: $command_name, command: $command");
+		return 0;
 	}
-	
-	return 1;
 }
 
 #/////////////////////////////////////////////////////////////////////////////
 
-=head2 reboot
+=head2 create_startup_scheduled_task
 
- Parameters  : $wait_for_reboot
- Returns     : 
- Description : 
+ Parameters  :
+ Returns     :
+ Description :
 
 =cut
 
-sub reboot {
+sub create_startup_scheduled_task {
 	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");
@@ -1497,246 +1335,1207 @@
 	my $management_node_keys     = $self->data->get_management_node_keys();
 	my $computer_node_name       = $self->data->get_computer_node_name();
 	
-	# Check if an argument was supplied
-	my $wait_for_reboot = shift;
-	if (!defined($wait_for_reboot) || $wait_for_reboot !~ /0/) {
-		notify($ERRORS{'DEBUG'}, 0, "rebooting $computer_node_name and waiting for ssh to become active");
-		$wait_for_reboot = 1;
-	}
-	else {
-		notify($ERRORS{'DEBUG'}, 0, "rebooting $computer_node_name and NOT waiting");
-		$wait_for_reboot = 0;
-	}
-	
-	my $reboot_start_time = time();
-	notify($ERRORS{'DEBUG'}, 0, "reboot will be attempted on $computer_node_name");
+	my $task_name = shift;
+	my $task_command = shift;
 	
-	# Make sure SSH access is enabled from private IP addresses
-	if (!$self->firewall_enable_ssh_private()) {
-		notify($ERRORS{'WARNING'}, 0, "reboot not attempted, failed to enable ssh from private IP addresses");
-		return 0;
-	}
+	# Escape backslashes, can never have enough...
+	$task_command =~ s/\\/\\\\/g;
 	
-	# Make sure ping access is enabled from private IP addresses
-	if (!$self->firewall_enable_ping_private()) {
-		notify($ERRORS{'WARNING'}, 0, "reboot not attempted, failed to enable ping from private IP addresses");
-		return 0;
-	}
+	# Replace forward slashes with backslashes
+	$task_command =~ s/\//\\\\/g;
 	
-	# Initiate the shutdown.exe command to reboot the computer
-	my $shutdown_command = "C:/Windows/system32/shutdown.exe -r -t 0 -f";
-	my ($shutdown_exit_status, $shutdown_output) = run_ssh_command($computer_node_name, $management_node_keys, $shutdown_command);
-	if (defined($shutdown_exit_status)  && $shutdown_exit_status == 0) {
-		notify($ERRORS{'OK'}, 0, "successfully executed reboot command on $computer_node_name");
-	}
-	elsif (defined($shutdown_exit_status)) {
-		notify($ERRORS{'WARNING'}, 0, "failed to execute reboot command on $computer_node_name, exit status: $shutdown_exit_status, output:\n@{$shutdown_output}");
-		return 0;
-	}
-	else {
-		notify($ERRORS{'WARNING'}, 0, "failed to execute ssh command to reboot $computer_node_name");
+	# Make sure arguments were supplied
+	if (!defined($task_name) && !defined($task_command)) {
+		notify($ERRORS{'WARNING'}, 0, "startup scheduled task not added, arguments were not passed correctly");
 		return;
 	}
 	
-	# Check if wait for reboot is set
-	if (!$wait_for_reboot) {
-		return 1;
-	}
-	
-	# Wait for reboot is true
-	notify($ERRORS{'OK'}, 0, "sleeping for 30 seconds while $computer_node_name begins reboot");
-	sleep 30;
-	
-	# Wait maximum of 4 minutes for the computer to go offline then come back up
-	if (!$self->wait_for_ping(4)) {
-		# Check if the computer was ever offline, it should have been or else reboot never happened
-		notify($ERRORS{'WARNING'}, 0, "$computer_node_name never responded to ping, attempting hard power reset");
-		
-		# Just explicitly call xCAT's _rpower for now
-		# TODO: implement public reset() subroutines in all of the provisioning modules
-		# TODO: allow provisioning and OS modules access to each other's subroutines
-		if (VCL::Module::Provisioning::xCAT::_rpower($computer_node_name, "cycle")) {
-			notify($ERRORS{'OK'}, 0, "initiated hard power reset on $computer_node_name");
-		}
-		else {
-			notify($ERRORS{'WARNING'}, 0, "reboot failed, failed to initiate hard power reset on $computer_node_name");
-			return 0;
-		}
-		
-		# Wait for computer to respond to ping after initiating hard reset
-		# Wait longer than the first attempt
-		if (!$self->wait_for_ping(6)) {
+	#SCHTASKS /Create [/S system [/U username [/P [password]]]]
+	#    [/RU username [/RP password]] /SC schedule [/MO modifier] [/D day]
+	#    [/M months] [/I idletime] /TN taskname /TR taskrun [/ST starttime]
+	#    [/RI interval] [ {/ET endtime | /DU duration} [/K] [/XML xmlfile] [/V1]]
+	#    [/SD startdate] [/ED enddate] [/IT | /NP] [/Z] [/F]
+	#
+	#Description:
+	#    Enables an administrator to create scheduled tasks on a local or
+	#    remote system.
+	#
+	#Parameter List:
+	#    /S   system        Specifies the remote system to connect to. If omitted
+	#                       the system parameter defaults to the local system.
+	#
+	#    /U   username      Specifies the user context under which SchTasks.exe 
+	#                       should execute.
+	#
+	#    /P   [password]    Specifies the password for the given user context.
+	#                       Prompts for input if omitted.
+	#
+	#    /RU  username      Specifies the "run as" user account (user context)
+	#                       under which the task runs. For the system account,
+	#                       valid values are "", "NT AUTHORITY\SYSTEM"
+	#                       or "SYSTEM".
+	#                       For v2 tasks, "NT AUTHORITY\LOCALSERVICE" and 
+	#                       "NT AUTHORITY\NETWORKSERVICE" are also available as well 
+	#                       as the well known SIDs for all three. 
+	#
+	#    /RP  [password]    Specifies the password for the "run as" user. 
+	#                       To prompt for the password, the value must be either
+	#                       "*" or none. This password is ignored for the 
+	#                       system account. Must be combined with either /RU or
+	#                       /XML switch.
+	#
+	#    /SC   schedule     Specifies the schedule frequency.
+	#                       Valid schedule types: MINUTE, HOURLY, DAILY, WEEKLY, 
+	#                       MONTHLY, ONCE, ONSTART, ONLOGON, ONIDLE, ONEVENT.
+	#
+	#    /MO   modifier     Refines the schedule type to allow finer control over
+	#                       schedule recurrence. Valid values are listed in the 
+	#                       "Modifiers" section below.
+	#
+	#    /D    days         Specifies the day of the week to run the task. Valid 
+	#                       values: MON, TUE, WED, THU, FRI, SAT, SUN and for
+	#                       MONTHLY schedules 1 - 31 (days of the month). 
+	#                       Wildcard "*" specifies all days.
+	#
+	#    /M    months       Specifies month(s) of the year. Defaults to the first 
+	#                       day of the month. Valid values: JAN, FEB, MAR, APR, 
+	#                       MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC. Wildcard "*" 
+	#                       specifies all months.
+	#
+	#    /I    idletime     Specifies the amount of idle time to wait before 
+	#                       running a scheduled ONIDLE task.
+	#                       Valid range: 1 - 999 minutes.
+	#
+	#    /TN   taskname     Specifies a name which uniquely
+	#                       identifies this scheduled task.
+	#
+	#    /TR   taskrun      Specifies the path and file name of the program to be 
+	#                       run at the scheduled time.
+	#                       Example: C:\windows\system32\calc.exe
+	#
+	#    /ST   starttime    Specifies the start time to run the task. The time 
+	#                       format is HH:mm (24 hour time) for example, 14:30 for 
+	#                       2:30 PM. Defaults to current time if /ST is not 
+	#                       specified.  This option is required with /SC ONCE.
+	#
+	#    /RI   interval     Specifies the repetition interval in minutes. This is 
+	#                       not applicable for schedule types: MINUTE, HOURLY,
+	#                       ONSTART, ONLOGON, ONIDLE, ONEVENT.
+	#                       Valid range: 1 - 599940 minutes.
+	#                       If either /ET or /DU is specified, then it defaults to 
+	#                       10 minutes.
+	#
+	#    /ET   endtime      Specifies the end time to run the task. The time format
+	#                       is HH:mm (24 hour time) for example, 14:50 for 2:50 PM.
+	#                       This is not applicable for schedule types: ONSTART, 
+	#                       ONLOGON, ONIDLE, ONEVENT.
+	#
+	#    /DU   duration     Specifies the duration to run the task. The time 
+	#                       format is HH:mm. This is not applicable with /ET and
+	#                       for schedule types: ONSTART, ONLOGON, ONIDLE, ONEVENT.
+	#                       For /V1 tasks, if /RI is specified, duration defaults 
+	#                       to 1 hour.
+	#
+	#    /K                 Terminates the task at the endtime or duration time. 
+	#                       This is not applicable for schedule types: ONSTART, 
+	#                       ONLOGON, ONIDLE, ONEVENT. Either /ET or /DU must be
+	#                       specified.
+	#
+	#    /SD   startdate    Specifies the first date on which the task runs. The 
+	#                       format is mm/dd/yyyy. Defaults to the current 
+	#                       date. This is not applicable for schedule types: ONCE, 
+	#                       ONSTART, ONLOGON, ONIDLE, ONEVENT.
+	#
+	#    /ED   enddate      Specifies the last date when the task should run. The 
+	#                       format is mm/dd/yyyy. This is not applicable for 
+	#                       schedule types: ONCE, ONSTART, ONLOGON, ONIDLE, ONEVENT.
+	#
+	#    /EC   ChannelName  Specifies the event channel for OnEvent triggers.
+	#
+	#    /IT                Enables the task to run interactively only if the /RU 
+	#                       user is currently logged on at the time the job runs.
+	#                       This task runs only if the user is logged in.
+	#
+	#    /NP                No password is stored.  The task runs non-interactively
+	#                       as the given user.  Only local resources are available.
+	#
+	#    /Z                 Marks the task for deletion after its final run.
+	#
+	#    /XML  xmlfile      Creates a task from the task XML specified in a file.
+	#                       Can be combined with /RU and /RP switches, or with /RP 
+	#                       alone, when task XML already contains the principal.
+	#
+	#    /V1                Creates a task visible to pre-Vista platforms.
+	#                       Not compatible with /XML.
+	#
+	#    /F                 Forcefully creates the task and suppresses warnings if 
+	#                       the specified task already exists.
+	#
+	#    /RL   level        Sets the Run Level for the job. Valid values are 
+	#                       LIMITED and HIGHEST. The default is LIMITED.
+	#
+	#    /DELAY delaytime   Specifies the wait time to delay the running of the 
+	#                       task after the trigger is fired.  The time format is
+	#                       mmmm:ss.  This option is only valid for schedule types
+	#                       ONSTART, ONLOGON, ONEVENT.
+	#
+	#    /?                 Displays this help message.
+	#
+	#Modifiers: Valid values for the /MO switch per schedule type:
+	#    MINUTE:  1 - 1439 minutes.
+	#    HOURLY:  1 - 23 hours.
+	#    DAILY:   1 - 365 days.
+	#    WEEKLY:  weeks 1 - 52.
+	#    ONCE:    No modifiers.
+	#    ONSTART: No modifiers.
+	#    ONLOGON: No modifiers.
+	#    ONIDLE:  No modifiers.
+	#    MONTHLY: 1 - 12, or 
+	#             FIRST, SECOND, THIRD, FOURTH, LAST, LASTDAY.
+	#
+	#    ONEVENT:  XPath event query string.
+
+	# Run schtasks.exe to add the task
+	my $create_task_command = 'schtasks.exe /Create /RU SYSTEM /SC ONSTART /TN "' . $task_name . '" /TR "' . $task_command . '" /F';
+	my ($create_task_exit_status, $create_task_output) = run_ssh_command($computer_node_name, $management_node_keys, $create_task_command);
+	if (defined($create_task_exit_status)  && $create_task_exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "successfully created scheduled task on $computer_node_name");
+	}
+	elsif (defined($create_task_exit_status)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to create scheduled task on $computer_node_name, exit status: $create_task_exit_status, output:\n@{$create_task_output}");
+		return 0;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to execute ssh command created scheduled task on $computer_node_name");
+		return;
+	}
+	
+	return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 enable_autoadminlogon
+
+ Parameters  :
+ Returns     :
+ Description :
+
+=cut
+
+sub enable_autoadminlogon {
+	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 $management_node_keys     = $self->data->get_management_node_keys();
+	my $computer_node_name       = $self->data->get_computer_node_name();
+
+	my $registry_string .= <<"EOF";
+Windows Registry Editor Version 5.00
+
+; This file enables autoadminlogon for the root account
+
+[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon]
+"AutoAdminLogon"="1"
+"DefaultUserName"="root"
+"DefaultPassword"= "$ROOT_PASSWORD"
+EOF
+	
+	# Import the string into the registry
+	if ($self->import_registry_string($registry_string)) {
+		notify($ERRORS{'OK'}, 0, "successfully enabled autoadminlogon");
+		return 1;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to enable autoadminlogon");
+		return 0;
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 disable_autoadminlogon
+
+ Parameters  :
+ Returns     :
+ Description :
+
+=cut
+
+sub disable_autoadminlogon {
+	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 $management_node_keys     = $self->data->get_management_node_keys();
+	my $computer_node_name       = $self->data->get_computer_node_name();
+
+	my $registry_string .= <<EOF;
+Windows Registry Editor Version 5.00
+
+; This file disables autoadminlogon for the root account
+
+[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon]
+"AutoAdminLogon"="0"
+"DefaultUserName"=""
+"DefaultPassword"= ""
+EOF
+	
+	# Import the string into the registry
+	if ($self->import_registry_string($registry_string)) {
+		notify($ERRORS{'OK'}, 0, "successfully disabled autoadminlogon");
+		return 1;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to disable autoadminlogon");
+		return 0;
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 set_network_location
+
+ Parameters  :
+ Returns     :
+ Description : 
+
+=cut
+
+sub set_network_location {
+	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 $management_node_keys     = $self->data->get_management_node_keys();
+	my $computer_node_name       = $self->data->get_computer_node_name();
+	
+	#Category key: Home/Work=00000000, Public=00000001
+	
+	my $registry_string .= <<"EOF";
+Windows Registry Editor Version 5.00
+
+[HKEY_LOCAL_MACHINE\\SOFTWARE\\Policies\\Microsoft\\Windows NT\\CurrentVersion\\NetworkList\\Signatures\\FirstNetwork]
+"Category"=dword:00000001
+EOF
+	
+	# Import the string into the registry
+	if ($self->import_registry_string($registry_string)) {
+		notify($ERRORS{'OK'}, 0, "successfully set network location");
+		return 1;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to set network location");
+		return 0;
+	}
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 set_kms_licensing
+
+ Parameters  :
+ Returns     :
+ Description :
+
+=cut
+
+sub set_kms_licensing {
+	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 $management_node_keys     = $self->data->get_management_node_keys();
+	my $computer_node_name       = $self->data->get_computer_node_name();
+	
+	my $kms_server = shift;
+	my $kms_port = shift;
+	
+	# Make sure the KMS server address was passed as an argument
+	if (!defined($kms_server)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to set kms server, server address was not passed correctly as an argument");
+		return 0;
+	}
+	
+	# Set the default KMS port if it wasn't specified.
+	$kms_port = 1688 if !$kms_port;
+	
+	# Run slmgr.vbs -skms
+	my $kms_command = '$SYSTEMROOT/System32/cscript.exe $SYSTEMROOT/System32/slmgr.vbs -skms ' . "$kms_server:$kms_port";
+	my ($kms_exit_status, $kms_output) = run_ssh_command($computer_node_name, $management_node_keys, $kms_command);
+	if (defined($kms_exit_status)  && $kms_exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "successfully set kms server to $kms_server:$kms_port on $computer_node_name");
+	}
+	elsif (defined($kms_exit_status)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to set kms server to $kms_server:$kms_port on $computer_node_name, exit status: $kms_exit_status, output:\n@{$kms_output}");
+		return 0;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to execute ssh command to set kms server to $kms_server:$kms_port on $computer_node_name");
+		return;
+	}
+
+	return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 activate_licensing
+
+ Parameters  :
+ Returns     :
+ Description :
+
+=cut
+
+sub activate_licensing {
+	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 $management_node_keys     = $self->data->get_management_node_keys();
+	my $computer_node_name       = $self->data->get_computer_node_name();
+	
+	# Run slmgr.vbs -ato
+	my $activate_command = '$SYSTEMROOT/System32/cscript.exe $SYSTEMROOT/System32/slmgr.vbs -ato';
+	my ($activate_exit_status, $activate_output) = run_ssh_command($computer_node_name, $management_node_keys, $activate_command);
+	if (defined($activate_exit_status)  && $activate_exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "successfully activated licensing on $computer_node_name");
+	}
+	elsif (defined($activate_exit_status)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to activated licensing on $computer_node_name, exit status: $activate_exit_status, output:\n@{$activate_output}");
+		return 0;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to activated licensing on $computer_node_name");
+		return;
+	}
+
+	return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 create_eventlog_entry
+
+ Parameters  :
+ Returns     :
+ Description :
+
+=cut
+
+sub create_eventlog_entry {
+	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 $management_node_keys     = $self->data->get_management_node_keys();
+	my $computer_node_name       = $self->data->get_computer_node_name();
+	
+	my $message = shift;
+	
+	# Make sure the message was passed as an argument
+	if (!defined($message)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to create eventlog entry, message was passed as an argument");
+		return 0;
+	}
+	
+	# Run eventcreate.exe to create an event log entry
+	my $eventcreate_command = '$SYSTEMROOT/System32/eventcreate.exe /T INFORMATION /L APPLICATION /SO VCL /ID 555 /D "' . $message . '"';
+	my ($eventcreate_exit_status, $eventcreate_output) = run_ssh_command($computer_node_name, $management_node_keys, $eventcreate_command);
+	if (defined($eventcreate_exit_status)  && $eventcreate_exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "successfully created event log entry on $computer_node_name: $message");
+	}
+	elsif (defined($eventcreate_exit_status)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to create event log entry on $computer_node_name: $message, exit status: $eventcreate_exit_status, output:\n@{$eventcreate_output}");
+		return 0;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to run ssh command to create event log entry on $computer_node_name: $message");
+		return;
+	}
+	
+	return 1;
+}
+
+#/////////////////////////////////////////////////////////////////////////////
+
+=head2 reboot
+
+ Parameters  : $wait_for_reboot
+ Returns     : 
+ Description : 
+
+=cut
+
+sub reboot {
+	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 $management_node_keys     = $self->data->get_management_node_keys();
+	my $computer_node_name       = $self->data->get_computer_node_name();
+	
+	# Check if an argument was supplied
+	my $wait_for_reboot = shift;
+	if (!defined($wait_for_reboot) || $wait_for_reboot !~ /0/) {
+		notify($ERRORS{'DEBUG'}, 0, "rebooting $computer_node_name and waiting for ssh to become active");
+		$wait_for_reboot = 1;
+	}
+	else {
+		notify($ERRORS{'DEBUG'}, 0, "rebooting $computer_node_name and NOT waiting");
+		$wait_for_reboot = 0;
+	}
+	
+	my $reboot_start_time = time();
+	notify($ERRORS{'DEBUG'}, 0, "reboot will be attempted on $computer_node_name");
+	
+	# Make sure SSH access is enabled from private IP addresses
+	if (!$self->firewall_enable_ssh_private()) {
+		notify($ERRORS{'WARNING'}, 0, "reboot not attempted, failed to enable ssh from private IP addresses");
+		return 0;
+	}
+	
+	# Make sure ping access is enabled from private IP addresses
+	if (!$self->firewall_enable_ping_private()) {
+		notify($ERRORS{'WARNING'}, 0, "reboot not attempted, failed to enable ping from private IP addresses");
+		return 0;
+	}
+	
+	# Initiate the shutdown.exe command to reboot the computer
+	my $shutdown_command = "C:/Windows/system32/shutdown.exe -r -t 0 -f";
+	my ($shutdown_exit_status, $shutdown_output) = run_ssh_command($computer_node_name, $management_node_keys, $shutdown_command);
+	if (defined($shutdown_exit_status)  && $shutdown_exit_status == 0) {
+		notify($ERRORS{'OK'}, 0, "successfully executed reboot command on $computer_node_name");
+	}
+	elsif (defined($shutdown_exit_status)) {
+		notify($ERRORS{'WARNING'}, 0, "failed to execute reboot command on $computer_node_name, exit status: $shutdown_exit_status, output:\n@{$shutdown_output}");
+		return 0;
+	}
+	else {
+		notify($ERRORS{'WARNING'}, 0, "failed to execute ssh command to reboot $computer_node_name");
+		return;
+	}
+	
+	# Check if wait for reboot is set
+	if (!$wait_for_reboot) {
+		return 1;
+	}
+	
+	# Wait for reboot is true
+	notify($ERRORS{'OK'}, 0, "sleeping for 30 seconds while $computer_node_name begins reboot");
+	sleep 30;
+	
+	# Wait maximum of 4 minutes for the computer to go offline then come back up
+	if (!$self->wait_for_ping(4)) {
+		# Check if the computer was ever offline, it should have been or else reboot never happened
+		notify($ERRORS{'WARNING'}, 0, "$computer_node_name never responded to ping, attempting hard power reset");
+		
+		# Just explicitly call xCAT's _rpower for now
+		# TODO: implement public reset() subroutines in all of the provisioning modules
+		# TODO: allow provisioning and OS modules access to each other's subroutines
+		if (VCL::Module::Provisioning::xCAT::_rpower($computer_node_name, "cycle")) {
+			notify($ERRORS{'OK'}, 0, "initiated hard power reset on $computer_node_name");
+		}
+		else {
+			notify($ERRORS{'WARNING'}, 0, "reboot failed, failed to initiate hard power reset on $computer_node_name");
+			return 0;
+		}
+		
+		# Wait for computer to respond to ping after initiating hard reset
+		# Wait longer than the first attempt
+		if (!$self->wait_for_ping(6)) {
 			# Check if the computer was ever offline, it should have been or else reboot never happened
 			notify($ERRORS{'WARNING'}, 0, "reboot failed, $computer_node_name never responded to ping even after hard power reset");
 			return 0;
 		}
 	}
 	

[... 1877 lines stripped ...]