One of the things I deal with a lot where I work when performing patch installations is that the number of .NET patches is just amazing! Obviously, these patches are a must to get installed, but sometimes they do not always install with the greatest of ease. The most common error I see with installing a .NET patch mentions that the rollback policy must be enabled for the installation to continue. This KB article provides more information about this as well.
The fix is to go to the registry and navigate to HKLM\Software\Policies\Microsoft\Windows\Installer and then locate the DisableRollback value name. Simply changing this value from a 1 to 0 will enable the rollback setting and the .NET patch will install without any issues.
Now this isn’t really a difficult task by any means, but it is tedious, especially if you have to do this against some 100+ systems. Can you imagine how long it would take and the tiresome clicking noise that you would have to put up with? That would drive me crazy! Enter PowerShell, the best tool for this job. Something that I say and am sure others say that if you have to do something more than once, automate it! So, with that, I put together a module of 3 commands to work with the rollback setting that can be run against one or more computers remotely so you can run the command once and move on with the installations across the network.
The biggest piece to this module that allows me to use it to make the changes lies in the Microsoft.Win32.RegistryKey namespace. The first thing we do is to create the object that makes the connection to the remote registry on a computer. For this, I look at the static methods that I can use to make this connection. The connection that stands out is the OpenRemoteBaseKey() method. This method takes two values: microsoft.win32.registryhive and the computername. Lets see what the microsoft.win32.registryhive properties are so we make sure we get the correct key.
[microsoft.win32.registryhive] | Get-Member -static -type Property
Name MemberType
—- ———-
ClassesRoot Property
CurrentConfig Property
CurrentUser Property
DynData Property
LocalMachine Property
PerformanceData Property
Users Property
Now we know what we can use with the static method to connect. We need the LocalMachine value so we can get to the DisableRollback value name.
Now I will connect to my server, DC1 and show the subkeys using the GetSubKeyNames() method from the object I created.
$computer = 'dc1' $registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine",$computer) $registry.GetSubKeyNames()
Image may be NSFW.
Clik here to view.
Using this newly created object, you can then connect to other keys within the registry and view the data within the value names. For instance, I will continue on down to find the DisableRollback value on DC1.
$subkey = $registry.OpenSubKey("Software\Policies\Microsoft\Windows\Installer",$True) $subkey.GetValue('DisableRollback')
Image may be NSFW.
Clik here to view.
I used two methods to accomplish task: OpenSubKey() and GetValue() which allows me to open the subkeys and then to open the value name and get the data from that value. The $True that I added at the end of the OpenSubKey() tells that I am opening this key as Read/Write vs. the default of Read Only. This is more important if you are going to modify the data in a value or creating a subkey.
Now with that, I can change the value, or create the value name with the value using the SetValue() method. Depending on whether the value exists, you can use either two or three values with the method. In the case of my module, I use three values to create the value using the defined value and to make it as a DWORD value.
First I need to find out the correct value to place to create the right kind of key.
[Microsoft.Win32.RegistryValueKind] | Get-Member -static -type Property
Name MemberType
—- ———-
Binary Property
DWord Property
ExpandString Property
MultiString Property
QWord Property
String Property
Unknown Property
$registry.SetValue("DisableRollback",0,"DWORD") $registry.GetValue('DisableRollback')
Image may be NSFW.
Clik here to view.
So that is a little background on what you can do with PowerShell to make changes to a remote computer registry values. Hopefully Microsoft and the PowerShell team will come up with come cmdlets that do this without having to go through these hoops. But in the meantime, here is an excellent module by Shay Levi that fills in that gap.
The module that I put together is only specific for working with the rollback policy, but you can most definitely take the code and adjust it to your own ideas. Below are a couple of examples with screen shots of my module in action.
Import-Module .\RollbackPolicyModule.psm1 -Verbose Get-RollbackPolicy -Computername DC1
Image may be NSFW.
Clik here to view.
Enable-RollbackPolicy -Computername DC1 -Passthru
Image may be NSFW.
Clik here to view.
Disable-RollbackPolicy -Computername DC1 -Passthru
Image may be NSFW.
Clik here to view.
There you have it, nothing groundbreaking by any means. But this also shows that it doesn’t matter what you decide to use PowerShell for, just as long as it helps you to accomplish exactly what you need to do, then that is all that counts.
Code
Function Get-RollbackPolicy { <# .SYNOPSIS Retieves the current rollback policy on a local or remote computer .DESCRIPTION Retieves the current rollback policy on a local or remote computer .PARAMETER Computername The name of the computer or computers to perform the query against .NOTES Name: Get-RollbackPolicy Author: Boe Prox DateCreated: 06/24/2011 Links: .EXAMPLE Get-RollbackPolicy -Computername DC1 Rollback Computer -------- -------- Enabled dc1 Description ----------- Performs a query against the server, DC1 and returns whether the rollback setting has been enabled or disabled. .EXAMPLE Get-RollbackPolicy -Computername DC1,server1,server2 Rollback Computer -------- -------- Enabled dc1 Disabled server1 Enabled server2 Description ----------- Performs a query against the server, DC1 and returns whether the rollback setting has been enabled or disabled. #> [cmdletbinding()] Param ( [parameter()] [string[]]$Computername ) Process { ForEach ($computer in $computername) { Try { #Make initial registry connection Write-Verbose "Making registry connection to remote computer" $registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine",$computer) #Connect to the subkey where the rollback is located Write-Verbose "Making registry connection to 'Software' key" If ($registry.GetSubkeyNames() -contains "Software") { $subkey = $registry.OpenSubKey("Software",$True) Write-Verbose "Making registry connection to 'Policies' key" If ($subkey.GetSubKeyNames() -contains "Policies") { $subkey = $subkey.OpenSubKey("Policies",$True) Write-Verbose "Making registry connection to 'Microsoft' key" If ($subkey.GetSubKeyNames() -contains "Microsoft") { $subkey = $subkey.OpenSubKey("Microsoft",$True) Write-Verbose "Making registry connection to 'Windows' key" If ($subkey.GetSubKeyNames() -contains "Windows") { $subkey = $subkey.OpenSubKey("Windows",$True) Write-Verbose "Making registry connection to 'Installer' key" If ($subkey.GetSubKeyNames() -contains "Installer") { $subkey = $subkey.OpenSubKey("Installer",$True) Write-Verbose "Checking to see if 'DisableRollback' value name exists" If ($subkey.GetValueNames() -contains "DisableRollback") { #Get the value of DisableRollback Write-Verbose "Retrieving value of 'DisableRollback'" $value = $subkey.GetValue('DisableRollback') Switch ($value) { 0 {$hash = @{Computer = $computer;Rollback = "Enabled"}} 1 {$hash = @{Computer = $computer;Rollback = "Disabled"}} Default {$hash = @{Computer = $computer;Rollback = "Unknown"}} } } Else { Write-Warning "$($computer): Missing 'DisableRollback' value name in registy!" $hash = @{Computer = $computer;Rollback = "Missing DisableRollback value name"} } } Else { Write-Warning "$($computer): Missing 'Installer' key in registy!" $hash = @{Computer = $computer;Rollback = "Missing Installer key"} } } Else { Write-Warning "$($computer): Missing 'Windows' key in registy!" $hash = @{Computer = $computer;Rollback = "Missing Windows key"} } } Else { Write-Warning "$($computer): Missing 'Microsoft' key in registy!" $hash = @{Computer = $computer;Rollback = "Missing Microsoft key"} } } Else { Write-Warning "$($computer): Missing 'Policies' key in registy!" $hash = @{Computer = $computer;Rollback = "Missing Policies key"} } } Else { Write-Warning "$($computer): Missing 'Software' key in registy!" $hash = @{Computer = $computer;Rollback = "Missing Software key"} } } Catch { Write-Warning "$($computer): $($Error[0])" $hash = @{Computer = $computer;Rollback = ($error[0].exception.innerexception.message -split "`n")[0]} } Finally { $object = New-Object PSObject -Property $hash $object.PSTypeNames.Insert(0,'RollbackStatus') Write-Output $object } } } } Function Disable-RollbackPolicy { <# .SYNOPSIS Disables the rollback policy on a local or remote computer .DESCRIPTION Disables the rollback policy on a local or remote computer .PARAMETER Computername The name of the computer or computers to disable the rollback policy on .PARAMETER Passthru Displays the returned object after disabling the policy. .NOTES Name: Disable-RollbackPolicy Author: Boe Prox DateCreated: 06/24/2011 Links: .EXAMPLE Disable-RollbackPolicy -Computername DC1 -Passthru Rollback Computer -------- -------- Disabled DC1 Description ----------- This disables the rollback policy on DC1 and returns the object showing that the policy has been disabled. .EXAMPLE Disable-RollbackPolicy -Computername DC1,server1,server2 -Passthru Rollback Computer -------- -------- Disabled DC1 Disabled server1 Disabled server2 Description ----------- This disables the rollback policy on DC1,server1 and server2 and returns the object showing that the policy has been disabled. #> [cmdletbinding( SupportsShouldProcess = 'True' )] Param ( [parameter()] [string[]]$Computername, [parameter()] [Switch]$Passthru ) Process { ForEach ($computer in $computername) { Try { #Make initial registry connection Write-Verbose "Making registry connection to remote computer" $registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine",$computer) If ($PScmdlet.ShouldProcess("$computer","Disable Rollback")) { #Connect to the subkey where the rollback is located Write-Verbose "Making registry connection to 'Software' key" If ($registry.GetSubkeyNames() -notcontains "Software") { $registry.CreateSubKey("Software") $subkey = $registry.OpenSubKey("Software",$True) } Else { $subkey = $registry.OpenSubKey("Software",$True) } Write-Verbose "Making registry connection to 'Policies' key" If ($subkey.GetSubKeyNames() -notcontains "Policies") { $subkey.CreateSubKey("Policies") $subkey = $subkey.OpenSubKey("Policies",$True) } Else { $subkey = $subkey.OpenSubKey("Policies",$True) } Write-Verbose "Making registry connection to 'Microsoft' key" If ($subkey.GetSubKeyNames() -notcontains "Microsoft") { $subkey.CreateSubKey("Microsoft") $subkey = $subkey.OpenSubKey("Microsoft",$True) } Else { $subkey = $subkey.OpenSubKey("Microsoft",$True) } Write-Verbose "Making registry connection to 'Windows' key" If ($subkey.GetSubKeyNames() -notcontains "Windows") { $subkey.CreateSubKey("Windows") $subkey = $subkey.OpenSubKey("Windows",$True) } Else { $subkey = $subkey.OpenSubKey("Windows",$True) } Write-Verbose "Making registry connection to 'Installer' key" If ($subkey.GetSubKeyNames() -notcontains "Installer") { $subkey.CreateSubKey("Installer") $subkey = $subkey.OpenSubKey("Installer",$True) } Else { $subkey = $subkey.OpenSubKey("Installer",$True) } Write-Verbose "Checking to see if 'DisableRollback' value name exists" If ($subkey.GetValueNames() -notcontains "DisableRollback") { Write-Verbose "Creating DisableRollback value name and setting to 1 (Disable)" $subkey.SetValue("DisableRollback","1","DWord") } Else { Write-Verbose "Disabling Rollback" $subkey.SetValue("DisableRollback","1","DWord") } If ($PSBoundParameters['Passthru']) { Write-Verbose "Retrieving value of 'DisableRollback'" $value = $subkey.GetValue('DisableRollback') Switch ($value) { 0 {$hash = @{Computer = $computer;Rollback = "Enabled"}} 1 {$hash = @{Computer = $computer;Rollback = "Disabled"}} Default {$hash = @{Computer = $computer;Rollback = "Unknown"}} } } } } Catch { Write-Warning "$($computer): $($Error[0])" } Finally { If ($PSBoundParameters['Passthru']) { $object = New-Object PSObject -Property $hash $object.PSTypeNames.Insert(0,'RollbackStatus') Write-Output $object } } } } } Function Enable-RollbackPolicy { <# .SYNOPSIS Enables the rollback policy on a local or remote computer .DESCRIPTION Enables the rollback policy on a local or remote computer .PARAMETER Computername Name of computer or computers to enable the rollback policy .PARAMETER Passthru Displays the object after enabling the rollback policy on a computer .NOTES Name: Enable-RollbackPolicy Author: Boe Prox DateCreated: 06/24/2011 Links: .EXAMPLE Enable-RollbackPolicy -Computername DC1 -Passthru Rollback Computer -------- -------- Enable DC1 Description ----------- This enables the rollback policy on DC1 and returns the object showing that the policy has been enabled. .EXAMPLE Enable-RollbackPolicy -Computername DC1,server1,server2 -Passthru Rollback Computer -------- -------- Enable DC1 Enable server1 Enable server2 Description ----------- This enables the rollback policy on DC1,server1 and server2 and returns the object showing that the policy has been enabled. #> [cmdletbinding( SupportsShouldProcess = 'True' )] Param ( [parameter()] [string[]]$Computername, [parameter()] [Switch]$Passthru ) Process { ForEach ($computer in $computername) { Try { #Make initial registry connection Write-Verbose "Making registry connection to remote computer" $registry = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey("LocalMachine",$computer) If ($PScmdlet.ShouldProcess("$computer","Enable Rollback")) { #Connect to the subkey where the rollback is located Write-Verbose "Making registry connection to 'Software' key" If ($registry.GetSubkeyNames() -notcontains "Software") { $registry.CreateSubKey("Software") $subkey = $registry.OpenSubKey("Software",$True) } Else { $subkey = $registry.OpenSubKey("Software",$True) } Write-Verbose "Making registry connection to 'Policies' key" If ($subkey.GetSubKeyNames() -notcontains "Policies") { $subkey.CreateSubKey("Policies") $subkey = $subkey.OpenSubKey("Policies",$True) } Else { $subkey = $subkey.OpenSubKey("Policies",$True) } Write-Verbose "Making registry connection to 'Microsoft' key" If ($subkey.GetSubKeyNames() -notcontains "Microsoft") { $subkey.CreateSubKey("Microsoft") $subkey = $subkey.OpenSubKey("Microsoft",$True) } Else { $subkey = $subkey.OpenSubKey("Microsoft",$True) } Write-Verbose "Making registry connection to 'Windows' key" If ($subkey.GetSubKeyNames() -notcontains "Windows") { $subkey.CreateSubKey("Windows") $subkey = $subkey.OpenSubKey("Windows",$True) } Else { $subkey = $subkey.OpenSubKey("Windows",$True) } Write-Verbose "Making registry connection to 'Installer' key" If ($subkey.GetSubKeyNames() -notcontains "Installer") { $subkey.CreateSubKey("Installer") $subkey = $subkey.OpenSubKey("Installer",$True) } Else { $subkey = $subkey.OpenSubKey("Installer",$True) } Write-Verbose "Checking to see if 'DisableRollback' value name exists" If ($subkey.GetValueNames() -notcontains "DisableRollback") { Write-Verbose "Creating DisableRollback value name and setting to 0 (Enable)" $subkey.SetValue("DisableRollback","0","DWord") } Else { Write-Verbose "Enabling Rollback" $subkey.SetValue("DisableRollback","0","DWord") } If ($PSBoundParameters['Passthru']) { Write-Verbose "Retrieving value of 'DisableRollback'" $value = $subkey.GetValue('DisableRollback') Switch ($value) { 0 {$hash = @{Computer = $computer;Rollback = "Enabled"}} 1 {$hash = @{Computer = $computer;Rollback = "Disabled"}} Default {$hash = @{Computer = $computer;Rollback = "Unknown"}} } } } } Catch { Write-Warning "$($computer): $($Error[0])" } Finally { If ($PSBoundParameters['Passthru']) { $object = New-Object PSObject -Property $hash $object.PSTypeNames.Insert(0,'RollbackStatus') Write-Output $object } } } } } Export-ModuleMember -Function Get-RollbackPolicy,Enable-RollbackPolicy,Disable-RollbackPolicy
Tagged: .net, Powershell, registry, rollback Image may be NSFW.
Clik here to view.
Clik here to view.
