Useful Little PowerShell Functions - Set-RegistryItemValue
Well hello again everyone! For this installment of "over-engineered PowerShell code", we have a robust little function that has one job; ensure that a registry item exists with a specified name and value in a specified key. Let me start first with the code.
function Set-RegistryItemValue
{
param (
#The full path of the registry key
[Parameter(Mandatory = $true)]
[String]$RegistryKeyPath,
# The name of the registry item
[Parameter(Mandatory = $true)]
[string]$RegistryItemName,
# The value of the registry item
[Parameter(Mandatory = $true)]
[String]$RegistryItemValue,
# The type of registry value
[Parameter(Mandatory = $true)]
[String]$RegistryItemType
)
[int]$RegistryKeyExistsCounter = 0
[bool]$RegistryKeyCurrentlyExists = $false
while(($RegistryKeyExistsCounter -le 1) -and ($RegistryKeyCurrentlyExists -eq $false))
{
[bool]$TestPathResult = $false
try
{
$TestPathResult = Test-Path -Path "Registry::$($RegistryKeyPath)" -ErrorAction Stop
}
catch
{
return $Error[0]
}
if($TestPathResult -eq $true)
{
$RegistryKeyCurrentlyExists = $true
}
else
{
try
{
New-Item -Path "Registry::$($RegistryKeyPath)" -ErrorAction Stop | Out-Null
}
catch
{
return $Error[0]
}
}
$RegistryKeyExistsCounter = $RegistryKeyExistsCounter + 1
}
if ($RegistryKeyCurrentlyExists -eq $false)
{
return 1
}
[int]$RegistryItemExistsCounter = 0
[Bool]$RegistryItemCurrentlyExists = $false
while(($RegistryItemCurrentlyExists -le 1) -and ($RegistryItemCurrentlyExists -eq $false))
{
$CurrentRegistryItemValue = $null
try
{
$CurrentRegistryItemValue = Get-ItemProperty -Path $RegistryKeyPath -Name $RegistryItemName -ErrorAction Stop
}
catch
{
return $Error[0]
}
if ($null -ne $CurrentRegistryItemValue)
{
$RegistryItemCurrentlyExists -eq $true
}
else
{
try
{
New-ItemProperty -Path $RegistryKeyPath -Name $RegistryItemName -Value $RegistryItemValue -PropertyType $RegistryItemType -ErrorAction Stop
}
catch
{
return $Error[0]
}
}
$RegistryItemExistsCounter = $RegistryItemExistsCounter + 1
}
if ($RegistryItemCurrentlyExists -eq $false)
{
return 1
}
[int]$RegistryItemTypeCorrectCounter = 0
[bool]$RegistryItemTypeCorrect = $false
while (($RegistryItemTypeCorrectCounter -le 1) -and ($RegistryItemTypeCorrect -eq $false))
{
$CurrentRegistryItemValue = $null
try
{
$CurrentRegistryItemValue = Get-ItemProperty -Path $RegistryKeyPath -Name $RegistryItemName -ErrorAction Stop
}
catch
{
return $Error[0]
}
if (($CurrentRegistryItemValue."$($RegistryItemName)").GetType() -eq $RegistryItemType)
{
$RegistryItemTypeCorrect = $true
}
else
{
try
{
Remove-ItemProperty -Path "Registry::$($RegistryKeyPath)" -Name $RegistryItemName -ErrorAction Stop | Out-Null
}
catch
{
return $Error[0]
}
try
{
New-ItemProperty -Path $RegistryKeyPath -Name $RegistryItemName -Value $RegistryItemValue -PropertyType $RegistryItemType -ErrorAction Stop | Out-Null
}
catch
{
return $Error[0]
}
}
$RegistryItemTypeCorrectCounter = $RegistryItemTypeCorrectCounter + 1
}
if ($RegistryItemTypeCorrect -eq $false)
{
return 1
}
[int]$RegistryItemSetValueCounter = 0
[bool]$RegistryItemValueSet = $false
while(($RegistryItemSetValueCounter -le 1) -and ($RegistryItemValueSet -eq $false))
{
$CurrentRegistryItemValue = $null
try
{
$CurrentRegistryItemValue = Get-ItemProperty -Path $RegistryKeyPath -Name $RegistryItemName -ErrorAction Stop
}
catch
{
return $Error[0]
}
if ($CurrentRegistryItemValue -eq $RegistryItemValue)
{
$RegistryItemValueSet = $true
}
else
{
try
{
Set-ItemProperty -Path "Registry::$($RegistryKeyPath)" -Name $RegistryItemName -Value $RegistryItemValue -ErrorAction Stop | Out-Null
}
catch
{
return $Error[0]
}
}
$RegistryItemSetValueCounter = $RegistryItemSetValueCounter + 1
}
if ($RegistryItemValueSet -eq $false)
{
return 1
}
return 0
}
To continue with the pattern we established last time, we take the end goal of "make sure a registry item with a specified name and a specified value exists in the specified key", and break it down to the most basic steps needed to get there. We then build code that will perform each step, check to ensure that the step was actually successful, and then move on to the next step.
Why build this function at all?
This really big project I've been working on has a few requirements. The first one is that we need to keep track of state between script executions. The script will reboot the computer a bunch of times, and then a scheduled task will run on boot or on login that relaunches the script. The script will need to be able to store details that future executions of the script will need. The registry is a pretty good spot to do that in, because it survives reboots and doesn't require us to parse anything.
The basic algorithm
Ok, so what are the things we need to make happen to ensure that a registry item with a specified value exists? Let's break it down:
Recommended by LinkedIn
Why check each individual part?
This is a perfectly reasonable question. Why not just check the registry key once and if it's wrong at all, blow it away and recreate it? Well, I don't really have a good answer, other than that I like casting really wide nets. Because each individual component gets checked as we go down the algorithm, we can know with something like 100% certainty that the thing we tried to do actually worked correctly.
Now, maybe an improvement in this function might be to check each part of the registry item, and then have one single Boolean value that determines whether it needs to be recreated at all. Then we could write the code to create the registry item only once.
Other future improvement ideas
One thing I have considered doing is using parameter validation to ensure that we're getting valid data from the function call. Strictly speaking, that functionality wasn't necessary in this code, but it would be an improvement.
Another improvement might be to automatically mount and dismount the HKU hive if that hive is passed in from the function call. That hive doesn't get mounted automatically in PowerShell.
Wrapping up
There's not really a ton to this function other than what's already been described. Let me know what you guys think. As always, I'm open to taking any advice I can get.