26 January, 2013

Edit MultiString registry value remotely - OS

The other day I needed to edit a RED_MULTI_SZ value on ~400 servers. You could say: it's an "everyday" task, why a blog post about it?
Indeed, it's no big deal, but I spent about an hr on one particular issue with this task that I wanted to share.

As I mentioned I had to edit a MultiString value (which is basically an array - in scripting world) and add a new element to it.

Let's start at the beginning. To write a String value to a remote registry you can use Microsoft.Win32.RegistryKey:
[Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine','REMOTEHOST').OpenSubKey("SOFTWARE\Microsoft\FTH",$true).SetValue('ExclusionList', $newdata,'string')

However, if you want to edit a REG_MULTI_SZ value, you need to read the content first, do whatever you want with it (search for item, remove, add...etc) and then write it back to the registry. In my case I had to add a new item to the beginning of the registry value:
$Array = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine','r2d2').OpenSubKey("SOFTWARE\Microsoft\FTH",$true).GetValue("ExclusionList")
$newArray = @("newitem")
$newarray += $array
[Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine','r2d2').OpenSubKey("SOFTWARE\Microsoft\FTH",$true).SetValue('ExclusionList', $newarray,'MultiString')

And it will throw an error:
Exception calling "SetValue" with "3" argument(s): "The type of the value object did not match the specified
RegistryValueKind or the object could not be properly converted."
At line:1 char:1
+ [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine','r2d2').OpenSubK ...


Why? The error message has the answer: the type of value object did not match...etc. Hah! Object?? What object? I've got strings in an array... let's see:
PS C:\> $newarray.GetType()
IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     Object[]                                 System.Array

Khm, ok, I forgot about PS being object oriented :) Let's force the array to have String type then:
[string[]]$newArray = @("newitem")

Now, let's double check its type:
PS C:\> $newArray.GetType()
IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object

Awesome. As expected, the following line now works:
[Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine','r2d2').OpenSubKey("SOFTWARE\Microsoft\FTH",$true).SetValue('ExclusionList', $newarray,'MultiString')


May the Force...
t

5 comments:

  1. Hello Tom (I assume your name is Tom?) thanks very much for posting very useful indeed.

    I had to make a slight adjustment to get it working for me e.g. using [string[]] (and array of strings) rather than [string] as follows

    $Array = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine',$ComputerName).OpenSubKey("SOFTWARE\Ernie",$true).GetValue("Version3")

    [String[]]$newArray = @("newitem")

    $newarray += $array
    [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine',$ComputerName).OpenSubKey("SOFTWARE\Ernie",$true).SetValue('Version3', $newarray,'MultiString')

    Thanks again, very helpful tip, I will subscribe to your blog

    Ernie
    ErnestBrant@Hotmail.co.uk

    ReplyDelete
  2. Great article but ... How do I rearrange the values w/o losing what is already there. I have been trying to edit "Bind" under SYSTEM\\CurrentControlSet\\services\\Tcpip\\Linkage to change the binding order of NICs...but it just adds the extra values so what was earlier 3 entries now becomes 6. How do I rearrange w/o adding additional entries ...here is my script ...
    $PrimaryNIC = Invoke-command -ComputerName $Server -cred $cred -ScriptBlock {Get-NetAdapter -Name Pri | select -Property InterfaceGUID -ExpandProperty InterfaceGUID} #--> Working
    $SecondaryNIC = Invoke-command -ComputerName $Server -cred $cred -ScriptBlock {Get-NetAdapter -Name Sec | select -Property InterfaceGUID -ExpandProperty InterfaceGUID} #--> Working
    $Array = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine',"$server").OpenSubKey("SYSTEM\\CurrentControlSet\\services\\Tcpip\\Linkage",$true).GetValue("Bind")
    [string[]]$newArray = @("\Device\$PrimaryNIC","\Device\$SecondaryNIC")
    $newarray += $array
    [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine',"$server").OpenSubKey("SYSTEM\\CurrentControlSet\\services\\Tcpip\\Linkage",$true).SetValue('Bind', $newarray,'MultiString')

    ReplyDelete
    Replies
    1. hi, the array you are committing to the registry ($newarray) should contain the values in the order you want them to be in the registry. If you want to get rid of the original values, just don't execute lin $newarray += $array, so basically don't add whatever was originally in that Value. But, be careful, you really need to make sure you are adding stuff into the registry you really want. So test it with a mockup registry value.

      Delete
  3. Replies
    1. Glad it was useful, Master Jedi

      Delete