Using XML for variable source to modify registry values.

Hello,

I have been steadily making progress in my first attempt at doing any type of scripting. My goal is to write a module that pulls a listing of registry keys from XML and allows you to manipulate the registry key values. At this point I have the basics working but have run into a challenge that I don’t even know enough about to know the right questions to ask.

So right now I can populate the XML with as many keys as I want. Pull in the data and set them all to “enabled”. I am not doing anything with the ID or Off values at this point I just created them as I suspect they will be useful in the future.

What I want to do create a parameter so I can do something like:

Test-XML -computername comp1, comp2 -keyid 1,3,7,9

And have it only iterate through the script with those RegUid’s. And I have no clue where I need to start reading to figure out how to achieve this. ComputerName makes sense because the user is inputting the “variable” values. But how would i have the user select them from an imported list?

So here is what I have so far:

FIXING screwed formatting up.

Powershell Script

function Test-XML
{
    [CmdletBinding()]
    [OutputType([int])]
    Param
    (
        # Supports Multiple Computers if Authenticated to make changes.
        [Parameter(Mandatory=$True,
                    ValueFromPipeline=$true,
                    ValueFromPipelineByPropertyName=$true)]
        
        [String[]]$ComputerName,
        
        #Switch to turn on Error logging
        [Switch]$ErrorLog,
        [String]$LogFile = 'c:\errorlog.txt'
        
    )
 
 #This is the Core process for checking if keys exist and setting them to enabled.
$scriptBlock = {
#sets parameter to be passed to remote system.
param ($xmlvalue)


#Iterates through XML values once for each computer specified.
foreach($xval in $xmlvalue)

    {

    $value = ((Get-ItemProperty -Path $xval.RegPath).($xval.RegName))
    
    If ($value -eq $xval.RegOn)
    
            { 
                    
                    Write-Host  "$env:COMPUTERNAME - "$xval.RegName" is already enabled"
       
            }else{ 
                    
                    Set-ItemProperty -Path ($xval.RegPath) -Name ($xval.RegName) -type ($xval.RegType) -value ($xval.RegOn)
                    Write-Host  "$env:COMPUTERNAME - "$xval.RegName" is not enabled, setting value to Enabled."
                    
                 }  

    }
  }

#Loads each Regkey name and path from XML sets to working varible
$xml = [XML](Get-Content C:\Temp\MyPshellTools\Example.xml)
$xmlvalue = $xml.SelectNodes("//KeyID")

#Run commands against each computer specified with -computername cname1,cname2,etc..
foreach ($Computer in $ComputerName) {

#Determine if you can connect to specified ComputerName
    if(Test-Connection -Count 1 -Quiet -ComputerName $Computer)
  
#Run the Script Block  
    {
  
        Invoke-Command -ComputerName $Computer -ScriptBlock $scriptBlock -ArgumentList $xmlvalue
    
    }
    
#Warn that a computer is unreachable

    else{Write-Warning "Can't connect to server: $Computer"}




}
}

Sorry dunno how to get XML to format properly. So giving you best i can as example:

(regkey)
(keyid reguid =1)
(RegName) RegPropertyName
(RegPath) Path to registry key
(RegType) Registry type
(RegOn) Value to turn key on
(RegOff) Value to turn key off
(Keyid)
(regkey)

Hello Kazrath,

You can attach the full XML file as .txt to make it easier for us to help you. A simple way to have a user to select something is the cmdlet Out-Gridview.

One thing I’ve noticed is that your function uses the Test verb. A good practice is to have a Test function never change anything. Your test function is changing values on remote machines which I wouldn’t expect as user of your module due to the Test verb.

Best,
Daniel

Why exactly are you using XML? Typically, if you are manipulating XML, it’s going to absorbed into another application outside of Powershell. Your posted issue sounds like you are trying to use XML as a database and write a function to read XML. It’s much easier to leverage XML that can be directly imported as a PSObject versus a custom XML. Using the base properties in your XML, you could do something like this:

$object = @()
$object += New-Object –TypeName PSObject -Property `
                (@{'KeyID'=1;
                'Root'="HKCU";
                'Name'="EnableBalloonTips";
                'Path'="\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced";
                'Type'=[Microsoft.Win32.RegistryValueKind]::DWord;
                'Value'=0})

$object += New-Object –TypeName PSObject -Property `
                (@{'KeyID'=2;
                'Root'="HKCU";
                'Name'="NoDrives";
                'Path'="\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer";
                'Type'=[Microsoft.Win32.RegistryValueKind]::DWord;
                'Value'=1})

$object += New-Object –TypeName PSObject -Property `
                (@{'KeyID'=3;
                'Root'="HKCU";
                'Name'="NoInternetOpenWith";
                'Path'="\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer";
                'Type'=[Microsoft.Win32.RegistryValueKind]::DWord;
                'Value'=1})

$object

$object | foreach{
    New-ItemProperty -path ("{0}:{1}" -f $_.Root,$_.Path) -name $_.Name -value $_.Value -PropertyType $_.Type -Force -WhatIf
}

With it being an object in Powershell, you can leverage Powershell syntax versus filtering XML:

$IDsToProcess = 1,3
$object | Where{ $IDsToProcess -contains $_.KeyID } |foreach{
        "Processing KeyID: {0}" -f $_.KeyID
        New-ItemProperty -path ("{0}:{1}" -f $_.Root,$_.Path) -name $_.Name -value $_.Value -PropertyType $_.Type -Force -WhatIf
}

If you want a “master” XML list to use as a database, you can just generate a PSObject and then export it to XML:

Export-Clixml -InputObject $object -Path C:\MyRegXML.xml

and then can do:

$object = Import-CliXML C:\MyRegXML.xml
$object | foreach{
    New-ItemProperty -path ("{0}:{1}" -f $_.Root,$_.Path) -name $_.Name -value $_.Value -PropertyType $_.Type -Force -WhatIf
}

As a side note, you can also use CSV as a database (flat-file). The biggest difference between XML and CSV is exporting to XML will retain the data types (e.g. DWORD will still be data type Microsoft.Win32.RegistryValueKind) versus imported properties from a CSV will all be strings. However, for what you are doing, all of the items can be strings and you can edit CSV’s in Excel versus XML would need to edited with Notepad (or XML editor) or a new XML generated from an object.

@Daniel, I am using Test verb because it at this point is just a “rough draft”. Initially I was just testing importing XML and kept expanding on it and had not modified the verb.

@Rob, Yes, the XML is designed to be a static flat database as the keys will never change name, location, type, or value. Ultimately the idea was to #1 Teach myself how to script, #2 have a script to set specific regkey values to enabled/disabled as my job function requires me to perform many removals/installations during the course of reproducing issues.

I was not aware you could handle the XML data in the way you specified. I will try playing with it.

@Rob, Good god that made things significantly more simple. I had no clue how arrays worked and you using an example that fit what I was trying to do allowed me to “sort of” grasp the concept. I will definitely need to play with this more.

I’m glad it’s making thing a little more simple for you. The big goal in Powershell is to understand how important generating a object to work with. Once you have the data you need in an object, Powershell excels at sorting, filtering and grouping data and sending that information to commands or pipe lines. Post more questions, that’s how you learn. :slight_smile: