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 ($FilePath) {
            $InputObject += Get-ChildItem -Path $FilePath
        }

        $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 ($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 -ListAvailable xWindowsUpdate -ErrorAction Ignore | Where-Object Version -eq 3.0.0
        }

        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
                } 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
                }

                $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 -ListAvailable xPSDesiredStateConfiguration -ErrorAction Ignore | Where-Object Version -eq 9.2.0
        }

        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
                } 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
                }

                $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 (-not $InputObject) {
            Write-PSFMessage -Level Verbose -Message "Nothing to install on $hostname, moving on"
        }
        foreach ($object in $InputObject) {
            if ($object.Link -and $RepositoryPath) {
                try {
                    $filename = Split-Path -Path $object.Link -Leaf
                    Write-PSFMessage -Level Verbose -Message "Adding $filename to $RepositoryPath"
                    $repofile = Join-Path -Path $RepositoryPath -ChildPath $filename
                    if ($remotehome) {
                        $null = Copy-Item -Path $repofile -Destination "$remotehome\Downloads\$filename" -ToSession $remotesession -Recurse -Force
                    } else {
                        $null = Copy-Item -Path $repofile -Destination "$home\Downloads" -Recurse -Force
                    }
                } catch {
                    $hostname = $object.ComputerName
                    Stop-PSFFunction -Message "Couldn't copy $filename from repo to $hostname." -Continue
            }
                Write-PSFMessage -Level Verbose -Message "Adding $($FilePath)"
            }

            if ($FilePath) {
                $remotefileexists = $updatefile = Invoke-KbCommand -ArgumentList $FilePath -ScriptBlock {
                    Get-ChildItem -Path $args -ErrorAction SilentlyContinue
                }
            }

            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) {
                    Write-PSFMessage -Level Verbose -Message "Update file not found, download it for them"
                    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) {
                        $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("exe")) {
                Write-PSFMessage -Level Verbose -Message "It's an exe"
                if (-not $ArgumentList -and $FilePath -match "sql") {
                    $ArgumentList = "/action=patch /AllInstances /quiet /IAcceptSQLServerLicenseTerms"
                } else {
                    # 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
                        }
                        $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
                            $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"

                    }
                }

                # 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
                    }
                }
            } elseif ("$FilePath".EndsWith("cab")) {
                Write-PSFMessage -Level Verbose -Message "It's a cab file"
                $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
                    }
                }
            } else {
                Write-PSFMessage -Level Verbose -Message "It's a WSU file"
                # 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
                }
            }
            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 $hotfixnameid from $hotfixpath"
                    try {
                        $ProgressPreference = "SilentlyContinue"
                        if (-not (Invoke-DscResource @hotfix -Method Test 4>$null)) {
                            $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 {
                                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."
                            }
                            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"
                $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
                } else {
                    $filetitle = $updatefile.VersionInfo.ProductName
                }

                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."
                    $exists = Get-KbInstalledSoftware -ComputerName $ComputerName -HotfixId $hotfix.property.id

                    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
                    } else {
                        $filetitle = $updatefile.VersionInfo.ProductName
                    }

                    [pscustomobject]@{
                        ComputerName = $hostname
                        Title        = $filetitle
                        ID           = $id
                        Status       = $Status
                        FileName     = $updatefile.Name
                    }
                } else {
                    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
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBax7KnC+Zs9fIb
# CuKOu3LOPxjtzpeqehsvE1Si7ig/RqCCHVkwggUaMIIEAqADAgECAhADBbuGIbCh
# 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
# BgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCA2wv17vTXPPc9RgpCnmI0eop63
# AQKTYPVAAdYZhA4wsjANBgkqhkiG9w0BAQEFAASCAQBR/JVsgiTtTYEPcESqOZs+
# GLR4NbCsyPzDHwIDRsBeV7IaX50HihKnjv9L6QzwzKdoXMEugRAgpDSvksflevuk
# 3hr7jRJks+Vxm9uPipW7YnNVw8otR6+1zu/j6BB+BggxpnE2Kbe2tueJnwBtTQ4h
# QxqzwstxGjQc2SBD5eGmSNd/RX6Tzj9rf2YNqC5CopHZoChw6F+MjYnNHuh1wLTv
# s71TiSH5wuRICXRj+iFKSmmJXdIZpZpXZXm/H3eycE3Y41FXOLOdQ8KH0UcLIrcn
# KPPXVh6LQS2IneC/SbBA1+fxGxvCdRRK0/NWwZHPFwFwpF0HkKQ2WvBo1iBfHSO2
# oYIDIDCCAxwGCSqGSIb3DQEJBjGCAw0wggMJAgEBMHcwYzELMAkGA1UEBhMCVVMx
# FzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVz
# dGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGluZyBDQQIQDE1pckuU+jwq
# Sj0pB4A9WjANBglghkgBZQMEAgEFAKBpMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0B
# BwEwHAYJKoZIhvcNAQkFMQ8XDTIyMDkyODA4MzMwM1owLwYJKoZIhvcNAQkEMSIE
# IJx+0KQV2q+tFuJeK/JMwBeqPuovMdAt7TM+L4c2QBgfMA0GCSqGSIb3DQEBAQUA
# BIICAB3u9Bsd7HUIEmACC/c9Xj5i28EnaQHzLqVZGt8w+58NuQCUhPzpnBG8C+Lp
# LZL2qDmP+Lr3XuGVcav25x29HufSw5bT5qtn2RFoT81o1e2ZuVl9kw16twVcERFR
# Z5N9ubZXUvlzi24hjwruJDF2eajZluu6a0ETP49Hl0nnYlsC/od/3KUZJpegMj/6
# H5EPt+kMYwNM6jr+vhDILPI1w8qIsxzqqgDdZpZMvUN90pBr8ODaM/bw4saGkVpd
# UH8n6JPkgyAT/GHPZ1Sjvu3mOfKZ5qBdxaGNmBtZy7CNKBkEh6efXC6nhd2diIOj
# 3w3bJpr7EZ+uCtZTRAdc4IkEX/QvdvMdA/01SaEQ7o1Fuo+wPpO/ZweTBv4AiQm+
# mLGy7kew4oCi/rEL4NY/Ps9ylZfzuKeElx7B7Hs1WORhhPhMDxWDrY+irD46v0bd
# dAZntBlZBLCH+nTjeJ/X8WVlCxUx+vKG7K2bKTLlwvT+FJqoQCWaHvgIcXnWdYQd
# jKbmpbaHc44rljBwrkx2IwXgSb8yivT8bVtVl45jtD84wucR81WzNMC1ARzJDXwN
# h+EMTB7JhG06bKgi9j++LLHybCjs4o1ISftcr2SMCFo4IvKVIFqTlnzOJgtPjIBk
# kHfUP7oWuQNO7QKaottNtSOQ419ETs1YRIWUfFFjd/e2plfR
# SIG # End signature block