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