Private/GetNestedVirtCapabilities.ps1

function GetNestedVirtCapabilities {
    [CmdletBinding(DefaultParameterSetName='Default')]
    Param(
        [Parameter(
            Mandatory = $False,
            ParameterSetName = 'Default'
        )]
        [string]$TargetHostNameOrIP,

        [Parameter(
            Mandatory=$True,
            ParameterSetName = 'UsingVMName'
        )]
        [string]$TargetVMName,

        [Parameter(Mandatory=$False)]
        [string]$HypervisorFQDNOrIP,

        [Parameter(Mandatory=$False)]
        $TargetHostNameCreds,

        [Parameter(Mandatory=$False)]
        $HypervisorCreds,

        # -TryWithoutHypervisorInfo MIGHT result in creating a Local NAT
        # with an Internal vSwitch on the Target Machine (assuming it's a Guest VM). It depends if
        # Get-NestedVirtCapabilities detemines whether the Target Machine can use an External vSwitch or not.
        # If it can, then a Local NAT will NOT be created.
        # If a NAT already exists on the Target Machine, that NAT will be changed to
        # 10.0.75.0/24 with IP 10.0.75.1 if it isn't already
        [Parameter(Mandatory=$False)]
        [switch]$TryWithoutHypervisorInfo,

        [Parameter(Mandatory=$False)]
        [switch]$AllowRestarts,

        [Parameter(
            Mandatory=$True,
            ParameterSetName = 'InfoAlreadyCollected'
        )]
        $GuestVMAndHVInfo, # Uses output of Get-GuestVMAndHypervisorInfo function

        [Parameter(Mandatory=$False)]
        [switch]$SkipHyperVInstallCheck
    )

    ##### BEGIN Variable/Parameter Transforms and PreRun Prep #####

    if ($PSBoundParameters['TargetHostNameCreds']) {
        if ($TargetHostNameCreds.GetType().FullName -ne "System.Management.Automation.PSCredential") {
            Write-Error "The object provided to the -TargetHostNameCreds parameter must be a System.Management.Automation.PSCredential! Halting!"
            $global:FunctionResult = "1"
            return
        }
    }
    if ($PSBoundParameters['HypervisorCreds']) {
        if ($HypervisorCreds.GetType().FullName -ne "System.Management.Automation.PSCredential") {
            Write-Error "The object provided to the -HypervisorCreds parameter must be a System.Management.Automation.PSCredential! Halting!"
            $global:FunctionResult = "1"
            return
        }
    }

    <#
    # From: https://msdn.microsoft.com/en-us/library/ms724358(VS.85).aspx
    $WindowsEditionTable = @{
        Windows10Pro = "30"
        Windows10Enterprise = "4"
        Windows10S = "B2"
        Windows10SN = "B3"
        ServerHyperCoreV = "40"
        ServerDatacenterEvaluation = "50"
        ServerDatacenterFull = "8"
        ServerDatacenterCore = "C"
        Windows10EnterpriseE = "46"
        Windows10EnterpriseEvaluation = "48"
        Windows10EnterpriseN = "1B"
        Windows10EnterpriseNEvaluation = "54"
        ServerEnterpriseFull = "A"
        ServerEnterpriseCore = "E"
        MicrosoftHyperVServer = "2A"
        Windows10ProN = "31"
        ServerStandardEvaluation = "4F"
        ServerStandard = "7"
        ServerStandardCore = "D"
    }
    #>


    # Get Guest VM and hypervisor info

    if (!$GuestVMAndHVInfo) {
        if (!$TargetHostNameOrIP -and !$TargetVMName) {
            $TargetHostNameOrIP = $env:ComputerName
        }

        try {
            $HostNameNetworkInfo = ResolveHost -HostNameOrIP $TargetHostNameOrIP
        }
        catch {
            Write-Error $_
            $global:FunctionResult = "1"
            return
        }

        $GetWorkingCredsSplatParams = @{
            RemoteHostNameOrIP          = $HostNameNetworkInfo.FQDN
            ErrorAction                 = "Stop"
        }
        if ($TargetHostNameCreds) {
            $GetWorkingCredsSplatParams.Add("AltCredentials",$TargetHostNameCreds)
        }
        
        try {
            $GetTargetHostCredsInfo = GetWorkingCredentials @GetWorkingCredsSplatParams
            if (!$GetTargetHostCredsInfo.DeterminedCredsThatWorkedOnRemoteHost) {throw "Can't determine working credentials for $($HostNameNetworkInfo.FQDN)!"}
            
            if ($GetTargetHostCredsInfo.CurrentLoggedInUserCredsWorked -eq $True) {
                $TargetHostNameCreds = $null
            }

            $TargetHostInvCmdLocation = $GetTargetHostCredsInfo.RemoteHostWorkingLocation
        }
        catch {
            Write-Error $_
            if ($PSBoundParameters['TargetHostNameCreds']) {
                Write-Error "The GetWorkingCredentials function failed! Check the credentials provided to the -TargetHostNameCreds parameter! Halting!"
            }
            else {
                Write-Error "The GetWorkingCredentials function failed! Try using the -TargetHostNameCreds parameter! Halting!"
            }
            $global:FunctionResult = "1"
            return
        }
    }

    if (![bool]$PSBoundParameters['GuestVMAndHVInfo']) {
        $GetVMAndHVSplatParams = @{}
        
        if ($($TargetHostNameOrIP -or $TargetHostInvCmdLocation) -and $GetVMAndHVSplatParams.Keys -notcontains "TargetHostNameOrIP") {
            if ($TargetHostInvCmdLocation) {
                $GetVMAndHVSplatParams.Add("TargetHostNameOrIP",$TargetHostInvCmdLocation)
            }
            elseif ($TargetHostNameOrIP) {
                $GetVMAndHVSplatParams.Add("TargetHostNameOrIP",$TargetHostNameOrIP)
            }
        }
        elseif ($TargetVMName -and $GetVMAndHVSplatParams.Keys -notcontains "TargetVMName") {
            $GetVMAndHVSplatParams.Add("TargetVMName",$TargetVMName)
        }

        if ($TargetHostNameCreds -and $GetVMAndHVSplatParams.Keys -notcontains "TargetHostNameCreds") {
            $GetVMAndHVSplatParams.Add("TargetHostNameCreds",$TargetHostNameCreds)
        }

        if ($($HypervisorFQDNOrIP -or $HypervisorInvCmdLocation) -and $GetVMAndHVSplatParams.Keys -notcontains "HypervisorFQDNOrIP") {
            if ($HypervisorInvCmdLocation) {
                $GetVMAndHVSplatParams.Add("HypervisorFQDNOrIP",$HypervisorInvCmdLocation)
            }
            elseif ($HypervisorFQDNOrIP) {
                $GetVMAndHVSplatParams.Add("HypervisorFQDNOrIP",$HypervisorFQDNOrIP)
            }
        }

        if ($HypervisorCreds -and $GetVMAndHVSplatParams.Keys -notcontains "HypervisorCreds") {
            $GetVMAndHVSplatParams.Add("HypervisorCreds",$HypervisorCreds)
        }
        
        if ($($TryWithoutHypervisorInfo -and $GetVMAndHVSplatParams.Keys -notcontains "TryWithoutHypervisorInfo") -or 
        $($(ConfirmAWSVM -EA SilentlyContinue) -or $(ConfirmAzureVM -EA SilentlyContinue) -or
        $(ConfirmGoogleComputeVM -EA SilentlyContinue))
        ) {
            $GetVMAndHVSplatParams.Add("TryWithoutHypervisorInfo",$True)
        }

        if ($AllowRestarts -and $GetVMAndHVSplatParams.Keys -notcontains "AllowRestarts") {
            $GetVMAndHVSplatParams.Add("AllowRestarts",$True)
        }

        if ($NoMacAddressSpoofing -and $GetVMAndHVSplatParams.Keys -notcontains "NoMacAddressSpoofing") {
            $GetVMAndHVSplatParams.Add("NoMacAddressSpoofing",$True)
        }

        if ($SkipHyperVInstallCheck -and $GetVMAndHVSplatParams.Keys -notcontains "SkipHyperVInstallCheck") {
            $GetVMAndHVSplatParams.Add("SkipHyperVInstallCheck",$True)
        }

        if ($SkipExternalvSwitchCheck -and $GetVMAndHVSplatParams.Keys -notcontains "SkipExternalvSwitchCheck") {
            $GetVMAndHVSplatParams.Add("SkipExternalvSwitchCheck",$True)
        }

        try {
            $GuestVMAndHVInfo = Get-GuestVMAndHypervisorInfo @GetVMAndHVSplatParams -ErrorAction SilentlyContinue -ErrorVariable GGIErr
            if (!$GuestVMAndHVInfo) {throw "The Get-GuestVMAndHypervisorInfo function failed! Halting!"}

            if ($PSVersionTable.PSEdition -eq "Core") {
                $GetPendingRebootAsString = ${Function:GetPendingReboot}.Ast.Extent.Text
                
                $RebootPendingCheck = Invoke-WinCommand -ComputerName localhost -ScriptBlock {
                    Invoke-Expression $args[0]
                    $(GetPendingReboot).RebootPending
                } -ArgumentList $GetPendingRebootAsString

                $RebootPendingFileCheck = Invoke-WinCommand -ComputerName localhost -ScriptBlock {
                    Invoke-Expression $args[0]
                    $(GetPendingReboot).PendFileRenVal
                } -ArgumentList $GetPendingRebootAsString
            }
            else {
                $RebootPendingCheck = $(GetPendingReboot).RebootPending
                $RebootPendingFileCheck = $(GetPendingReboot).PendFileRenVal
            }

            if ($GuestVMAndHVInfo.RestartNeeded -and $RebootPendingCheck -and $RebootPendingFileCheck -ne $null -and !$AllowRestarts) {
                Write-Verbose "You might need to restart $env:ComputerName before the GetNestedVirtCapabilities function can proceed! Halting!"
            }
        }
        catch {
            Write-Error $_
            if ($($GGIErr | Out-String) -match "The operation has timed out") {
                Write-Error "There was a problem downloading the Vagrant Box to be used to test the External vSwitch! Halting!"
                $global:FunctionResult = "1"
                return
            }
            else {
                Write-Host "Errors for the Get-GuestVMAndHypervisorInfo function are as follows:"
                Write-Error $($GGIErr | Out-String)
                $global:FunctionResult = "1"
                return
            }
        }
    }
    else {
        $ValidGuestVMAndHVInfoNoteProperties = @(
            "HypervisorNetworkInfo"
            "HypervisorInvCmdLocation"
            "HypervisorComputerInfo"
            "HypervisorOSInfo"
            "TargetVMInfoFromHyperV"
            "VMProcessorInfo"
            "VMNetworkAdapterInfo"
            "VMMemoryInfo"
            "HypervisorCreds"
            "HostNameNetworkInfo"
            "TargetHostInvCmdLocation"
            "HostNameComputerInfo"
            "HostNameOSInfo"
            "HostNameProcessorInfo"
            "HostNameBIOSInfo"
            "TargetHostNameCreds"
            "RestartNeeded"
            "RestartOccurred"
            "VirtualizationExtensionsExposed"
            "MacAddressSpoofingEnabled"
        )
        [System.Collections.ArrayList]$FoundIssueWithGuestVMAndHVInfo = @()
        $ParamObjMembers = $($GuestVMAndHVInfo | Get-Member -MemberType NoteProperty).Name
        foreach ($noteProp in $ParamObjMembers) {
            if ($ValidGuestVMAndHVInfoNoteProperties -notcontains $noteProp) {
                $null = $FoundIssueWithGuestVMAndHVInfo.Add($noteProp)
            }
        }
        if ($FoundIssueWithGuestVMAndHVInfo.Count -gt 3) {
            $ParamObjMembers
            Write-Error "The object provided to the -GuestVMAndHVInfo parameter is invalid! It must be output from the Get-GuestVMAndHypervisorInfo function! Halting!"
            $global:FunctionResult = "1"
            return
        }
    }

    if (!$GuestVMAndHVInfo) {
        Write-Error "There was a problem with the Get-GuestVMandHypervisorInfo function! Halting!"
        $global:FunctionResult = "1"
        return
    }

    if ($GuestVMAndHVInfo.HypervisorCreds -ne $null) {
        $HypervisorCreds = $GuestVMAndHVInfo.HypervisorCreds
    }
    if ($GuestVMAndHVInfo.TargetHostNameCreds -ne $null) {
        $TargetHostNameCreds = $GuestVMAndHVInfo.TargetHostNameCreds
        if ($TargetHostNameCreds -eq $(whoami)) {
            $TargetHostNameCreds = $null
        }
    }

    if ($GuestVMAndHVInfo.HostNameBIOSInfo.IsVirtual -eq $False) {
        Write-Error "The GetNestedVirtCapabilities function should only be used to determine if a Guest VM is capable of Nested Virtualization. $($GuestVMAndHVInfo.HostNameNetworkInfo.FQDN) is a physical machine! Halting!"
        $global:FunctionResult = "1"
        return
    }

    ##### END Variable/Parameter Transforms and PreRun Prep #####


    ##### BEGIN Main Body #####

    [System.Collections.ArrayList]$HypervisorSoftwareThatWillWorkOnGuestVM = @()
    [System.Collections.ArrayList]$PossibleOSArchitectureOfNestedVMs = @()
    [System.Collections.ArrayList]$StepsToAllow64BitNestedVMs = @()

    ## BEGIN Analyzing info about the Guest VM - i.e. the Virtual Machine Configuration itself ##

    # We need to determine if we have access to the Hyper-V hypervisor. If we're in a public cloud (like AWS, Azure, etc)
    # we can assume that we don't, in which case we cannot use Mac Address Spoofing for Nested VM networking, which means
    # that we need to run Get-GuestVMAndHypervisorInfo with the -TryWithoutHypervisorInfo switch and that we need to
    # setup a NAT Adapter on the Guest VM Hyper-V Host (as opposed to an External vSwitch). This info all needs to be
    # in this function's output.
    [System.Collections.ArrayList]$NetworkingOptions = @()
    if ($(ConfirmAWSVM -EA SilentlyContinue) -or $(ConfirmAzureVM -EA SilentlyContinue) -or 
    $(ConfirmGoogleComputeVM -EA SilentlyContinue) -or $GuestVMAndHVInfo.HypervisorNetworkInfo -eq $null -or
    $GuestVMAndHVInfo.HypervisorComputerInfo -eq $null -or $GuestVMAndHVInfo.HypervisorOSInfo -eq $null -or
    $TryWithoutHypervisorInfo
    ) {
        $null = $NetworkingOptions.Add("Network Address Translation")
        
        $null = $StepsToAllow64BitNestedVMs.Add("Might need Disable Dynamic Memory")
        $null = $StepsToAllow64BitNestedVMs.Add("Might need to Remove Save States")
    }

    if ($GuestVMAndHVInfo.MacAddressSpoofingEnabled -eq $True -or $GuestVMAndHVInfo.VMNetworkAdapterInfo.MacAddressSpoofing -eq $True) {
        $null = $NetworkingOptions.Add("Mac Address Spoofing")
    }
    elseif ($GuestVMAndHVInfo.MacAddressSpoofingEnabled -ne $True -or
    $($GuestVMAndHVInfo.MacAddressSpoofingEnabled -ne $True -and $GuestVMAndHVInfo.VMNetworkAdapterInfo -eq $null)
    ) {
        $null = $StepsToAllow64BitNestedVMs.Add("Might need to Turn On MacAddress Spoofing")
    }

    if ($($GuestVMAndHVInfo.VMProcessorInfo -ne $null -and 
    $GuestVMAndHVInfo.VMProcessorInfo.ExposeVirtualizationExtensions -eq $False) -or
    $GuestVMAndHVInfo.VirtualizationExtensionsExposed -match "Unknown" -or
    $GuestVMAndHVInfo.VirtualizationExtensionsExposed -eq $False
    ) {
        $null = $StepsToAllow64BitNestedVMs.Add("Expose Virtualization Extensions")
        $NestedVirtualizationPossible = $False
    }
    elseif ($GuestVMAndHVInfo.VirtualizationExtensionsExposed -eq $null) {
        $null = $StepsToAllow64BitNestedVMs.Add("Might need to Expose Virtualization Extensions")
        $null = $PossibleOSArchitectureOfNestedVMs.Add("MAYBE_64-bit")
    }

    # Other information about the hypervisor...
    if ($GuestVMAndHVInfo.HypervisorOSInfo -ne $null -and $GuestVMAndHVInfo.HypervisorOSInfo.OSArchitecture -ne "64-bit") {
        $null = $StepsToAllow64BitNestedVMs.Add("The hypervisor must be running on a 64-bit Operating System.")
    }

    if ($GuestVMAndHVInfo.HypervisorOSInfo -ne $null) {
        $HypervisorOSNameCheck = $GuestVMAndHVInfo.HypervisorOSInfo.Caption -match "Windows 10||Windows Server 2016"
        $HypervisorOSTypeCheck = $GuestVMAndHVInfo.HypervisorOSInfo.Caption -match "Pro|Enterprise|Server"

        if (!$($HypervisorOSNameCheck -and $HypervisorOSTypeCheck) -and $GuestVMAndHVInfo.HypervisorOSInfo -ne $null) {
            $NestedVirtualizationPossible = $False
            $null = $StepsToAllow64BitNestedVMs.Add("The hypervisor must be running on Windows 10 Pro/Enterprise or Windows Server 2016.")
        }
    }

    # Guest VM bust be version 8.0 or higher
    if ($GuestVMAndHVInfo.TargetVMInfoFromHyperV -ne $null) {
        if ($([version]$GuestVMAndHVInfo.TargetVMInfoFromHyperV.Version).Major -lt 8) {
            $null = $StepsToAllow64BitNestedVMs.Add("Guest VM Configuration must be Version 8.0 or higher")
        }
    }

    ## END Analyzing info about the Guest VM - i.e. the Virtual Machine Configuration itself ##

    ## BEGIN Analyzing info about the Guest VM OS ##

    if ($GuestVMAndHVInfo.HostNameOSInfo.OSArchitecture -ne "64-bit") {
        $NestedVirtualizationPossible = $False
        $null = $StepsToAllow64BitNestedVMs.Add("Change the Guest VM OS Architecture to 64-bit")
    }

    # If at this point, $StepsToAllow64BitNestedVMs.Count -eq 0, or just "Might need to Turn On MacAddress Spoofing"
    # then we know the following to be true
    if ($StepsToAllow64BitNestedVMs.Count -eq 0 -or
    $($StepsToAllow64BitNestedVMs.Count -eq 1 -and $StepsToAllow64BitNestedVMs[0] -eq "Might need to Turn On MacAddress Spoofing")) {
        $NestedVirtualizationPossible = $True
        $null = $PossibleOSArchitectureOfNestedVMs.Add("32-bit")
        $null = $HypervisorSoftwareThatWillWorkOnGuestVM.Add("VMWare")
        $null = $HypervisorSoftwareThatWillWorkOnGuestVM.Add("Xen")
        $null = $HypervisorSoftwareThatWillWorkOnGuestVM.Add("VirtualBox")
    }

    $GuestVMOSNameCheck = $GuestVMAndHVInfo.HostNameOSInfo.Caption -match "Windows 10||Windows Server 2016"
    $GuestVMOSTypeCheck = $GuestVMAndHVInfo.HostNameOSInfo.Caption -match "Pro|Enterprise|Server"

    ## END Analyzing info about the Guest VM OS ##

    ## BEGIN Mixed Analysis ##

    # If the Hypervisor and VM are Windows 2016, then 64-bit Nested VMs are also possible
    if (!$($GuestVMOSNameCheck -and $GuestVMOSTypeCheck)) {
        $null = $StepsToAllow64BitNestedVMs.Add("Run Windows 10 Pro/Enterprise or Windows Server 2016 as the Guest VM OS.")
    }
    else {
        if (!$GuestVMAndHVInfo.HostNameProcessorInfo.VirtualizationFirmwareEnabled) {
            $null = $StepsToAllow64BitNestedVMs.Add("Might need to Enable VirtualizationFirmware")
        }

        $null = $HypervisorSoftwareThatWillWorkOnGuestVM.Add("Hyper-V")
        if ($PossibleOSArchitectureOfNestedVMs -notcontains "MAYBE_64-bit") {
            $null = $PossibleOSArchitectureOfNestedVMs.Add("64-bit")
        }
    }

    if ($GuestVMAndHVInfo.HypervisorOSInfo -eq $null -and 
    $GuestVMAndHVInfo.HostNameBIOSInfo.SMBIOSBIOSVersion -notmatch "Hyper-V|VMWare|Xen|American Megatrends" -and 
    $GuestVMAndHVInfo.HostNameBIOSInfo.Manufacturer -notmatch "Hyper-V|VMWare|Xen|American Megatrends" -and 
    $GuestVMAndHVInfo.HostNameBIOSInfo.Name -notmatch "Hyper-V|VMWare|Xen|American Megatrends" -and 
    $GuestVMAndHVInfo.HostNameBIOSInfo.SerialNumber -notmatch "Hyper-V|VMWare|Xen|American Megatrends" -and 
    $GuestVMAndHVInfo.HostNameBIOSInfo.Version -notmatch "Hyper-V|VMWare|Xen|American Megatrends|VRTUAL"
    ) {
        $null = $StepsToAllow64BitNestedVMs.Add("Use Hyper-V or VMWare or Xen as the baremetal hypervisor.")
    }

    if ($GuestVMAndHVInfo.VirtualizationExtensionsExposed -eq $True) {
        $NestedVirtualizationPossible = $True
    }

    $FinalSteps = [pscustomobject]@{
        StepsThatAreDefinitelyNeeded = $($StepsToAllow64BitNestedVMs | Where-Object {$_ -notmatch "Might need"})
        StepsThatMightBeNeeded       = $($StepsToAllow64BitNestedVMs | Where-Object {$_ -match "Might need"})
    }

    ## END Mixed Analysis ##

    [pscustomobject]@{
        NestedVirtualizationPossible                = $NestedVirtualizationPossible
        PossibleOSArchitectureOfNestedVMs           = if ($NestedVirtualizationPossible) {$PossibleOSArchitectureOfNestedVMs} else {$null}
        HypervisorSoftwareThatWillWorkOnGuestVM     = if ($NestedVirtualizationPossible) {$HypervisorSoftwareThatWillWorkOnGuestVM} else {$null}
        StepsToAllow64BitNestedVMs                  = $FinalSteps
        NetworkingPossibilities                     = if ($NestedVirtualizationPossible) {[array]$NetworkingOptions} else {$null}
        GuestVMAndHVInfo                            = $GuestVMAndHVInfo
    }

    ##### END Main Body #####
}

