Here is my automatic way of utilizing the registry whitelist work around mentioned on Microsoft's site.
Now, before I run my install command against a remote machine, I look at the MSI and extract the product code with Get-ProductCodeFromMSI, and then use Add-GuidToWhitelist to add each GUID to the list on that computer. Here's an example:
$guids = Get-ChildItem -Path D:\somefolder -filter "*.msi" -recurse | % {Get-ProductCodefromMSI $_.FullName}
Add-GUIDtoWhiteList -computername "SomeServer" -GUIDs $guids
Before doing that, each machine can be tested and repaired for the workaround using Test-SecureRepairPolicy and Repair-SecureRepairPolicy, respectively.
Get-ProductCodeFromMSI will require the DLL referenced to be placed somewhere and unblocked - this DLL can be retrieved from the Wix toolset.
Code for the functions I reference is here:
Function Test-SecureRepairPolicy{
param (
[Parameter(mandatory=$true,ValueFromPipelineByPropertyName=$true)][string[]]
# Specifies the computer name to connect to
$ComputerName
)
Process {
foreach ($Computer in $ComputerName)
{
#Open Remote Base
$reg=[microsoft.win32.registrykey]::OpenRemoteBaseKey('LocalMachine',$Computer)
#Get Windows key
$subkey = $reg.OpenSubKey("SOFTWARE\Policies\Microsoft\Windows")
$subkeynames = $subkey.GetSubKeyNames()
if (($subkeynames | Measure-Object).Count -lt 1){
return New-Object -type PSObject -Property @{
Success = $False
Note = "Can not open base key"
ComputerName = $Computer
}
}
if ($subkeynames -notcontains "Installer"){
return New-Object -type PSObject -Property @{
Success = $False
Note = "Can not locate installer subkey"
}
}
$subkey.Close();$subkey = $null
$subkey = $reg.OpenSubKey("SOFTWARE\Policies\Microsoft\Windows\Installer")
$subkeynames = $subkey.GetSubKeyNames()
if ($subkeynames -notcontains "SecureRepairWhitelist"){
return New-Object -type PSObject -Property @{
Success = $False
Note = "Can not locate repairlist subkey"
ComputerName = $Computer
}
}
$repairvalue = $subkey.GetValue("SecureRepairPolicy")
if ($repairvalue -ne 2){
return New-Object -type PSObject -Property @{
Success = $False
Note = "SecureRepairPolicy is incorrect"
ComputerName = $Computer
}
}
$subkey.Close();$subkey = $null;$reg.Close();$reg = $null
return New-Object -type PSObject -Property @{
Success = $True
Note = "SecureRepairPolicy structure is in place"
ComputerName = $Computer
}
}
}
}
Function Repair-SecureRepairPolicy{
param (
[Parameter(mandatory=$true,ValueFromPipelineByPropertyName=$true)][string[]]
# Specifies the computer name to connect to
$ComputerName
)
Begin{
Function Add-RemoteRegistryKey($Computer,$Parent,$Name){
$reg=[microsoft.win32.registrykey]::OpenRemoteBaseKey('LocalMachine',$Computer)
$subkey = $reg.OpenSubKey($Parent, $true)
$subkey.CreateSubKey($Name)
$subkey.Close();$subkey = $null;$reg.Close();$reg = $null
}
Function Add-InstallerKey($Computer){
Add-RemoteRegistryKey $Computer "SOFTWARE\Policies\Microsoft\Windows" "Installer"
}
Function Add-RepairPolicy($Computer){
$reg=[microsoft.win32.registrykey]::OpenRemoteBaseKey('LocalMachine',$Computer)
$subkey = $reg.OpenSubKey("SOFTWARE\Policies\Microsoft\Windows\Installer", $true)
$subkey.SetValue("SecureRepairPolicy",2, "DWORD")
$subkey.Close();$subkey = $null;$reg.Close();$reg = $null
}
Function Add-WhitelistKey($Computer){
Add-RemoteRegistryKey $Computer "SOFTWARE\Policies\Microsoft\Windows\Installer" "SecureRepairWhitelist"
}
}
Process {
foreach ($Computer in $ComputerName)
{
$audit = Test-SecureRepairPolicy $computer
if ($audit.Success){
Write-Output "Repair whitelist keys setup. No repair being performed."
}
else{
Write-Output "Repair whitelist keys not setup. Attempting to resolve"
if ($audit.Note -match "Can not open base key"){
Write-Error "Unable to open computer's registry key"
return
}
if ($audit.Note -match "Can not locate installer subkey"){
Add-Installerkey $Computer
Add-RepairPolicy $Computer
Add-WhitelistKey $Computer
}
if ($audit.Note -match "Can not locate repairlist subkey"){
Add-RepairPolicy $Computer
Add-WhitelistKey $Computer
}
if ($audit.Note -match "Can not locate repairlist subkey"){
Add-RepairPolicy $Computer
}
Write-Output "Showing new audit"
Test-SecureRepairPolicy $computer
}
}
}
}
Function Add-GUIDtoWhiteList{
param (
[Parameter(mandatory=$true,ValueFromPipelineByPropertyName=$true)][string[]]
# Specifies the computer name to connect to
$ComputerName,
[Parameter(mandatory=$true)][string[]]
# Specifies the GUID(s) to add.
$GUIDs
)
Process {
foreach ($Computer in $ComputerName)
{
#Open Remote Base
$reg=[microsoft.win32.registrykey]::OpenRemoteBaseKey('LocalMachine',$Computer)
$subkey = $reg.OpenSubKey("SOFTWARE\Policies\Microsoft\Windows\Installer\SecureRepairWhitelist", $true)
foreach($GUID in $GUIDs){
$subkey.SetValue($GUID,"", "String")
}
$subkey.Close();$subkey = $null;$reg.Close();$reg = $null
}
}
}
Function Get-ProductCodefromMSI ($msi){
[Reflection.Assembly]::LoadFrom("D:\scripts\lib\Microsoft.Deployment.WindowsInstaller.dll") | out-null
(New-Object -TypeName Microsoft.Deployment.WindowsInstaller.Database -ArgumentList $msi).ExecuteQuery("SELECT Value FROM Property WHERE Property = 'ProductCode'")
}