Remote Recursive Registry script (Part 2)

Posted: June 2, 2015 in General, Powershell, Scripting

In part 1, I started building my advanced function for setting registry values on both the local machine, or a remote machine using the built-in .net methods.  Using the PSDrive called HKLM: or HKCU: both have significant limitations (like not being able to access a remote machine, and that registry values are considered ItemProperties of the registry key which is an Item.  There are other limitations also, but these are a substantial hassle for me, so I try to avoid using them).

The rest of the main body of the code looks like this:

Write-Debug -Message "Entering foreach (computer in computername) loop"
ForEach ($Computer in $ComputerName)
{
	Write-Debug -Message "ForEach ($Computer in ComputerName) pass"
	try
	{
		$Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($Hive, $Computer)
		Write-Debug -Message "Successfully able to open $hive on $computer"
	}
	catch
	{
		Write-Error -Message "Unable to open remote registry hive for $Computer. Please verify connectivity and permissions to retry"
		continue
	}
	Write-Debug -Message "Entering foreach loop to create full registry path if $force is enabled"
	foreach ($SubKey in $RegKeyPath.Split('\'))
	{
		$RegName = $Reg.Name
		Write-Debug -Message "SubKey is $subkey, and current registry path is $RegName"
		if ($Reg.GetSubKeyNames -contains $SubKey)
		{
			Write-Debug "\$Reg contains $SubKey, opening that key"
			$Reg = $Reg.OpenSubKey($SubKey)
		}
		else
		{
			Write-Debug -Message "\$Reg does not contain $subkey, checking for Force"
			if ($Force)
			{
				Write-Debug -Message "\$Force enabled, continuing"
				try
				{
				Write-Debug -Message "Trying to create SubKey ($SubKey)"
				$Reg = $Reg.CreateSubKey($SubKey)
				}
				catch
				{
					$Reg.Close()
					Write-Error -Message "Unable to create $subkey in $RegName, please check your permissions or the Remote Registry service may not be running" -ErrorAction 'Stop'
				}
			}
			else
			{
				$Reg.Close()
				Write-Error -Message "Force not specified, and subkey ($subkey) does not exist in $RegName" -ErrorAction 'Stop'
			}
		}
	}
	Write-Debug -Message "Registry either exists, or has been created. Proceeding with setting the value"
		try
		{
			$Reg.SetValue($ValueName, $ValueData, $ValueType)
			Write-Debug -Message "Succesfully set the value $ValueName on $Computer"
		}
		catch
		{
			Write-Debug -Message "Unable to set the value $ValueName in $RegName on $Computer; Please validate your access & permissions and try again"
		}
		Finally
		{
			$Reg.Close()
		}
	}
}

The primary block of code is the

ForEach ($Computer in $ComputerName) {
}

This is a critical component of the advanced function. Since we declared that the $ComputerName parameter could contain multiple parameters from the pipeline, this construction allows the script to automatically process each of the passed computer objects.

The next block is where we attempt to open the registry hive on the remote computer

Write-Debug -Message "ForEach ($Computer in ComputerName) pass"
try
{
	$Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($Hive, $Computer)
	Write-Debug -Message "Successfully able to open $hive on $computer"
}
catch
{
	Write-Error -Message "Unable to open remote registry hive for $Computer. Please verify connectivity and permissions to retry"
	continue
}

I use the Write-Debug lines to provide inline comments if the script is run using the -debug parameter (thanks to Don Jones). The other option is to use write-verbose which is another text “stream” that can be used to provide inline comments. Typically, if you had a comment for your code that you would use in another language (cmd, vbscript, etc.) then write-verbose is your alternative. Write-debug is useful for “soft” errors that don’t necessarily need to stop your script and won’t necessarily have a huge effect.

The other major construction in this piece of code is the Try/Catch loops. Many other languages have the same type of error handling, but in my background (cmd, vbscript, kix, winbatch, etc.) that construction didn’t necessarily exist. This allows us to try a piece of code to see if it works, and then if it does *not* work, then we expect and error and we catch it. In the catch block, we provide whatever code we think is necessary. In my catch block, I am first writing a non-terminating error with write-error. I also use a continue statement to “break” out of the foreach loop and move on to the next computer.

The next section starts the actual registry processing..

Write-Debug -Message "Entering foreach loop to create full registry path if $force is enabled"
	foreach ($SubKey in $RegKeyPath.Split('\'))
	{
		$RegName = $Reg.Name
		Write-Debug -Message "SubKey is $subkey, and current registry path is $RegName"

First, is our inline comment explaining what is happening, and then we start a foreach loop. In the loop we are getting each element of the path specified since Split breaks it along the backslash dividers. We set $RegName equal to the name of the registry item. This sets RegName to the actual string of the path, and of course the inline comment.

Next is the section to test the path. We use the registry object’s method for GetSubKeyNmes which gives you an array of all the subkey names of the provided object, and then we use the standard -contains function to see if the subkey name from the foreach loop. If it does contain it, then we change the $Reg object to be the actual subkey that we found. This is the meat of the recursive part; with each round of the loop, we look at the registry key and if it is there, we set it one layer deeper.

if ($Reg.GetSubKeyNames -contains $SubKey)
	{
		Write-Debug "\$Reg contains $SubKey, opening that key"
		$Reg = $Reg.OpenSubKey($SubKey)
	}
	else
	{

Now, if the $Reg.GetSubKeyNames does not contain the $Subkey, then we perform the next piece of code.

Write-Debug -Message "\$Reg does not contain $subkey, checking for Force"
			if ($Force)
			{
				Write-Debug -Message "\$Force enabled, continuing"
				try
				{
				Write-Debug -Message "Trying to create SubKey ($SubKey)"
				$Reg = $Reg.CreateSubKey($SubKey)
				}
				catch
				{
					$Reg.Close()
					Write-Error -Message "Unable to create $subkey in $RegName, please check your permissions or the Remote Registry service may not be running" -ErrorAction 'Stop'
				}

In this piece of code, we check to see if the $Force switch is present which tells us to go ahead and create the path “piece” because it is missing. So, if $Force is there, we try to create the piece and then if it fails, we catch the error. And of course, with an error we follow the proper programming practice and Close() the registry key, and then write out an error indicating that there was a problem. The text message includes the most likely causes of the issue.
This takes us to the next piece of code, which is the else section of the main “if” block.

else
			{
				$Reg.Close()
				Write-Error -Message "Force not specified, and subkey ($subkey) does not exist in $RegName" -ErrorAction 'Stop'
			}
		}
	}

By closing out the registry section, we wrap up the loop to finish our recursive piece. In pseudo code, we are doing this:


<pre>for each piece of the path
     Open the registry key
          Did it open?
               If yes, then set the key to this level
               If no, then are we set to force create it?
                    If yes, then create it and set the key to this level
                    If no, then write out and error and close the registry level
go to top of the loop

Then the last portion of the main body of code is this piece:

Write-Debug -Message "Registry either exists, or has been created. Proceeding with setting the value"
		try
		{
			$Reg.SetValue($ValueName, $ValueData, $ValueType)
			Write-Debug -Message "Succesfully set the value $ValueName on $Computer"
		}
		catch
		{
			Write-Debug -Message "Unable to set the value $ValueName in $RegName on $Computer; Please validate your access &amp; permissions and try again"
		}
		Finally
		{
			$Reg.Close()
		}
	}
}

We try to set the registry value that we specified at the beginning of the function. If we can set the value, we write the information that we can, if we can’t then we write out a debug message indicating this, and then the Finally block follows the proper programming practices and closes the registry key (we don’t want memory leaks). And because we are accepting multiple computer names, the entire script will be repeated for each of the computers provided. I hope everyone finds this useful. This is now one of my standard “toolkit” scripts.

And here is a link to a PDF containing the script (it has to be a PDF because WordPress doesn’t like plain text documents as far as I can tell).
set-drfremoteregistry.pdf

Till next time,

David

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s