TheGeekery

The Usual Tech Ramblings

PowerShell: Remote Registry, and fixing an Office 2007 install

We’ve had a stubborn Windows 2003 server that we’ve been trying to get Office 2007 installed for a while. It has a couple of things wrong with it which have made the install substantially harder.

The first being an MSI speed issue. Because the server plays host to about 120+ custom applications, all installed using MSI, any further MSI installs simply drag whilst it reads through all the keys, does its validation and backup checks, and then proceeds.

The second issue we have is that this box has a partially installed, corrupted Office 2007 install already on here. Attempting to do “upgrades” fail, and uninstalling just barfs with a delightful error. This resulted in some stumbling to figure out how to get it off, and start again.

Fortunately Microsoft do frequently document things like this, you just have to find the right search terms to find it, which luckily I did, in the form of KB928218. This article gives you a list of the registry keys you have to go through and remove, and you should be all well and good. Unfortunately, this list is over 200 registry keys on our count, and quite a list all over the place. This is where Powershell comes in handy…

As the server itself doesn’t (yet) have Powershell installed, I ran the commands from my laptop, which required remote registry access. Powershell makes use of the .Net libraries to do this:

$server = 'MyRemoteServer'
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('LocalMachine', $server)

The LocalMachine reference here is the part of the registry we want to open, which is equivalent to HKEY_LOCAL_MACHINE. This now gives us a registry object in the HKLM section of the registry. From here, there are a bunch of functions we can use to fetch the keys we need. The first is OpenSubKey. This opens a subkey in the $reg object.

"Removing Office 12 Software Keys"
$regKey = $reg.OpenSubKey('SOFTWARE\Microsoft\Office', $true)

This assigns $regKey to the subkey. The boolean flag $true on the end is to make sure the key is opened in writable mode. This will be required to modify the contents of the key.

Per the KB article, Microsoft say to delete all of the 12.0 subkey from under here. I had originally used the following:

$regKey.DeleteSubKey('12.0')

This resulted in an error about not being allowed to delete subkeys. Then I stumbled upon the much more suitable DeleteSubKeyTree, which, as the name hints, deletes the tree. So the new code became:

$regKey.DeleteSubKeyTree('12.0')

This was repeated for the various other subkeys Microsoft recommended deleting. It became more complex when it asks you to delete keys that match some of the characters below, and use a * as the match. For example:

Note In the following registry keys, the asterisk character (*) represents one or more characters in the subkey name.

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall*0FF1CE*

Fortunately, Powershell is very good at string manipulation, and queries, so this was a bit of a case for a where, and foreach loop.

$regKey = $reg.OpenSubKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall', $true)
$regKey.GetSubKeyNames() | Where { $_ -like "*0FF1CE*" } | ForEach {
  "Deleting subkey $_"
  $regKey.DeleteSubKeyTree($_)
}
It looks pretty messy, but it’s actually pretty easy if you understand pipes (the character)1. The above line 24 boils down to:
  • Get the subkey names
  • Find only the ones that have 0FF1CE in them (yes I know that’s office with a zero and a one)
  • For each one that matches, go delete it, and its children

See, not so hard.

There are some parts of this that venture KB into other areas of the registry, so the syntax changes in the opening command, for example:

$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('ClassesRoot', $server)

This opens HKEY_CLASSES_ROOT. You can use CurrentUser to access HKEY_CURRENT_USER too.

That’s about it for remote registry access and fixing this blasted Office install. As a side, the script saved me about 30 minutes of bashing through about 200+ registry keys for cleaning up.

Below is the full script, with the delete statements commented out. If you have 2007 installed, give it a run, see what happens (please double check that all the deletes are in fact commented out, this script was used against a server to fix an install issue, and did have active delete statements in, and I cannot remember if I got all the deletes).

$server = 'MyRemoteServer'
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine", $server)