# SIG # Begin signature block
# MIIMiAYJKoZIhvcNAQcCoIIMeTCCDHUCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUkcbMvAj0xxpnco0HLC38YGEO
# 3/Ogggn9MIIEJjCCAw6gAwIBAgITawAAAB/Nnq77QGja+wAAAAAAHzANBgkqhkiG
# 9w0BAQsFADAwMQwwCgYDVQQGEwNMQUIxDTALBgNVBAoTBFpFUk8xETAPBgNVBAMT
# CFplcm9EQzAxMB4XDTE3MDkyMDIxMDM1OFoXDTE5MDkyMDIxMTM1OFowPTETMBEG
# CgmSJomT8ixkARkWA0xBQjEUMBIGCgmSJomT8ixkARkWBFpFUk8xEDAOBgNVBAMT
# B1plcm9TQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDCwqv+ROc1
# bpJmKx+8rPUUfT3kPSUYeDxY8GXU2RrWcL5TSZ6AVJsvNpj+7d94OEmPZate7h4d
# gJnhCSyh2/3v0BHBdgPzLcveLpxPiSWpTnqSWlLUW2NMFRRojZRscdA+e+9QotOB
# aZmnLDrlePQe5W7S1CxbVu+W0H5/ukte5h6gsKa0ktNJ6X9nOPiGBMn1LcZV/Ksl
# lUyuTc7KKYydYjbSSv2rQ4qmZCQHqxyNWVub1IiEP7ClqCYqeCdsTtfw4Y3WKxDI
# JaPmWzlHNs0nkEjvnAJhsRdLFbvY5C2KJIenxR0gA79U8Xd6+cZanrBUNbUC8GCN
# wYkYp4A4Jx+9AgMBAAGjggEqMIIBJjASBgkrBgEEAYI3FQEEBQIDAQABMCMGCSsG
# AQQBgjcVAgQWBBQ/0jsn2LS8aZiDw0omqt9+KWpj3DAdBgNVHQ4EFgQUicLX4r2C
# Kn0Zf5NYut8n7bkyhf4wGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwDgYDVR0P
# AQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUdpW6phL2RQNF
# 7AZBgQV4tgr7OE0wMQYDVR0fBCowKDAmoCSgIoYgaHR0cDovL3BraS9jZXJ0ZGF0
# YS9aZXJvREMwMS5jcmwwPAYIKwYBBQUHAQEEMDAuMCwGCCsGAQUFBzAChiBodHRw
# Oi8vcGtpL2NlcnRkYXRhL1plcm9EQzAxLmNydDANBgkqhkiG9w0BAQsFAAOCAQEA
# tyX7aHk8vUM2WTQKINtrHKJJi29HaxhPaHrNZ0c32H70YZoFFaryM0GMowEaDbj0
# a3ShBuQWfW7bD7Z4DmNc5Q6cp7JeDKSZHwe5JWFGrl7DlSFSab/+a0GQgtG05dXW
# YVQsrwgfTDRXkmpLQxvSxAbxKiGrnuS+kaYmzRVDYWSZHwHFNgxeZ/La9/8FdCir
# MXdJEAGzG+9TwO9JvJSyoGTzu7n93IQp6QteRlaYVemd5/fYqBhtskk1zDiv9edk
# mHHpRWf9Xo94ZPEy7BqmDuixm4LdmmzIcFWqGGMo51hvzz0EaE8K5HuNvNaUB/hq
# MTOIB5145K8bFOoKHO4LkTCCBc8wggS3oAMCAQICE1gAAAH5oOvjAv3166MAAQAA
# AfkwDQYJKoZIhvcNAQELBQAwPTETMBEGCgmSJomT8ixkARkWA0xBQjEUMBIGCgmS
# JomT8ixkARkWBFpFUk8xEDAOBgNVBAMTB1plcm9TQ0EwHhcNMTcwOTIwMjE0MTIy
# WhcNMTkwOTIwMjExMzU4WjBpMQswCQYDVQQGEwJVUzELMAkGA1UECBMCUEExFTAT
# BgNVBAcTDFBoaWxhZGVscGhpYTEVMBMGA1UEChMMRGlNYWdnaW8gSW5jMQswCQYD
# VQQLEwJJVDESMBAGA1UEAxMJWmVyb0NvZGUyMIIBIjANBgkqhkiG9w0BAQEFAAOC
# AQ8AMIIBCgKCAQEAxX0+4yas6xfiaNVVVZJB2aRK+gS3iEMLx8wMF3kLJYLJyR+l
# rcGF/x3gMxcvkKJQouLuChjh2+i7Ra1aO37ch3X3KDMZIoWrSzbbvqdBlwax7Gsm
# BdLH9HZimSMCVgux0IfkClvnOlrc7Wpv1jqgvseRku5YKnNm1JD+91JDp/hBWRxR
# 3Qg2OR667FJd1Q/5FWwAdrzoQbFUuvAyeVl7TNW0n1XUHRgq9+ZYawb+fxl1ruTj
# 3MoktaLVzFKWqeHPKvgUTTnXvEbLh9RzX1eApZfTJmnUjBcl1tCQbSzLYkfJlJO6
# eRUHZwojUK+TkidfklU2SpgvyJm2DhCtssFWiQIDAQABo4ICmjCCApYwDgYDVR0P
# AQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMB0GA1UdDgQWBBS5d2bhatXq
# eUDFo9KltQWHthbPKzAfBgNVHSMEGDAWgBSJwtfivYIqfRl/k1i63yftuTKF/jCB
# 6QYDVR0fBIHhMIHeMIHboIHYoIHVhoGubGRhcDovLy9DTj1aZXJvU0NBKDEpLENO
# PVplcm9TQ0EsQ049Q0RQLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNl
# cnZpY2VzLENOPUNvbmZpZ3VyYXRpb24sREM9emVybyxEQz1sYWI/Y2VydGlmaWNh
# dGVSZXZvY2F0aW9uTGlzdD9iYXNlP29iamVjdENsYXNzPWNSTERpc3RyaWJ1dGlv
# blBvaW50hiJodHRwOi8vcGtpL2NlcnRkYXRhL1plcm9TQ0EoMSkuY3JsMIHmBggr
# BgEFBQcBAQSB2TCB1jCBowYIKwYBBQUHMAKGgZZsZGFwOi8vL0NOPVplcm9TQ0Es
# Q049QUlBLENOPVB1YmxpYyUyMEtleSUyMFNlcnZpY2VzLENOPVNlcnZpY2VzLENO
# PUNvbmZpZ3VyYXRpb24sREM9emVybyxEQz1sYWI/Y0FDZXJ0aWZpY2F0ZT9iYXNl
# P29iamVjdENsYXNzPWNlcnRpZmljYXRpb25BdXRob3JpdHkwLgYIKwYBBQUHMAKG
# Imh0dHA6Ly9wa2kvY2VydGRhdGEvWmVyb1NDQSgxKS5jcnQwPQYJKwYBBAGCNxUH
# BDAwLgYmKwYBBAGCNxUIg7j0P4Sb8nmD8Y84g7C3MobRzXiBJ6HzzB+P2VUCAWQC
# AQUwGwYJKwYBBAGCNxUKBA4wDDAKBggrBgEFBQcDAzANBgkqhkiG9w0BAQsFAAOC
# AQEAszRRF+YTPhd9UbkJZy/pZQIqTjpXLpbhxWzs1ECTwtIbJPiI4dhAVAjrzkGj
# DyXYWmpnNsyk19qE82AX75G9FLESfHbtesUXnrhbnsov4/D/qmXk/1KD9CE0lQHF
# Lu2DvOsdf2mp2pjdeBgKMRuy4cZ0VCc/myO7uy7dq0CvVdXRsQC6Fqtr7yob9NbE
# OdUYDBAGrt5ZAkw5YeL8H9E3JLGXtE7ir3ksT6Ki1mont2epJfHkO5JkmOI6XVtg
# anuOGbo62885BOiXLu5+H2Fg+8ueTP40zFhfLh3e3Kj6Lm/NdovqqTBAsk04tFW9
# Hp4gWfVc0gTDwok3rHOrfIY35TGCAfUwggHxAgEBMFQwPTETMBEGCgmSJomT8ixk
# ARkWA0xBQjEUMBIGCgmSJomT8ixkARkWBFpFUk8xEDAOBgNVBAMTB1plcm9TQ0EC
# E1gAAAH5oOvjAv3166MAAQAAAfkwCQYFKw4DAhoFAKB4MBgGCisGAQQBgjcCAQwx
# CjAIoAKAAKECgAAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGC
# NwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFCeXFvJ7cvGxgAbD
# z731QoqfbTPGMA0GCSqGSIb3DQEBAQUABIIBAB/bk6Ytd3Uu9hxjlytTRZLtyFlb
# qFWxiU4GZKP3gISAbpuayWcF+Z0zMpbmvKJ/U2CCKyPBeLdiqkUuq7JB3M7dxfk2
# DYfLIy2GCtDjx14HI+xV/qlID9g/NEjLBY6JHa/94PtFGvMedrDuxznRUet1/yrf
# +AR6YAUwK9mZmDT5/Bxd5ZfEwgDx7I4oMd6fy51RYqtl7bnyX0eAbohR2Mt8hTVz
# SVoNuB7ncJBnsu0F7DdaQF2dubMLQlZU3UzgsTDCbG65NQApEPqZ2d+/1RFPmHB/
# qtTfbxhzBnf9TTzjddF1wsZ8uc0s1+z6+bkPfMT9CVj1abPcDwYFck9PyWE=
# SIG # End signature block