private/Start-DscUpdate.ps1
function Start-DscUpdate { [CmdletBinding()] param ( [Parameter(Mandatory)] [psobject]$ComputerName, [PSCredential]$Credential, [PSCredential]$PSDscRunAsCredential, [Parameter(ValueFromPipelineByPropertyName)] [Alias("Name", "KBUpdate", "Id")] [string]$HotfixId, [Alias("Path")] [string]$FilePath, [string]$RepositoryPath, [Parameter(ValueFromPipelineByPropertyName)] [Alias("UpdateId")] [string]$Guid, [Parameter(ValueFromPipelineByPropertyName)] [string]$Title, [string]$ArgumentList, [Parameter(ValueFromPipeline)] [pscustomobject[]]$InputObject, [switch]$AllNeeded, [switch]$NoMultithreading, [switch]$EnableException, [bool]$IsLocalHost, [string]$VerbosePreference, [string]$ScanFilePath, [string[]]$ModulePath ) begin { if ($PSVersionTable.PSVersion.Major -gt 5) { $null = Import-Module -UseWindowsPowerShell PSDesiredStateConfiguration -MaximumVersion 1.1 *>$null } # No idea why this sometimes happens if ($ComputerName -is [hashtable]) { $hashtable = $ComputerName.PsObject.Copy() $null = Remove-Variable -Name ComputerName foreach ($key in $hashtable.keys) { Set-Variable -Name $key -Value $hashtable[$key] } } # load up if a job foreach ($path in $ModulePath) { $null = Import-Module $path 4>$null } if ($EnableException) { $PSDefaultParameterValues["*:EnableException"] = $true } else { $PSDefaultParameterValues["*:EnableException"] = $false } if ($ComputerName.ComputerName) { $hostname = $ComputerName.ComputerName } else { $hostname = $ComputerName } if ($AllNeeded) { if ($ScanFilePath) { $InputObject = Get-KbNeededUpdate -ComputerName $ComputerName -ScanFilePath $ScanFilePath -Force } else { $InputObject = Get-KbNeededUpdate -ComputerName $ComputerName } } if ($HotfixId -and -not $InputObject.Link) { Write-PSFMessage -Level Verbose -Message "Hotfix detected without InputObject, getting info" $InputObject += Get-KbUpdate -HotfixId $HotfixId -ComputerName $ComputerName } $script:ModuleRoot = Split-Path -Path $($ModulePath | Select-Object -Last 1) # null out a couple things to be safe $remotefileexists = $programhome = $remotesession = $null # Method is DSC if ($PSDefaultParameterValues["Invoke-KbCommand:ComputerName"]) { $null = $PSDefaultParameterValues.Remove("Invoke-KbCommand:ComputerName") } $PSDefaultParameterValues["Invoke-KbCommand:ComputerName"] = $ComputerName if ($Credential) { $PSDefaultParameterValues["Invoke-KbCommand:Credential"] = $Credential } if ($FilePath) { if ($ComputerName.IsLocalHost) { $InputObject += Get-ChildItem -Path $FilePath } else { $InputObject += Invoke-KbCommand -ScriptBlock { Get-ChildItem -Path $FilePath } } } if ($IsLocalHost) { # a lot of the file copy work will be done in the $home dir $programhome = Invoke-KbCommand -ScriptBlock { $home } } else { Write-PSFMessage -Level Verbose -Message "Adding $hostname to PSDefaultParameterValues for Invoke-KbCommand:ComputerName" $PSDefaultParameterValues["Invoke-KbCommand:ComputerName"] = $ComputerName Write-PSFMessage -Level Verbose -Message "Initializing remote session to $hostname and also getting the remote home directory" $programhome = Invoke-KbCommand -ScriptBlock { $home } if (-not $remotesession) { $remotesession = Get-PSSession -ComputerName $ComputerName -Verbose | Where-Object { $PsItem.Availability -eq 'Available' -and ($PsItem.Name -match 'WinRM' -or $PsItem.Name -match 'Runspace') } | Select-Object -First 1 } if (-not $remotesession) { $remotesession = Get-PSSession -ComputerName $ComputerName | Where-Object { $PsItem.Availability -eq 'Available' } | Select-Object -First 1 } if (-not $remotesession) { Stop-PSFFunction -Message "Session for $hostname can't be found or no runspaces are available. Please file an issue on the GitHub repo at https://github.com/potatoqualitee/kbupdate/issues" -Continue } } # fix for SYSTEM which doesn't have a downloads directory by default Write-PSFMessage -Level Verbose -Message "Checking for home downloads directory" Invoke-KbCommand -ScriptBlock { if (-not (Test-Path -Path "$home\Downloads")) { Write-Warning "Creating Downloads directory at $home\Downloads" $null = New-Item -ItemType Directory -Force -Path "$home\Downloads" } } $hasxhotfix = Invoke-KbCommand -ScriptBlock { Get-Module -Name xWindowsUpdate -ListAvailable -ErrorAction Ignore | Where-Object { $PSItem.Path -match "3.0.0" -and $PSItem.Path -like "$env:ProgramFiles*" } } if (-not $hasxhotfix) { try { # Copy xWindowsUpdate to Program Files. The module is pretty much required to be in the PS Modules directory. $oldpref = $ProgressPreference $ProgressPreference = "SilentlyContinue" $programfiles = Invoke-KbCommand -ScriptBlock { $env:ProgramFiles } if ($IsLocalHost) { Write-PSFMessage -Level Verbose -Message "Copying xWindowsUpdate to $hostname (local to $programfiles\WindowsPowerShell\Modules\xWindowsUpdate)" $null = Copy-Item -Path "$script:ModuleRoot\library\xWindowsUpdate" -Destination "$programfiles\WindowsPowerShell\Modules" -Recurse -Force -ErrorAction Stop } else { Write-PSFMessage -Level Verbose -Message "Copying xWindowsUpdate to $hostname (remote to $programfiles\WindowsPowerShell\Modules\xWindowsUpdate)" $null = Copy-Item -Path "$script:ModuleRoot\library\xWindowsUpdate" -Destination "$programfiles\WindowsPowerShell\Modules" -ToSession $remotesession -Recurse -Force -ErrorAction Stop } $ProgressPreference = $oldpref } catch { Stop-PSFFunction -Message "Couldn't auto-install xHotfix on $hostname. Please Install-Module xWindowsUpdate on $hostname to continue." -Continue } } $hasxdsc = Invoke-KbCommand -ScriptBlock { Get-Module -Name xPSDesiredStateConfiguration -ListAvailable -ErrorAction Ignore | Where-Object { $PSItem.Path -match "9.2.0" -and $PSItem.Path -like "$env:ProgramFiles*" } } if (-not $hasxdsc) { try { Write-PSFMessage -Level Verbose -Message "Adding xPSDesiredStateConfiguration to $hostname" # Copy xWindowsUpdate to Program Files. The module is pretty much required to be in the PS Modules directory. $oldpref = $ProgressPreference $ProgressPreference = "SilentlyContinue" $programfiles = Invoke-KbCommand -ScriptBlock { $env:ProgramFiles } if ($IsLocalHost) { Write-PSFMessage -Level Verbose -Message "Copying xPSDesiredStateConfiguration to $hostname (local to $programfiles\WindowsPowerShell\Modules\xPSDesiredStateConfiguration)" $null = Copy-Item -Path "$script:ModuleRoot\library\xPSDesiredStateConfiguration" -Destination "$programfiles\WindowsPowerShell\Modules" -Recurse -Force -ErrorAction Stop } else { Write-PSFMessage -Level Verbose -Message "Copying xPSDesiredStateConfiguration to $hostname (remote)" $null = Copy-Item -Path "$script:ModuleRoot\library\xPSDesiredStateConfiguration" -Destination "$programfiles\WindowsPowerShell\Modules" -ToSession $remotesession -Recurse -Force -ErrorAction Stop } $ProgressPreference = $oldpref } catch { Stop-PSFFunction -Message "Couldn't auto-install newer DSC resources on $hostname. Please Install-Module xPSDesiredStateConfiguration version 9.2.0 on $hostname to continue." -Continue } } } process { if ($FilePath -and -not $InputObject) { Write-PSFMessage -Level Verbose -Message "Setting InputObject to $FilePath" $InputObject = $FilePath } if (-not $InputObject) { Write-PSFMessage -Level Verbose -Message "Nothing to install on $hostname, moving on" } foreach ($object in $InputObject) { if ($object.Link -and $RepositoryPath) { try { foreach ($item in $object.Link) { $filename = Split-Path -Path $item -Leaf Write-PSFMessage -Level Verbose -Message "Adding $filename to $RepositoryPath" $repofile = Join-Path -Path $RepositoryPath -ChildPath $filename if (-not (Test-Path -Path $repofile)) { Write-PSFMessage -Level Verbose -Message "File does not exist, trying to download $item to $repofile" $null = Save-KbUpdate -Link $item -Path $RepositoryPath } if ($remotehome) { $null = Copy-Item -Path $repofile -Destination "$remotehome\Downloads\$filename" -ToSession $remotesession -Recurse -Force -ErrorAction Stop } else { $null = Copy-Item -Path $repofile -Destination "$home\Downloads" -Recurse -Force -ErrorAction Stop } } } catch { if (-not $hostname) { $hostname = $object.ComputerName } Stop-PSFFunction -Message "Couldn't copy $filename from repo to $hostname." -ErrorRecord $PSItem -Continue } } if (-not $remotefileexists) { if ($FilePath) { # try really hard to find it locally $updatefile = Get-ChildItem -Path $FilePath -ErrorAction SilentlyContinue if (-not $updatefile) { Write-PSFMessage -Level Verbose -Message "Update file not found, try in Downloads" $filename = Split-Path -Path $FilePath -Leaf $updatefile = Get-ChildItem -Path "$home\Downloads\$filename" -ErrorAction SilentlyContinue } } if (-not $updatefile) { if ($HotfixId) { $Pattern = $HotfixId } elseif ($Guid) { $Pattern = $Guid } elseif ($object.UpdateId) { $Pattern = $object.UpdateId } elseif ($object.Id) { $Pattern = $object.Id } elseif ($object.Id) { $Pattern = $object.Id } elseif ($filename) { $number = "$filename".Split('KB') | Select-Object -Last 1 $number = $number.Split(" ") | Select-Object -First 1 $Pattern = "KB$number".Trim().Replace(")", "") } elseif ($FilePath) { $number = "$(Split-Path $FilePath -Leaf)".Split('KB') | Select-Object -Last 1 $number = $number.Split(" ") | Select-Object -First 1 $Pattern = "KB$number".Trim().Replace(")", "") } # try to automatically download it for them if (-not $object -and $Pattern) { Write-Message -Level Verbose -Message "No object and a pattern" $object = Get-KbUpdate -ComputerName $ComputerName -Pattern $Pattern | Where-Object { $PSItem.Link -and $PSItem.Title -match $Pattern } } # note to reader: if this picks the wrong one, please download the required file manually. if ($object.Link) { if ($object.Link -match 'x64') { $file = $object | Where-Object Link -match 'x64' | Select-Object -ExpandProperty Link -Last 1 | Split-Path -Leaf } else { $file = Split-Path $object.Link -Leaf | Select-Object -Last 1 } } else { Stop-PSFFunction -Message "Could not find file on $hostname and couldn't find it online. Try piping in exactly what you'd like from Get-KbUpdate." -Continue } if ((Test-Path -Path "$home\Downloads\$file")) { $updatefile = Get-ChildItem -Path "$home\Downloads\$file" } else { Write-PSFMessage -Level Verbose -Message "File not detected on $hostname, downloading now to $home\Downloads and copying to remote computer" $warnatbottom = $true # fix for SYSTEM which doesn't have a downloads directory by default Write-PSFMessage -Level Verbose -Message "Checking for home downloads directory" if (-not (Test-Path -Path "$home\Downloads")) { Write-PSFMessage -Level Warning -Message "Creating Downloads directory at $home\Downloads" $null = New-Item -ItemType Directory -Force -Path "$home\Downloads" } $updatefile = $object | Select-Object -First 1 | Save-KbUpdate -Path "$home\Downloads" } } if (-not $FilePath) { $FilePath = "$programhome\Downloads\$(Split-Path -Leaf $updateFile)" } if ($IsLocalHost) { $remotefile = $updatefile } else { $remotefile = "$programhome\Downloads\$(Split-Path -Leaf $updateFile)" } # copy over to destination server unless # it's local or it's on a network share if (-not "$($FilePath)".StartsWith("\\") -and -not $IsLocalHost) { Write-PSFMessage -Level Verbose -Message "Update is not located on a file server and not local, copying over the remote server" try { $exists = Invoke-KbCommand -ComputerName $ComputerName -ArgumentList $remotefile -ScriptBlock { Get-ChildItem -Path $args -ErrorAction SilentlyContinue } if (-not $exists) { $null = Copy-Item -Path $updatefile -Destination $remotefile -ToSession $remotesession -ErrorAction Stop $deleteremotefile = $remotefile } } catch { $null = Invoke-KbCommand -ComputerName $ComputerName -ArgumentList $remotefile -ScriptBlock { Remove-Item $args -Force -ErrorAction SilentlyContinue } try { Write-PSFMessage -Level Warning -Message "Copy failed, trying again" $null = Copy-Item -Path $updatefile -Destination $remotefile -ToSession $remotesession -ErrorAction Stop $deleteremotefile = $remotefile } catch { $null = Invoke-KbCommand -ComputerName $ComputerName -ArgumentList $remotefile -ScriptBlock { Remove-Item $args -Force -ErrorAction SilentlyContinue } Stop-PSFFunction -Message "Could not copy $updatefile to $remotefile" -ErrorRecord $PSItem -Continue } } } } # if user doesnt add kb, try to find it for them from the provided filename if (-not $HotfixId) { $HotfixId = $FilePath.ToUpper() -split "\-" | Where-Object { $psitem.Startswith("KB") } } # i probably need to fix some logic but until then, check a few things if ($IsLocalHost) { if ($updatefile) { $FilePath = $updatefile } else { $updatefile = Get-ChildItem -Path $FilePath } if (-not $Title) { Write-PSFMessage -Level Verbose -Message "Trying to get Title from $($updatefile.FullName)" $Title = $updatefile.VersionInfo.ProductName } } elseif ($remotefile) { $FilePath = $remotefile } if ("$FilePath".EndsWith("msi")) { Write-PSFMessage -Level Verbose -Message "It's an msi" if ($ComputerName.IsLocalhost) { try { $msi = New-Object -ComObject WindowsInstaller.Installer $info = $msi.SummaryInformation($FilePath) $title = $info.Property(2) $guid = "$guid".TrimStart("{").TrimEnd("}") } catch { # don't care } } else { $installerinfo = Invoke-KbCommand -ScriptBlock { $FilePath = $args[0] $msi = New-Object -ComObject WindowsInstaller.Installer $info = $msi.SummaryInformation($FilePath) [pscustombject]@{ Title = $info.Property(2) } } -ArgumentList $FilePath -ErrorAction Ignore $title = $installerinfo.Title } Write-PSFMessage -Level Verbose -Message "FilePath $FilePath" Write-PSFMessage -Level Verbose -Message "Guid $guid" Write-PSFMessage -Level Verbose -Message "Title $title" } if ("$FilePath".EndsWith("exe") -or $PSBoundParameters.ArgumentList) { if ($PSBoundParameters.ArgumentList) { Write-PSFMessage -Level Verbose -Message "ArgumentList is $ArgumentList" } if ("$FilePath".EndsWith("exe")) { Write-PSFMessage -Level Verbose -Message "It's an exe" } if (-not $ArgumentList -and $FilePath -match "sql") { $ArgumentList = "/action=patch /AllInstances /quiet /IAcceptSQLServerLicenseTerms" } elseif (-not $ArgumentList) { # Setting a default argumentlist that hopefully works for most things? $ArgumentList = "/install /quiet /notrestart" } if (-not $Guid) { if ($object) { if ($object.UpdateId) { $Guid = $object.UpdateId } else { $Guid = $object.Guid } if (-not $Title) { $Title = $object.Title } } else { try { $hotfixid = $guid = $null Write-PSFMessage -Level Verbose -Message "Trying to get Title from $($updatefile.FullName)" $updatefile = Get-ChildItem -Path $updatefile.FullName -ErrorAction SilentlyContinue if (-not $Title) { $Title = $updatefile.VersionInfo.ProductName } Write-PSFMessage -Level Verbose -Message "Trying to get GUID from $($updatefile.FullName)" <# The reason you want to find the GUID is to save time, mostly, I guess? It saves time because it won't even attempt the install if there are GUID matches in the registry. If you pass a fake but compliant GUID, it attempts the install and fails, no big deal. Overall, it just seems like a good idea to get a GUID if it's required. #> <# It's better to just read from memory but I can't get this to work $cab = New-Object Microsoft.Deployment.Compression.Cab.Cabinfo "C:\path\path.exe" $file = New-Object Microsoft.Deployment.Compression.Cab.CabFileInfo($cab, "0") $content = $file.OpenRead() #> $cab = New-Object Microsoft.Deployment.Compression.Cab.Cabinfo $updatefile.FullName $files = $cab.GetFiles("*") $index = $files | Where-Object Name -eq 0 if (-not $index) { $index = $files | Where-Object Name -match "none.xml| ParameterInfo.xml" } $temp = Get-PSFPath -Name Temp $indexfilename = $index.Name $xmlfile = Join-Path -Path $temp -ChildPath "$($updatefile.BaseName).xml" $null = $cab.UnpackFile($indexfilename, $xmlfile) if ((Test-Path -Path $xmlfile)) { $xml = [xml](Get-Content -Path $xmlfile) $tempguid = $xml.BurnManifest.Registration.Id } if (-not $tempguid -and $xml.MsiPatch.PatchGUID) { $tempguid = $xml.MsiPatch.PatchGUID } if (-not $tempguid -and $xml.Setup.Items.Patches.MSP.PatchCode) { $tempguid = $xml.Setup.Items.Patches.MSP.PatchCode } Get-ChildItem -Path $xmlfile -ErrorAction SilentlyContinue | Remove-Item -Confirm:$false -ErrorAction SilentlyContinue # if we can't find the guid, use one that we know # is valid but not associated with any hotfix if (-not $tempguid) { $tempguid = "DAADB00F-DAAD-B00F-B00F-DAADB00FB00F" } $guid = ([guid]$tempguid).Guid } catch { $guid = "DAADB00F-DAAD-B00F-B00F-DAADB00FB00F" } Write-PSFMessage -Level Verbose -Message "GUID is $guid" Write-PSFMessage -Level Verbose -Message "Title is $title" } } # this takes care of things like SQL Server updates $hotfix = @{ Name = "xPackage" ModuleName = @{ ModuleName = "xPSDesiredStateConfiguration" ModuleVersion = "9.2.0" } Property = @{ Ensure = "Present" ProductId = $Guid Name = $Title Path = $FilePath Arguments = $ArgumentList ReturnCode = 0, 3010 } } $scriptblock = @" Configuration DscWithoutWinRm { Import-DscResource -ModuleName PSDesiredStateConfiguration Import-DscResource -ModuleName xPSDesiredStateConfiguration -ModuleVersion 9.2.0 node localhost { xPackage "xPackage" { Ensure = "Present" ProductId = "$guid" Name = "$title" Path = "$FilePath" Arguments = "$ArgumentList" ReturnCode = 0, 3010 } } } DscWithoutWinRm "@ $dsc = [scriptblock]::Create($scriptblock) } elseif ("$FilePath".EndsWith("cab")) { Write-PSFMessage -Level Verbose -Message "It's a cab file" Write-PSFMessage -Level Verbose -Message "ArgumentList is $ArgumentList" $basename = Split-Path -Path $FilePath -Leaf $logfile = Join-Path -Path $env:windir -ChildPath ($basename + ".log") $hotfix = @{ Name = "xWindowsPackageCab" ModuleName = @{ ModuleName = "xPSDesiredStateConfiguration" ModuleVersion = "9.2.0" } Property = @{ Ensure = "Present" Name = $basename SourcePath = $FilePath # adding a directory will add other msus in the dir LogPath = $logfile } } $scriptblock = @" Configuration DscWithoutWinRm { Import-DscResource -ModuleName PSDesiredStateConfiguration Import-DscResource -ModuleName xPSDesiredStateConfiguration -ModuleVersion 9.2.0 node localhost { xWindowsPackageCab xWindowsPackageCab { Ensure = "Present" Name = "$basename" SourcePath = "$FilePath" # adding a directory will add other msus in the dir LogPath = "$logfile" } } } DscWithoutWinRm "@ $dsc = [scriptblock]::Create($scriptblock) } else { Write-PSFMessage -Level Verbose -Message "It's a WSU file" Write-PSFMessage -Level Verbose -Message "ArgumentList is $ArgumentList" # this takes care of WSU files $hotfix = @{ Name = "xHotFix" ModuleName = @{ ModuleName = "xWindowsUpdate" ModuleVersion = "3.0.0" } Property = @{ Ensure = "Present" Id = $HotfixId Path = $FilePath } } if ($PSDscRunAsCredential) { $hotfix.Property.PSDscRunAsCredential = $PSDscRunAsCredential $scriptblock = @" Configuration DscWithoutWinRm { Import-DscResource -ModuleName PSDesiredStateConfiguration Import-DscResource -ModuleName xWindowsUpdate -ModuleVersion 3.0.0 node localhost { xHotFix xHotFix { Ensure = "Present" Id = "$HotfixId" Path = "$FilePath" PSDscRunAsCredential = $PSDscRunAsCredential } } } DscWithoutWinRm "@ $dsc = [scriptblock]::Create($scriptblock) } else { $scriptblock = @" Configuration DscWithoutWinRm { Import-DscResource -ModuleName PSDesiredStateConfiguration Import-DscResource -ModuleName xWindowsUpdate -ModuleVersion 3.0.0 node localhost { xHotFix xHotFix { Ensure = "Present" Id = "$HotfixId" Path = "$FilePath" } } } DscWithoutWinRm "@ $dsc = [scriptblock]::Create($scriptblock) } } try { $parms = @{ ArgumentList = $hotfix, $VerbosePreference, $FileName EnableException = $true WarningAction = "SilentlyContinue" WarningVariable = "dscwarnings" } $null = Invoke-KbCommand @parms -ScriptBlock { param ( $Hotfix, $VerbosePreference, $ManualFileName ) if ($PSVersionTable.PSVersion.Major -gt 5) { Import-Module -UseWindowsPowerShell PSDesiredStateConfiguration -MaximumVersion 1.1 *>$null } else { Import-Module PSDesiredStateConfiguration 4>$null } Import-Module xPSDesiredStateConfiguration -RequiredVersion 9.2.0 4>$null Import-Module xWindowsUpdate -RequiredVersion 3.0.0 4>$null $PSDefaultParameterValues.Remove("Invoke-WebRequest:ErrorAction") $PSDefaultParameterValues['*:ErrorAction'] = 'SilentlyContinue' $PSDefaultParameterValues['Invoke-DscResource:WarningAction'] = 'SilentlyContinue' $ErrorActionPreference = "Stop" $oldpref = $ProgressPreference $ProgressPreference = "SilentlyContinue" if (-not (Get-Command Invoke-DscResource)) { throw "Invoke-DscResource not found on $env:ComputerName" } $null = Import-Module xWindowsUpdate -Force 4>$null $hotfixpath = $hotfix.property.path if (-not $hotfixpath) { $hotfixpath = $hotfix.property.sourcepath } $hotfixnameid = $hotfix.property.name if (-not $hotfixnameid) { $hotfixnameid = $hotfix.property.id } Write-Verbose -Message "Installing $hotfixpath" # https://martin77s.wordpress.com/2017/03/01/using-dsc-with-the-winrm-service-disabled/ if (-not (Test-WSMan -ErrorAction Ignore)) { Write-Verbose -Message "Invoke-DscResource is not available on this system because remoting isn't enabled. Using Invoke-CimMethod." $workaround = $true Push-Location -Path $env:temp $null = Invoke-Command -ScriptBlock $dsc $mofpath = Resolve-Path -Path ".\DscWithoutWinRm\localhost.mof" $configData = [byte[]][System.IO.File]::ReadAllBytes($mofpath) Pop-Location } else { Write-Verbose -Message "DSC appears to be available on this system. Using Invoke-DscResource." $workaround = $false } try { $ProgressPreference = "SilentlyContinue" if ($workaround) { $parms = @{ Namespace = "root/Microsoft/Windows/DesiredStateConfiguration" ClassName = "MSFT_DSCLocalConfigurationManager" Method = "TestConfiguration" Arguments = @{ ConfigurationData = $configData Force = $true } } $testresource = Invoke-CimMethod @parms 4>$null } else { $testresource = Invoke-DscResource @hotfix -Method Test 4>$null } if (-not $testresource) { if ($workaround) { $parms = @{ Namespace = "root/Microsoft/Windows/DesiredStateConfiguration" ClassName = "MSFT_DSCLocalConfigurationManager" Method = "SendConfigurationApply" Arguments = @{ ConfigurationData = $configData Force = $true } } $msgs = Invoke-CimMethod @parms 4>&1 } else { $msgs = Invoke-DscResource @hotfix -Method Set -ErrorAction Stop 4>&1 } } if ($msgs) { foreach ($msg in $msgs) { # too many extra spaces, baw while ("$msg" -match " ") { $msg = "$msg" -replace " ", " " } $msg | Write-Verbose } } $ProgressPreference = $oldpref } catch { $message = "$_".TrimStart().TrimEnd().Trim() # Unsure how to figure out params, try another way if ($message -match "The return code 1 was not expected.") { try { if (-not $workaround) { Write-Verbose -Message "Retrying install with /quit parameter" $hotfix.Property.Arguments = "/quiet" $msgs = Invoke-DscResource @hotfix -Method Set -ErrorAction Stop 4>&1 } if ($msgs) { foreach ($msg in $msgs) { # too many extra spaces, baw while ("$msg" -match " ") { $msg = "$msg" -replace " ", " " } $msg | Write-Verbose } } } catch { $message = "$_".TrimStart().TrimEnd().Trim() } } switch ($message) { # some things can be ignored { $message -match "Serialized XML is nested too deeply" -or $message -match "Name does not match package details" } { $null = 1 } { $message -match "2359302" } { throw "Error 2359302: update is already installed on $env:ComputerName" } { $message -match "could not be started" } { throw "The install coult not initiate. The $($hotfix.Property.Path) on $env:ComputerName may be corrupt or only partially downloaded. Delete it and try again." } { $message -match "2042429437" } { throw "Error -2042429437. Configuration is likely not correct. The requested features may not be installed or features are already at a higher patch level." } { $message -match "2068709375" } { throw "Error -2068709375. The exit code suggests that something is corrupt. See if this tutorial helps: http://www.sqlcoffee.com/Tips0026.htm" } { $message -match "2067919934" } { throw "Error -2067919934 You likely need to reboot $env:ComputerName." } { $message -match "2147942402" } { throw "System can't find the file specified for some reason." } { $message -match "2149842967" } { throw "Error 2149842967 - Update is probably not applicable or already installed. $message" } default { throw $message } } } } if ($dscwarnings) { foreach ($warning in $dscwarnings) { # too many extra spaces, baw while ("$warning" -match " ") { $warning = "$warning" -replace " ", " " } Write-PSFMessage -Level Warning -Message $warning } } if ($deleteremotefile) { Write-PSFMessage -Level Verbose -Message "Deleting $deleteremotefile" $null = Invoke-KbCommand -ComputerName $ComputerName -ArgumentList $deleteremotefile -ScriptBlock { Get-ChildItem -ErrorAction SilentlyContinue $args | Remove-Item -Force -ErrorAction SilentlyContinue -Confirm:$false } } Write-PSFMessage -Level Verbose -Message "Finished installing, checking status" if ($hotfix.property.id) { $exists = Get-KbInstalledSoftware -ComputerName $ComputerName -Pattern $hotfix.property.id -IncludeHidden } if ($exists.Summary -match "restart") { # The summary is just too long $status = "Install successful. This update requires a restart." } else { $status = "Install successful" } if ($HotfixId) { $id = $HotfixId } else { $id = $guid } if ($id -eq "DAADB00F-DAAD-B00F-B00F-DAADB00FB00F") { $id = $null } if ($object.Title) { $filetitle = $object.Title } elseif ($exists.Title) { $filetitle = $exists.Title } else { $filetitle = $updatefile.VersionInfo.ProductName } if (-not $filetitle) { $filetitle = $Title } if ($message) { $status = "sucks" } [pscustomobject]@{ ComputerName = $hostname Title = $filetitle ID = $id Status = $status FileName = $updatefile.Name } } catch { if ("$PSItem" -match "Serialized XML is nested too deeply") { Write-PSFMessage -Level Verbose -Message "Serialized XML is nested too deeply. Forcing output." if ($hotfix.property.id) { $exists = Get-KbInstalledSoftware -ComputerName $ComputerName -Pattern $hotfix.property.id -IncludeHidden } if ($exists.Summary -match "restart") { $status = "This update requires a restart" } else { $status = "Install successful" } if ($HotfixId) { $id = $HotfixId } else { $id = $guid } if ($id -eq "DAADB00F-DAAD-B00F-B00F-DAADB00FB00F") { $id = $null } if ($object.Title) { $filetitle = $object.Title } elseif ($exists.Title) { $filetitle = $exists.Title } else { $filetitle = $updatefile.VersionInfo.ProductName } if (-not $filetitle) { $filetitle = $Title } [pscustomobject]@{ ComputerName = $hostname Title = $filetitle ID = $id Status = $Status FileName = $updatefile.Name } } elseif ("$PSItem" -match "find message text") { Write-PSFMessage -Level Verbose -Message "The system cannot find message text for message number 0x%1 in the message file for %2. Checking to see if it was actually installed." if ($hotfix.property.id) { $exists = Get-KbInstalledSoftware -ComputerName $ComputerName -Pattern $hotfix.property.id -IncludeHidden } if (-not $exists) { Stop-PSFFunction -Message "Failure on $hostname" -ErrorRecord $PSitem -Continue -EnableException:$EnableException } else { $status = "This update requires a restart" } if ($HotfixId) { $id = $HotfixId } else { $id = $guid } if ($id -eq "DAADB00F-DAAD-B00F-B00F-DAADB00FB00F") { $id = $null } if ($object.Title) { $filetitle = $object.Title } else { $filetitle = $updatefile.VersionInfo.ProductName } if (-not $filetitle) { $filetitle = $Title } [pscustomobject]@{ ComputerName = $hostname Title = $filetitle ID = $id Status = $Status FileName = $updatefile.Name } } else { Pop-Location Stop-PSFFunction -Message "Failure on $hostname" -ErrorRecord $PSitem -Continue -EnableException:$EnableException } } } if ($warnatbottom) { Write-PSFMessage -Level Output -Message "Downloaded files may still exist on your local drive and other servers as well, in the Downloads directory." Write-PSFMessage -Level Output -Message "If you ran this as SYSTEM, the downloads will be in windows\system32\config\systemprofile." } } } # SIG # Begin signature block # MIIjYAYJKoZIhvcNAQcCoIIjUTCCI00CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBkwr32OMojx4qd # BmAahTEeEZ4m8e+3yYjVhodtCBThv6CCHVkwggUaMIIEAqADAgECAhADBbuGIbCh # Y1+/3q4SBOdtMA0GCSqGSIb3DQEBCwUAMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNV # BAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EwHhcN # MjAwNTEyMDAwMDAwWhcNMjMwNjA4MTIwMDAwWjBXMQswCQYDVQQGEwJVUzERMA8G # A1UECBMIVmlyZ2luaWExDzANBgNVBAcTBlZpZW5uYTERMA8GA1UEChMIZGJhdG9v # bHMxETAPBgNVBAMTCGRiYXRvb2xzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB # CgKCAQEAvL9je6vjv74IAbaY5rXqHxaNeNJO9yV0ObDg+kC844Io2vrHKGD8U5hU # iJp6rY32RVprnAFrA4jFVa6P+sho7F5iSVAO6A+QZTHQCn7oquOefGATo43NAadz # W2OWRro3QprMPZah0QFYpej9WaQL9w/08lVaugIw7CWPsa0S/YjHPGKQ+bYgI/kr # EUrk+asD7lvNwckR6pGieWAyf0fNmSoevQBTV6Cd8QiUfj+/qWvLW3UoEX9ucOGX # 2D8vSJxL7JyEVWTHg447hr6q9PzGq+91CO/c9DWFvNMjf+1c5a71fEZ54h1mNom/ # XoWZYoKeWhKnVdv1xVT1eEimibPEfQIDAQABo4IBxTCCAcEwHwYDVR0jBBgwFoAU # WsS5eyoKo6XqcQPAYPkt9mV1DlgwHQYDVR0OBBYEFPDAoPu2A4BDTvsJ193ferHL # 454iMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzB3BgNVHR8E # cDBuMDWgM6Axhi9odHRwOi8vY3JsMy5kaWdpY2VydC5jb20vc2hhMi1hc3N1cmVk # LWNzLWcxLmNybDA1oDOgMYYvaHR0cDovL2NybDQuZGlnaWNlcnQuY29tL3NoYTIt # YXNzdXJlZC1jcy1nMS5jcmwwTAYDVR0gBEUwQzA3BglghkgBhv1sAwEwKjAoBggr # BgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAIBgZngQwBBAEw # gYQGCCsGAQUFBwEBBHgwdjAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNl # cnQuY29tME4GCCsGAQUFBzAChkJodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20v # RGlnaUNlcnRTSEEyQXNzdXJlZElEQ29kZVNpZ25pbmdDQS5jcnQwDAYDVR0TAQH/ # BAIwADANBgkqhkiG9w0BAQsFAAOCAQEAj835cJUMH9Y2pBKspjznNJwcYmOxeBcH # Ji+yK0y4bm+j44OGWH4gu/QJM+WjZajvkydJKoJZH5zrHI3ykM8w8HGbYS1WZfN4 # oMwi51jKPGZPw9neGS2PXrBcKjzb7rlQ6x74Iex+gyf8z1ZuRDitLJY09FEOh0BM # LaLh+UvJ66ghmfIyjP/g3iZZvqwgBhn+01fObqrAJ+SagxJ/21xNQJchtUOWIlxR # kuUn9KkuDYrMO70a2ekHODcAbcuHAGI8wzw4saK1iPPhVTlFijHS+7VfIt/d/18p # MLHHArLQQqe1Z0mTfuL4M4xCUKpebkH8rI3Fva62/6osaXLD0ymERzCCBTAwggQY # oAMCAQICEAQJGBtf1btmdVNDtW+VUAgwDQYJKoZIhvcNAQELBQAwZTELMAkGA1UE # BhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2lj # ZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJRCBSb290IENBMB4X # DTEzMTAyMjEyMDAwMFoXDTI4MTAyMjEyMDAwMFowcjELMAkGA1UEBhMCVVMxFTAT # BgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEx # MC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUgU2lnbmluZyBD # QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPjTsxx/DhGvZ3cH0wsx # SRnP0PtFmbE620T1f+Wondsy13Hqdp0FLreP+pJDwKX5idQ3Gde2qvCchqXYJawO # eSg6funRZ9PG+yknx9N7I5TkkSOWkHeC+aGEI2YSVDNQdLEoJrskacLCUvIUZ4qJ # RdQtoaPpiCwgla4cSocI3wz14k1gGL6qxLKucDFmM3E+rHCiq85/6XzLkqHlOzEc # z+ryCuRXu0q16XTmK/5sy350OTYNkO/ktU6kqepqCquE86xnTrXE94zRICUj6whk # PlKWwfIPEvTFjg/BougsUfdzvL2FsWKDc0GCB+Q4i2pzINAPZHM8np+mM6n9Gd8l # k9ECAwEAAaOCAc0wggHJMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQD # AgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMDMHkGCCsGAQUFBwEBBG0wazAkBggrBgEF # BQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsGAQUFBzAChjdodHRw # Oi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0Eu # Y3J0MIGBBgNVHR8EejB4MDqgOKA2hjRodHRwOi8vY3JsNC5kaWdpY2VydC5jb20v # RGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMDqgOKA2hjRodHRwOi8vY3JsMy5k # aWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsME8GA1UdIARI # MEYwOAYKYIZIAYb9bAACBDAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5kaWdp # Y2VydC5jb20vQ1BTMAoGCGCGSAGG/WwDMB0GA1UdDgQWBBRaxLl7KgqjpepxA8Bg # +S32ZXUOWDAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzANBgkqhkiG # 9w0BAQsFAAOCAQEAPuwNWiSz8yLRFcgsfCUpdqgdXRwtOhrE7zBh134LYP3DPQ/E # r4v97yrfIFU3sOH20ZJ1D1G0bqWOWuJeJIFOEKTuP3GOYw4TS63XX0R58zYUBor3 # nEZOXP+QsRsHDpEV+7qvtVHCjSSuJMbHJyqhKSgaOnEoAjwukaPAJRHinBRHoXpo # aK+bp1wgXNlxsQyPu6j4xRJon89Ay0BEpRPw5mQMJQhCMrI2iiQC/i9yfhzXSUWW # 6Fkd6fp0ZGuy62ZD2rOwjNXpDd32ASDOmTFjPQgaGLOBm0/GkxAG/AeB+ova+YJJ # 92JuoVP6EpQYhS6SkepobEQysmah5xikmmRR7zCCBY0wggR1oAMCAQICEA6bGI75 # 0C3n79tQ4ghAGFowDQYJKoZIhvcNAQEMBQAwZTELMAkGA1UEBhMCVVMxFTATBgNV # BAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIG # A1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJRCBSb290IENBMB4XDTIyMDgwMTAwMDAw # MFoXDTMxMTEwOTIzNTk1OVowYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lD # ZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGln # aUNlcnQgVHJ1c3RlZCBSb290IEc0MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC # CgKCAgEAv+aQc2jeu+RdSjwwIjBpM+zCpyUuySE98orYWcLhKac9WKt2ms2uexuE # DcQwH/MbpDgW61bGl20dq7J58soR0uRf1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNw # wrK6dZlqczKU0RBEEC7fgvMHhOZ0O21x4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs0 # 6wXGXuxbGrzryc/NrDRAX7F6Zu53yEioZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e # 5TXnMcvak17cjo+A2raRmECQecN4x7axxLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtV # gkEy19sEcypukQF8IUzUvK4bA3VdeGbZOjFEmjNAvwjXWkmkwuapoGfdpCe8oU85 # tRFYF/ckXEaPZPfBaYh2mHY9WV1CdoeJl2l6SPDgohIbZpp0yt5LHucOY67m1O+S # kjqePdwA5EUlibaaRBkrfsCUtNJhbesz2cXfSwQAzH0clcOP9yGyshG3u3/y1Yxw # LEFgqrFjGESVGnZifvaAsPvoZKYz0YkH4b235kOkGLimdwHhD5QMIR2yVCkliWzl # DlJRR3S+Jqy2QXXeeqxfjT/JvNNBERJb5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFr # b7GrhotPwtZFX50g/KEexcCPorF+CiaZ9eRpL5gdLfXZqbId5RsCAwEAAaOCATow # ggE2MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOzX44LScV1kTN8uZz/nupiu # HA9PMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQE # AwIBhjB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp # Z2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQu # Y29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDBFBgNVHR8EPjA8MDqgOKA2 # hjRodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290 # Q0EuY3JsMBEGA1UdIAQKMAgwBgYEVR0gADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/ # Q1xV5zhfoKN0Gz22Ftf3v1cHvZqsoYcs7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNK # ei8ttzjv9P+Aufih9/Jy3iS8UgPITtAq3votVs/59PesMHqai7Je1M/RQ0SbQyHr # lnKhSLSZy51PpwYDE3cnRNTnf+hZqPC/Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4 # oVaO7KTVPeix3P0c2PR3WlxUjG/voVA9/HYJaISfb8rbII01YBwCA8sgsKxYoA5A # Y8WYIsGyWfVVa88nq2x2zm8jLfR+cWojayL/ErhULSd+2DrZ8LaHlv1b0VysGMNN # n3O3AamfV6peKOK5lDCCBq4wggSWoAMCAQICEAc2N7ckVHzYR6z9KGYqXlswDQYJ # KoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IElu # YzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQg # VHJ1c3RlZCBSb290IEc0MB4XDTIyMDMyMzAwMDAwMFoXDTM3MDMyMjIzNTk1OVow # YzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQD # EzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGlu # ZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMaGNQZJs8E9cklR # VcclA8TykTepl1Gh1tKD0Z5Mom2gsMyD+Vr2EaFEFUJfpIjzaPp985yJC3+dH54P # Mx9QEwsmc5Zt+FeoAn39Q7SE2hHxc7Gz7iuAhIoiGN/r2j3EF3+rGSs+QtxnjupR # PfDWVtTnKC3r07G1decfBmWNlCnT2exp39mQh0YAe9tEQYncfGpXevA3eZ9drMvo # hGS0UvJ2R/dhgxndX7RUCyFobjchu0CsX7LeSn3O9TkSZ+8OpWNs5KbFHc02DVzV # 5huowWR0QKfAcsW6Th+xtVhNef7Xj3OTrCw54qVI1vCwMROpVymWJy71h6aPTnYV # VSZwmCZ/oBpHIEPjQ2OAe3VuJyWQmDo4EbP29p7mO1vsgd4iFNmCKseSv6De4z6i # c/rnH1pslPJSlRErWHRAKKtzQ87fSqEcazjFKfPKqpZzQmiftkaznTqj1QPgv/Ci # PMpC3BhIfxQ0z9JMq++bPf4OuGQq+nUoJEHtQr8FnGZJUlD0UfM2SU2LINIsVzV5 # K6jzRWC8I41Y99xh3pP+OcD5sjClTNfpmEpYPtMDiP6zj9NeS3YSUZPJjAw7W4oi # qMEmCPkUEBIDfV8ju2TjY+Cm4T72wnSyPx4JduyrXUZ14mCjWAkBKAAOhFTuzuld # yF4wEr1GnrXTdrnSDmuZDNIztM2xAgMBAAGjggFdMIIBWTASBgNVHRMBAf8ECDAG # AQH/AgEAMB0GA1UdDgQWBBS6FtltTYUvcyl2mi91jGogj57IbzAfBgNVHSMEGDAW # gBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAww # CgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsGAQUFBzABhhhodHRwOi8v # b2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNlcnRzLmRp # Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3J0MEMGA1UdHwQ8MDow # OKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRS # b290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwHATANBgkq # hkiG9w0BAQsFAAOCAgEAfVmOwJO2b5ipRCIBfmbW2CFC4bAYLhBNE88wU86/GPvH # UF3iSyn7cIoNqilp/GnBzx0H6T5gyNgL5Vxb122H+oQgJTQxZ822EpZvxFBMYh0M # CIKoFr2pVs8Vc40BIiXOlWk/R3f7cnQU1/+rT4osequFzUNf7WC2qk+RZp4snuCK # rOX9jLxkJodskr2dfNBwCnzvqLx1T7pa96kQsl3p/yhUifDVinF2ZdrM8HKjI/rA # J4JErpknG6skHibBt94q6/aesXmZgaNWhqsKRcnfxI2g55j7+6adcq/Ex8HBanHZ # xhOACcS2n82HhyS7T6NJuXdmkfFynOlLAlKnN36TU6w7HQhJD5TNOXrd/yVjmScs # PT9rp/Fmw0HNT7ZAmyEhQNC3EyTN3B14OuSereU0cZLXJmvkOHOrpgFPvT87eK1M # rfvElXvtCl8zOYdBeHo46Zzh3SP9HSjTx/no8Zhf+yvYfvJGnXUsHicsJttvFXse # GYs2uJPU5vIXmVnKcPA3v5gA3yAWTyf7YGcWoWa63VXAOimGsJigK+2VQbc61RWY # MbRiCQ8KvYHZE/6/pNHzV9m8BPqC3jLfBInwAM1dwvnQI38AC+R2AibZ8GV2QqYp # hwlHK+Z/GqSFD/yYlvZVVCsfgPrA8g4r5db7qS9EFUrnEw4d2zc4GqEr9u3WfPww # ggbAMIIEqKADAgECAhAMTWlyS5T6PCpKPSkHgD1aMA0GCSqGSIb3DQEBCwUAMGMx # CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMy # RGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcg # Q0EwHhcNMjIwOTIxMDAwMDAwWhcNMzMxMTIxMjM1OTU5WjBGMQswCQYDVQQGEwJV # UzERMA8GA1UEChMIRGlnaUNlcnQxJDAiBgNVBAMTG0RpZ2lDZXJ0IFRpbWVzdGFt # cCAyMDIyIC0gMjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAM/spSY6 # xqnya7uNwQ2a26HoFIV0MxomrNAcVR4eNm28klUMYfSdCXc9FZYIL2tkpP0GgxbX # kZI4HDEClvtysZc6Va8z7GGK6aYo25BjXL2JU+A6LYyHQq4mpOS7eHi5ehbhVsbA # umRTuyoW51BIu4hpDIjG8b7gL307scpTjUCDHufLckkoHkyAHoVW54Xt8mG8qjoH # ffarbuVm3eJc9S/tjdRNlYRo44DLannR0hCRRinrPibytIzNTLlmyLuqUDgN5YyU # XRlav/V7QG5vFqianJVHhoV5PgxeZowaCiS+nKrSnLb3T254xCg/oxwPUAY3ugjZ # Naa1Htp4WB056PhMkRCWfk3h3cKtpX74LRsf7CtGGKMZ9jn39cFPcS6JAxGiS7uY # v/pP5Hs27wZE5FX/NurlfDHn88JSxOYWe1p+pSVz28BqmSEtY+VZ9U0vkB8nt9Kr # FOU4ZodRCGv7U0M50GT6Vs/g9ArmFG1keLuY/ZTDcyHzL8IuINeBrNPxB9Thvdld # S24xlCmL5kGkZZTAWOXlLimQprdhZPrZIGwYUWC6poEPCSVT8b876asHDmoHOWIZ # ydaFfxPZjXnPYsXs4Xu5zGcTB5rBeO3GiMiwbjJ5xwtZg43G7vUsfHuOy2SJ8bHE # uOdTXl9V0n0ZKVkDTvpd6kVzHIR+187i1Dp3AgMBAAGjggGLMIIBhzAOBgNVHQ8B # Af8EBAMCB4AwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAg # BgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEwHwYDVR0jBBgwFoAUuhbZ # bU2FL3MpdpovdYxqII+eyG8wHQYDVR0OBBYEFGKK3tBh/I8xFO2XC809KpQU31Kc # MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdp # Q2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcmwwgZAG # CCsGAQUFBwEBBIGDMIGAMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy # dC5jb20wWAYIKwYBBQUHMAKGTGh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9E # aWdpQ2VydFRydXN0ZWRHNFJTQTQwOTZTSEEyNTZUaW1lU3RhbXBpbmdDQS5jcnQw # DQYJKoZIhvcNAQELBQADggIBAFWqKhrzRvN4Vzcw/HXjT9aFI/H8+ZU5myXm93KK # mMN31GT8Ffs2wklRLHiIY1UJRjkA/GnUypsp+6M/wMkAmxMdsJiJ3HjyzXyFzVOd # r2LiYWajFCpFh0qYQitQ/Bu1nggwCfrkLdcJiXn5CeaIzn0buGqim8FTYAnoo7id # 160fHLjsmEHw9g6A++T/350Qp+sAul9Kjxo6UrTqvwlJFTU2WZoPVNKyG39+Xgmt # dlSKdG3K0gVnK3br/5iyJpU4GYhEFOUKWaJr5yI+RCHSPxzAm+18SLLYkgyRTzxm # lK9dAlPrnuKe5NMfhgFknADC6Vp0dQ094XmIvxwBl8kZI4DXNlpflhaxYwzGRkA7 # zl011Fk+Q5oYrsPJy8P7mxNfarXH4PMFw1nfJ2Ir3kHJU7n/NBBn9iYymHv+XEKU # gZSCnawKi8ZLFUrTmJBFYDOA4CPe+AOk9kVH5c64A0JH6EE2cXet/aLol3ROLtoe # HYxayB6a1cLwxiKoT5u92ByaUcQvmvZfpyeXupYuhVfAYOd4Vn9q78KVmksRAsiC # nMkaBXy6cbVOepls9Oie1FqYyJ+/jbsYXEP10Cro4mLueATbvdH7WwqocH7wl4R4 # 4wgDXUcsY6glOJcB0j862uXl9uab3H4szP8XTE0AotjWAQ64i+7m4HJViSwnGWH2 # dwGMMYIFXTCCBVkCAQEwgYYwcjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lD # ZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMoRGln # aUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUgU2lnbmluZyBDQQIQAwW7hiGwoWNf # v96uEgTnbTANBglghkgBZQMEAgEFAKCBhDAYBgorBgEEAYI3AgEMMQowCKACgACh # AoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAM # BgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCDG7ga9LNiRS2Z9xDgeF/qfj7BX # s8w05Qkk4CB+O/vaQDANBgkqhkiG9w0BAQEFAASCAQCiqp0SKB7cilOXHrUhu9vP # pBOGNpxdXHpKtpWVuQV1vBcn1Kyqg9t99M0H9Z8XQmWrbpfmvcrEODSVA47SW7xU # BPlP27ZeKTvwfrjChnGQnqGa/tpMcotvjDVz/ZHNoOi1mqZSZ7hFJ61rhiPFgVYv # Kc6yMWl3Xot48722LS++5fU9I3OLPVAadyPdn529inO/QFU7Gh8af21PanUEDYFf # B2ouWwy0uhVj+fOnQd38QyRuo67jNOXSBE0ep01U8nMYwmYQxQhWQ8d95o+3H/H3 # dnXVAGTCHJdxrNzFyGJLPQaU2WZ29yLiAwkTIKUnUXkv+gIUrjn6cFE/Esr1nsyk # oYIDIDCCAxwGCSqGSIb3DQEJBjGCAw0wggMJAgEBMHcwYzELMAkGA1UEBhMCVVMx # FzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVz # dGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGluZyBDQQIQDE1pckuU+jwq # Sj0pB4A9WjANBglghkgBZQMEAgEFAKBpMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0B # BwEwHAYJKoZIhvcNAQkFMQ8XDTIzMDMxNTAwMjI0MVowLwYJKoZIhvcNAQkEMSIE # IAx33zU3t219nr2OUZE+QwerynKeY3W3oHcTsecXMDhTMA0GCSqGSIb3DQEBAQUA # BIICAMgk8zH0Ct2xwaY36H/uQ5Bq74P4TIDSzc0WcJw+F9fDawj1+49csVVW9OqE # YHGfHpCJefenEGQlFlvoOuGVjFn5yX3s+YQYgL2ukIbe0Rwt7Q5J8yYu8CKC8zrX # iu0V35fz+VZAeOa91ii8WHZF9n9Ixv4eyh04nmqAOrr70wimKuBhm696PcG1c7t1 # opvtHEqJUztrNqmh07kwadbxnODSXYZpO/+IiC4PGiy8pIOq1cw1OXsfAzU9dn8c # SVzKo46ZeIgLiaayBGwNFvOdD6MnGzBpim08EiPAeS93C+dmK2hV5c+bjjUrz7fK # XWfkPy2Waj3opZ+k2Y+vpBYdONo35QXco4wKK8Aci4bI6sdKjd6PX2w1FCDs1Clb # Np1OzaudUbUQ+QauDjU/lIEnfkeOn/Nj2s+hTayl8HR1NpOoIJOBozGbtrI3Jehn # KhvZfBWdycCLNkGlTbaOrGWkvyA+c5diOFfeSLnzD4rR37/jMoB86HipDNjWyJtF # WujTTAYsOnbvJEeCNJso+/3VeJlP792WIcl7Qxv65EU5orPX/8v7Ezszg89rPR8Z # kZ/URGgrgYFzjut8F7VfXw1Zz2+napTBWuK7L0dRZXDjiUi1lIFrq340ByZ4xzAu # aUbxbEK1yPSCOxdqaKjRuyZKcplNdGkSo0YWvinjbkfZSvTo # SIG # End signature block |