Beems
Beems

Reputation: 810

Test a Large Number of Registry Values in Powershell

Testing that individual registry values exist and checking them for the proper data is not overly-complicated in Powershell using Get-Item and Get-ItemProperty. What I would like to be able to do is check a large number of registry values for existence as well as data.

For example, given the following registry entries:

HKLM:\SOFTWARE\Breakfast\1001 = 3 [DWORD]
HKLM:\SOFTWARE\Breakfast\1003 = 3 [DWORD]
HKLM:\SOFTWARE\Breakfast\1004 = 3 [DWORD]
HKLM:\SOFTWARE\Breakfast\1005 = 1 [DWORD]

A big, ugly script that performs the test on each value and data individually isn't complicated, but I'd love to see if it is possible to throw a friendly name, registry path/value, and the desired data into an array so that we could have a function that would perform our tests.

The array could look something like this:

$registry_list = @()
$registry_list.gettype()
$registry_list += ,@('Poptarts','HKLM:\SOFTWARE\Breakfast\','1001','3')  
$registry_list += ,@('Toast','HKLM:\SOFTWARE\Breakfast\','1002','3') 
$registry_list += ,@('Muffins','HKLM:\SOFTWARE\Breakfast\','1003','3') 
$registry_list += ,@('Bagels','HKLM:\SOFTWARE\Breakfast\','1004','3')
$registry_list += ,@('Biscuits','HKLM:\SOFTWARE\Breakfast\','1005','3')

Since I'm new to arrays, I have no idea how to feed these into a function that can output something showing only the errors

Toast
  Value Missing (HKLM:\SOFTWARE\Breakfast\1002)
Biscuits
  Value Set Incorrectly (HKLM:\SOFTWARE\Breakfast\1005) Desired: 3. Actual: 1

If anyone can weigh in to help figure out how a function or similar can iterate through each of the registry values it would be appreciated. The examples here are short, but I really want to be able to run hundreds of registry values through this test.

Upvotes: 2

Views: 2128

Answers (3)

Mathias R. Jessen
Mathias R. Jessen

Reputation: 174825

I would store the data in a CSV file:

friendlyName,key,value,data
Poptarts,HKLM:\SOFTWARE\Breakfast\,1001,3
Toast,HKLM:\SOFTWARE\Breakfast\,1002,3
Muffins,HKLM:\SOFTWARE\Breakfast\,1003,3
Bagels,HKLM:\SOFTWARE\Breakfast\,1004,3
Biscuits,HKLM:\SOFTWARE\Breakfast\,1005,3

Then loop over each row in the file

foreach($row in Import-Csv .\breakfast.csv)
{
    # retrieve key
    $key = Get-Item $row.key

    # retrieve value
    $value = $key |Get-ItemProperty -Name $row.value

    # compare data
    $valid = $value."$($row.value)" -eq $row.data

    # output result
    $outParams = @{
        Object = if($valid){"$($row.friendlyName) is correct"} else {"$($row.friendlyName) is incorrect"}
        ForegroundColor = @('Red','Green')[+$valid]
    }
    Write-Host @outParams
}

I'll leave implementation of error handling and nicer output an excercise for OP :-)

Upvotes: 1

Paolis
Paolis

Reputation: 150

I'd highly suggest when using large arrays to use an array of objects. Creating objects makes referencing the different parts of your array very easy using properties.

You can also then use a template server that has the proper values already in place to then build the object/array of objects to then use to validate other systems.

Here is a basic example of building an object. It is more efficient to create a simple function that builds these objects for you so that you don't have so much code repetition, but this is basic way to go about it. If you want a more advanced method of creating objects, let me know and I'll post an example.

$registrySet = @()

$registryObj = New-Object -TypeName psobject
$registryObj | Add-Member -MemberType NoteProperty -Name Name -Value 'Toast'
$registryObj | Add-Member -MemberType NoteProperty -Name Key -Value 'HKLM:\SOFTWARE\Breakfast\'

$subKeySet = @()
$subKeyObj = New-Object -TypeName psobject
$subKeyObj | Add-Member -MemberType NoteProperty -Name Name -Value '1001'
$subKeyObj | Add-Member -MemberType NoteProperty -Name Type -Value 'DWORD'
$subKeyObj | Add-Member -MemberType NoteProperty -Name Value -Value '3'
$subKeySet += $subKeyObj

$subKeyObj = New-Object -TypeName psobject
$subKeyObj | Add-Member -MemberType NoteProperty -Name Name -Value '1002'
$subKeyObj | Add-Member -MemberType NoteProperty -Name Type -Value 'DWORD'
$subKeyObj | Add-Member -MemberType NoteProperty -Name Value -Value '3'
$subKeySet += $subKeyObj

$subKeyObj = New-Object -TypeName psobject
$subKeyObj | Add-Member -MemberType NoteProperty -Name Name -Value '1003'
$subKeyObj | Add-Member -MemberType NoteProperty -Name Type -Value 'DWORD'
$subKeyObj | Add-Member -MemberType NoteProperty -Name Value -Value '1'
$subKeySet += $subKeyObj

$registryObj | Add-Member -MemberType NoteProperty -Name SubKeySet -Value 
$subKeySet

$registrySet += $registryObj

$registrySet | Where {$_.Name -ieq 'toast'} | select SubKeySet

Upvotes: 0

Bacon Bits
Bacon Bits

Reputation: 32220

I've never been a huge fan of multidimensional arrays in PowerShell. They end up feeling very flaky or unstable. Arrays in PowerShell also suck because when you use the += operator, the system has to build a new array with the new element and then throw the old array away. It's computationally expensive.

For this case, I would create an ArrayList, and add the arrays to that. I would also probably use a HashTable for each item so I could use a name instead of an index number to refer to the items:

$registry_list = New-Object System.Collections.ArrayList;

# Use the Add() function to add records.  The [void] type is here because the function 
# normally returns the number of records in the ArrayList, and we don't want that to go to output.
[void]$registry_list.Add(@{Value='Poptarts';Path='HKLM:\SOFTWARE\Breakfast';Key='1001';Data='3'});
[void]$registry_list.Add(@{Value='Toast';Path='HKLM:\SOFTWARE\Breakfast';Key='1002';Data='3'});

$registry_list | ForEach-Object {
    $RegistryPath = Join-Path -Path $_.Path -ChildPath $_.Key;
    if (Test-Path -Path $RegistryPath) {
        Write-Host "Path '$RegistryPath' exists."
        $RegistryData = (Get-ItemProperty -Path $RegistryPath).($_.Value)
        if ($RegistryData -eq $_.Data) {
            Write-Host "Check OK.  Value $($_.Value) data is set to '$RegistryData'.  Desired data is '$($_.Data)'."
        }
        else {
            Write-Host "Check Failed.  Value $($_.Value) data is set to '$RegistryData'.  Desired data is '$($_.Data)'."
        }
    }
    else {
        Write-Host "Path '$RegistryPath' does not exist."
    }
}

Note that I have not rigorously tested this code. Notably, I'm a bit skeptical about how correct if ($RegistryData -eq $_.Data) is for all cases.

Upvotes: 3

Related Questions