"Removing Office 12 Software Keys"
$regKey = $reg.OpenSubKey('SOFTWARE\Microsoft\Office\', $true)
#$regKey.DeleteSubKeyTree('12.0')

$regKey.Close()

"Removing 2003 shadow keys"
$regKey = $reg.OpenSubKey('Software\Microsoft\Windows NT\CurrentVersion\Terminal Server\Install\Software\Microsoft\Office', $true)
#$regKey.DeleteSubKeyTree('12.0')

$regKey.Close()

"Removing office download references"
$regKey = $reg.OpenSubKey('SOFTWARE\Microsoft\Office\Delivery\SourceEngine\Downloads', $true)
$regKey.GetSubKeyNames() | Where{ $_ -like "*0FF1CE*" } | ForEach {
  "Deleting subkey $_"
  #$regKey.DeleteSubKeyTree($_);

}
$regKey.Close()

"Removing Office Uninstall keys"
$regKey = $reg.OpenSubKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall', $true)
$regKey.GetSubKeyNames() | Where { $_ -like "*0FF1CE*" } | ForEach {
  "Deleting subkey $_"
  #$regKey.DeleteSubKeyTree($_)

}
$regKey.Close()

"Removing Office upgrade keys"
$regKey = $reg.OpenSubKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UpgradeCodes', $true)
$regKey.GetSubKeyNames() | Where { $_ -like "*F01FEC" } | ForEach {
  "Deleting subkey $_"
  #$regKey.DeleteSubKeyTree($_)

}
$regKey.Close()

"Removing Office product key references"
$regKey = $reg.OpenSubKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\S-1-5-18\Products', $true)
$regKey.GetSubKeyNames() | Where { $_ -like "*F01FEC" } | ForEach {
  "Deleting subkey $_"
  #$regKey.DeleteSubKeyTree($_)

}
$regKey.Close()

"Removing Office services"
$regKey = $reg.OpenSubKey('SYSTEM\CurrentControlSet\Services', $true)
#$regKey.DeleteSubKeyTree('ose')

$regKey.Close()

"Removing Uninstall keys"
$regKey = $reg.OpenSubKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall', $true)
$regKey.GetSubKeyNames() | ForEach{
  $subName = $_
  $subKey = $regKey.OpenSubKey($subName)
  $value = $subKey.GetValue("UninstallString")
  if ($value -like "*Office Setup Controller\Setup.*") {
    "Deleting Uninstall key $subName"
  	#$regKey.DeleteSubKeyTree($subName)

  }
  $subKey.Close()
}
$regKey.Close()

$reg.Close()

$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('ClassesRoot', $server)

$regKey = $reg.OpenSubKey('Installer\Features', $true)
$regKey.GetSubKeyNames() | Where { $_ -like "*F01FEC" } | ForEach {
  "Deleting subkey $_"
  #$regKey.DeleteSubKeyTree($_)

}
$regKey.Close()

$regKey = $reg.OpenSubKey('Installer\Products', $true)
$regKey.GetSubKeyNames() | Where { $_ -like "*F01FEC" } | ForEach {
  "Deleting subkey $_"
  #$regKey.DeleteSubKeyTree($_)

}
$regKey.Close()

$regKey = $reg.OpenSubKey('Installer\UpgradeCodes', $true)
$regKey.GetSubKeyNames() | Where { $_ -like "*F01FEC" } | ForEach {
  "Deleting subkey $_"
  #$regKey.DeleteSubKeyTree($_)

}
$regKey.Close()

$regKey = $reg.OpenSubKey('Installer\Win32Assemblies', $true)
$regKey.GetSubKeyNames() | Where { $_ -like "*Office12*" } | ForEach {
  "Deleting subkey $_"
  #$regKey.DeleteSubKeyTree($_)

}
$regKey.Close()
$reg.Close()


"Deleting UserInfo"
$reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey('CurrentUser', $server)
$regKey = $reg.OpenSubKey('Software\Microsoft\Office\Common', $true)
#$regKey.DeleteSubKeyTree('UserInfo')

$regKey.Close()

$reg.Close()
  1. Don’t worry if you don’t, they’re easier than you think. It’s just a fancy way of stringing commands together and using the output of one command as the input in the next command. 

Comments