ShieldedVMDeployment/ShieldedVMDeployment.psm1
# Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. function New-ShieldedVM { <# .SYNOPSIS Creates a new shielded virtual machine. .DESCRIPTION Creates a new shielded virtual machine using a prepared template disk and shielding data file. The virtual machine will go through shielded VM provisioning, which re-encrypts the OS volume and may take several minutes to complete. Use the -Wait parameter to observe the progress. .PARAMETER Name Name of the new virtual machine. .PARAMETER TemplateDiskPath Location of the template disk that has been prepared for use with shielded virtual machines. .PARAMETER ShieldingDataFilePath Location of the shielding data file used to configure the shielded VM. .PARAMETER SwitchName Name of network switch to which the VM should be connected. If no switch name is provided, and there is only one switch configured in Hyper-V, the VM will be connected to that switch. .PARAMETER Linux Indicates that the VM will run a Linux-based operating system. .PARAMETER MemoryStartupBytes Amount of memory to allocate to the VM (defaults to 2GB). .PARAMETER CpuCount Number of virtual processors to allocate to the VM (defaults to 2). .PARAMETER VMPath Location to store the resulting VM. If omitted, the default VM path configured in Hyper-V will be used for VM storage. .PARAMETER SpecializationValues Key-value pairs to replace in the shielding data answer file. @ComputerName@ is automatically set using the value of the -Name parameter, but can be overridden if desired. .PARAMETER Wait Shows the progress of the provisioning job and waits to return control until the VM is provisioned. .EXAMPLE New-ShieldedVM -Name 'CorpDC01' -TemplateDiskPath '.\WS2016-Template.vhdx' -ShieldingDataFilePath '.\DC.pdk' -SwitchName 'corpnet' Creates a new Windows shielded VM called "CorpDC01" using the specified templtae disk and shielding data file. .EXAMPLE New-ShieldedVM -Name 'ExampleVM' -TemplateDiskPath '.\template.vhdx' -ShieldingDataFilePath '.\myvm.pdk' -SpecializationValues @{ '@ComputerName@' = 'myVM01' } Creates a new Windows shielded VM with a custom replacement for the @ComputerName@ property in the shielding data answer file. #> [CmdletBinding()] param( [Parameter(Mandatory=$true)] [string] $Name, [Parameter(Mandatory=$true)] [string] $TemplateDiskPath, [Parameter(Mandatory=$true)] [string] $ShieldingDataFilePath, [string] $SwitchName, [switch] $Linux, [ValidateRange(1GB, 1TB)] [Int64] $MemoryStartupBytes = 2GB, [ValidateRange(1,240)] [int] $CpuCount = 2, [string] $VMPath, [System.Collections.IDictionary] $SpecializationValues, [switch] $Wait ) ## Parameter validation # Check for invalid names (relating to file paths assumed later) if ($Name -match '"|<|>|\||\\|/|\*|\?') { throw [System.ArgumentException] "Name cannot contain the following characters: `", ?, *, |, \, /, <, >" } # Ensure template disk exists $TemplateDiskPath = Microsoft.PowerShell.Management\Resolve-Path $TemplateDiskPath -ErrorAction Stop | Microsoft.PowerShell.Management\Convert-Path if (-not (Microsoft.PowerShell.Management\Test-Path $TemplateDiskPath -PathType Leaf) -or $TemplateDiskPath -notlike "*.vhdx") { throw [System.IO.FileNotFoundException] "The template disk path is invalid." } # Ensure shielding data file exists $ShieldingDataFilePath = Microsoft.PowerShell.Management\Resolve-Path $ShieldingDataFilePath -ErrorAction Stop | Microsoft.PowerShell.Management\Convert-Path if (-not (Microsoft.PowerShell.Management\Test-Path $ShieldingDataFilePath -PathType Leaf) -or $ShieldingDataFilePath -notlike "*.pdk") { throw [System.IO.FileNotFoundException] "The shielding data file path is invalid." } # Ensure the switch name is valid if ($SwitchName -and -not (Hyper-V\Get-VMSwitch -Name $SwitchName -ErrorAction SilentlyContinue)) { throw [System.ArgumentException] ("A networking switch with the name '{0}' could not be found on the host." -f $SwitchName) } elseif (-not $SwitchName) { $switches = Hyper-V\Get-VMSwitch if ($switches.Count -eq 1) { Microsoft.PowerShell.Utility\Write-Verbose ("No switch name was provided. The VM will be connected to the '{0}' switch." -f $switches.Name) $SwitchName = $switches.Name } else { throw [System.ArgumentException] ("More than one VM switch was found. Re-run the command and specify one of the following switch names to the -SwitchName parameter: {0}" -f ` [string]::Join(', ', $switches.Name)) } } # Ensure the VM path is valid if ($VMPath) { $VMPath = Microsoft.PowerShell.Management\Resolve-Path $VMPath -ErrorAction Stop | Microsoft.PowerShell.Management\Convert-Path if (-not (Microsoft.PowerShell.Management\Test-Path $VMPath -PathType Container)) { throw [System.IO.DirectoryNotFoundException] "The VM path is not a valid directory." } $VhdDirectory = Microsoft.PowerShell.Management\Join-Path $VMPath "Virtual Hard Disks" if (-not (Microsoft.PowerShell.Management\Test-Path $VhdDirectory)) { Microsoft.PowerShell.Utility\Write-Verbose ("Creating directory for VHD at '{0}'" -f $VhdDirectory) $null = Microsoft.PowerShell.Management\New-Item $VhdDirectory -ItemType Directory } } else { $vmroot = (Hyper-V\Get-VMHost).VirtualMachinePath $VMPath = Microsoft.PowerShell.Management\Join-Path $vmroot $Name if ((Microsoft.PowerShell.Management\Test-Path $VMPath)) { foreach ($i in 1..999) { $samplePath = "{0}-{1:D3}" -f $Name, $i $VMPath = Microsoft.PowerShell.Management\Join-Path $vmroot $samplePath if (-not (Microsoft.PowerShell.Management\Test-Path $VMPath)) { break } } } Microsoft.PowerShell.Utility\Write-Verbose ("Creating a VM directory at '{0}'" -f $VMPath) $null = Microsoft.PowerShell.Management\New-Item -Path $VMPath -ItemType Directory -ErrorAction Stop $VhdDirectory = Microsoft.PowerShell.Management\Join-Path $VMPath "Virtual Hard Disks" $null = Microsoft.PowerShell.Management\New-Item -Path $VhdDirectory -ItemType Directory -ErrorAction Stop } # Ensure specialization values are not null if ($SpecializationValues) { foreach ($key in $SpecializationValues.Keys) { if ($key -isnot [string] -or $key -notlike "@*@") { throw [System.ArgumentException] ("Specialization key '{0}' is invalid. All specialization keys must be in the form '@KeyName@'." -f $key) } $value = $SpecializationValues.$key if ($value -isnot [string] -or [string]::IsNullOrEmpty($value)) { throw [System.ArgumentException] ("The value for specialization key '{0}' is invalid. Values must be non-empty strings." -f $key) } } } ## Create the VM $VHDPath = Microsoft.PowerShell.Management\Join-Path $VhdDirectory "$Name-OS.vhdx" Microsoft.PowerShell.Utility\Write-Verbose ("Copying the template disk to '{0}'" -f $VHDPath) Microsoft.PowerShell.Management\Copy-Item -Path $TemplateDiskPath -Destination $VHDPath -ErrorAction Stop Microsoft.PowerShell.Utility\Write-Verbose "Creating the new VM" $vm = Hyper-V\New-VM -Name $Name -Generation 2 -Path $VMPath -VhdPath $VHDPath -SwitchName $SwitchName -MemoryStartupBytes $MemoryStartupBytes -ErrorAction Stop Hyper-V\Set-VMProcessor -VM $vm -Count $CpuCount if ($Linux) { Hyper-V\Set-VMFirmware -VM $vm -SecureBootTemplate OpenSourceShieldedVM -ErrorAction Stop } # Attach the key protector $kp = Get-KeyProtectorFromShieldingDataFile -ShieldingDataFilePath $ShieldingDataFilePath Hyper-V\Set-VMKeyProtector -VM $vm -KeyProtector $kp -ErrorAction Stop # Get the security data from the PDK file try { $pdk = CimCmdlets\Invoke-CimMethod -ClassName Msps_ProvisioningFileProcessor -Namespace root\msps -MethodName PopulateFromFile -Arguments @{ FilePath = $ShieldingDataFilePath } -Verbose:$false -ErrorAction Stop $cimvm = CimCmdlets\Get-CimInstance -Namespace root\virtualization\v2 -Class Msvm_ComputerSystem -Filter "Name = '$($vm.VMId)'" -Verbose:$false -ErrorAction Stop $vsd = CimCmdlets\Get-CimAssociatedInstance -InputObject $cimvm -ResultClassName "Msvm_VirtualSystemSettingData" -Verbose:$false -ErrorAction Stop $ssd = CimCmdlets\Get-CimAssociatedInstance -InputObject $vsd -ResultClassName "Msvm_SecuritySettingData" -Verbose:$false -ErrorAction Stop $ss = CimCmdlets\Get-CimAssociatedInstance -InputObject $cimvm -ResultClassName "Msvm_SecurityService" -Verbose:$false -ErrorAction Stop $cimSerializer = [Microsoft.Management.Infrastructure.Serialization.CimSerializer]::Create() $ssdString = [System.Text.Encoding]::Unicode.GetString($cimSerializer.Serialize($ssd, [Microsoft.Management.Infrastructure.Serialization.InstanceSerializationOptions]::None)) } catch { throw "A security policy could not be created from the shielding data file.`n`n$($_.Message)" } # Apply the VM security policy and enable the VM TPM Microsoft.PowerShell.Utility\Write-Verbose "Enabling VM TPM" $null = CimCmdlets\Invoke-CimMethod -InputObject $ss -MethodName SetSecurityPolicy -Arguments @{ "SecuritySettingData" = $ssdString; "SecurityPolicy" = $pdk.ProvisioningFile.PolicyData } -Verbose:$false -ErrorAction Stop Hyper-V\Enable-VMTPM -VM $vm -ErrorAction Stop # Create the fabric specialization keyfile Microsoft.PowerShell.Utility\Write-Verbose "Creating specialization data file" $fskPath = Microsoft.PowerShell.Management\Join-Path $VMPath "SpecializationData.fsk" $simpleComputerName = $Name -replace '[^\w-]', '' $fskParams = @{ '@ComputerName@' = $simpleComputerName } if ($SpecializationValues) { foreach ($key in $SpecializationValues.Keys) { $fskParams.$key = $SpecializationValues.$key } } ShieldedVMProvisioning\New-ShieldedVMSpecializationDataFile -ShieldedVMSpecializationDataFilePath $fskPath -SpecializationDataPairs $fskParams -ErrorAction Stop # Provision the VM Microsoft.PowerShell.Utility\Write-Verbose "Initiating shielded VM provisioning process" $provisioningJob = ShieldedVMProvisioning\Initialize-ShieldedVM -VM $vm -ShieldingDataFilePath $ShieldingDataFilePath -ShieldedVMSpecializationDataFilePath $fskPath -ErrorAction Stop if ($Wait) { do { $status = ShieldedVMProvisioning\Get-ShieldedVMProvisioningStatus -VM $vm Write-Progress -Activity ("Provisioning shielded VM '{0}'" -f $Name) -PercentComplete $status.PercentComplete -Status ("{0}% complete" -f $Status.PercentComplete) Microsoft.PowerShell.Utility\Start-Sleep -Milliseconds 1500 } while ($status -and $status.Status -ne 'Error' -and $status.PercentComplete -lt 100) if ($status) { Microsoft.PowerShell.Utility\Write-Output $status.JobStatus if ($status.ErrorDescription) { Microsoft.PowerShell.Utility\Write-Error $status.ErrorDescription } } else { Microsoft.Powershell.Utility\Write-Output "Unable to check the status of the shielded VM provisioning process." } } else { return $provisioningJob } } # SIG # Begin signature block # MIIkFwYJKoZIhvcNAQcCoIIkCDCCJAQCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCSzQibPD549aci # Fk5wo5wVe51UUlMVgKLctjmKoIzBiqCCDYMwggYBMIID6aADAgECAhMzAAAAxOmJ # +HqBUOn/AAAAAADEMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMTcwODExMjAyMDI0WhcNMTgwODExMjAyMDI0WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQCIirgkwwePmoB5FfwmYPxyiCz69KOXiJZGt6PLX4kvOjMuHpF4+nypH4IBtXrL # GrwDykbrxZn3+wQd8oUK/yJuofJnPcUnGOUoH/UElEFj7OO6FYztE5o13jhwVG87 # 7K1FCTBJwb6PMJkMy3bJ93OVFnfRi7uUxwiFIO0eqDXxccLgdABLitLckevWeP6N # +q1giD29uR+uYpe/xYSxkK7WryvTVPs12s1xkuYe/+xxa8t/CHZ04BBRSNTxAMhI # TKMHNeVZDf18nMjmWuOF9daaDx+OpuSEF8HWyp8dAcf9SKcTkjOXIUgy+MIkogCy # vlPKg24pW4HvOG6A87vsEwvrAgMBAAGjggGAMIIBfDAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUy9ZihM9gOer/Z8Jc0si7q7fDE5gw # UgYDVR0RBEswSaRHMEUxDTALBgNVBAsTBE1PUFIxNDAyBgNVBAUTKzIzMDAxMitj # ODA0YjVlYS00OWI0LTQyMzgtODM2Mi1kODUxZmEyMjU0ZmMwHwYDVR0jBBgwFoAU # SG5k5VAF04KqFzc3IrVtqMp1ApUwVAYDVR0fBE0wSzBJoEegRYZDaHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljQ29kU2lnUENBMjAxMV8yMDEx # LTA3LTA4LmNybDBhBggrBgEFBQcBAQRVMFMwUQYIKwYBBQUHMAKGRWh0dHA6Ly93 # d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljQ29kU2lnUENBMjAxMV8y # MDExLTA3LTA4LmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQAG # Fh/bV8JQyCNPolF41+34/c291cDx+RtW7VPIaUcF1cTL7OL8mVuVXxE4KMAFRRPg # mnmIvGar27vrAlUjtz0jeEFtrvjxAFqUmYoczAmV0JocRDCppRbHukdb9Ss0i5+P # WDfDThyvIsoQzdiCEKk18K4iyI8kpoGL3ycc5GYdiT4u/1cDTcFug6Ay67SzL1BW # XQaxFYzIHWO3cwzj1nomDyqWRacygz6WPldJdyOJ/rEQx4rlCBVRxStaMVs5apao # pIhrlihv8cSu6r1FF8xiToG1VBpHjpilbcBuJ8b4Jx/I7SCpC7HxzgualOJqnWmD # oTbXbSD+hdX/w7iXNgn+PRTBmBSpwIbM74LBq1UkQxi1SIV4htD50p0/GdkUieeN # n2gkiGg7qceATibnCCFMY/2ckxVNM7VWYE/XSrk4jv8u3bFfpENryXjPsbtrj4Ns # h3Kq6qX7n90a1jn8ZMltPgjlfIOxrbyjunvPllakeljLEkdi0iHv/DzEMQv3Lz5k # pTdvYFA/t0SQT6ALi75+WPbHZ4dh256YxMiMy29H4cAulO2x9rAwbexqSajplnbI # vQjE/jv1rnM3BrJWzxnUu/WUyocc8oBqAU+2G4Fzs9NbIj86WBjfiO5nxEmnL9wl # iz1e0Ow0RJEdvJEMdoI+78TYLaEEAo5I+e/dAs8DojCCB3owggVioAMCAQICCmEO # kNIAAAAAAAMwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQI # EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv # ZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmlj # YXRlIEF1dGhvcml0eSAyMDExMB4XDTExMDcwODIwNTkwOVoXDTI2MDcwODIxMDkw # OVowfjELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT # B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UE # AxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMTCCAiIwDQYJKoZIhvcN # AQEBBQADggIPADCCAgoCggIBAKvw+nIQHC6t2G6qghBNNLrytlghn0IbKmvpWlCq # uAY4GgRJun/DDB7dN2vGEtgL8DjCmQawyDnVARQxQtOJDXlkh36UYCRsr55JnOlo # XtLfm1OyCizDr9mpK656Ca/XllnKYBoF6WZ26DJSJhIv56sIUM+zRLdd2MQuA3Wr # aPPLbfM6XKEW9Ea64DhkrG5kNXimoGMPLdNAk/jj3gcN1Vx5pUkp5w2+oBN3vpQ9 # 7/vjK1oQH01WKKJ6cuASOrdJXtjt7UORg9l7snuGG9k+sYxd6IlPhBryoS9Z5JA7 # La4zWMW3Pv4y07MDPbGyr5I4ftKdgCz1TlaRITUlwzluZH9TupwPrRkjhMv0ugOG # jfdf8NBSv4yUh7zAIXQlXxgotswnKDglmDlKNs98sZKuHCOnqWbsYR9q4ShJnV+I # 4iVd0yFLPlLEtVc/JAPw0XpbL9Uj43BdD1FGd7P4AOG8rAKCX9vAFbO9G9RVS+c5 # oQ/pI0m8GLhEfEXkwcNyeuBy5yTfv0aZxe/CHFfbg43sTUkwp6uO3+xbn6/83bBm # 4sGXgXvt1u1L50kppxMopqd9Z4DmimJ4X7IvhNdXnFy/dygo8e1twyiPLI9AN0/B # 4YVEicQJTMXUpUMvdJX3bvh4IFgsE11glZo+TzOE2rCIF96eTvSWsLxGoGyY0uDW # iIwLAgMBAAGjggHtMIIB6TAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQUSG5k # 5VAF04KqFzc3IrVtqMp1ApUwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYD # VR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUci06AjGQQ7kU # BU7h6qfHMdEjiTQwWgYDVR0fBFMwUTBPoE2gS4ZJaHR0cDovL2NybC5taWNyb3Nv # ZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0MjAxMV8yMDExXzAz # XzIyLmNybDBeBggrBgEFBQcBAQRSMFAwTgYIKwYBBQUHMAKGQmh0dHA6Ly93d3cu # bWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0MjAxMV8yMDExXzAz # XzIyLmNydDCBnwYDVR0gBIGXMIGUMIGRBgkrBgEEAYI3LgMwgYMwPwYIKwYBBQUH # AgEWM2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvZG9jcy9wcmltYXJ5 # Y3BzLmh0bTBABggrBgEFBQcCAjA0HjIgHQBMAGUAZwBhAGwAXwBwAG8AbABpAGMA # eQBfAHMAdABhAHQAZQBtAGUAbgB0AC4gHTANBgkqhkiG9w0BAQsFAAOCAgEAZ/KG # pZjgVHkaLtPYdGcimwuWEeFjkplCln3SeQyQwWVfLiw++MNy0W2D/r4/6ArKO79H # qaPzadtjvyI1pZddZYSQfYtGUFXYDJJ80hpLHPM8QotS0LD9a+M+By4pm+Y9G6XU # tR13lDni6WTJRD14eiPzE32mkHSDjfTLJgJGKsKKELukqQUMm+1o+mgulaAqPypr # WEljHwlpblqYluSD9MCP80Yr3vw70L01724lruWvJ+3Q3fMOr5kol5hNDj0L8giJ # 1h/DMhji8MUtzluetEk5CsYKwsatruWy2dsViFFFWDgycScaf7H0J/jeLDogaZiy # WYlobm+nt3TDQAUGpgEqKD6CPxNNZgvAs0314Y9/HG8VfUWnduVAKmWjw11SYobD # HWM2l4bf2vP48hahmifhzaWX0O5dY0HjWwechz4GdwbRBrF1HxS+YWG18NzGGwS+ # 30HHDiju3mUv7Jf2oVyW2ADWoUa9WfOXpQlLSBCZgB/QACnFsZulP0V3HjXG0qKi # n3p6IvpIlR+r+0cjgPWe+L9rt0uX4ut1eBrs6jeZeRhL/9azI2h15q/6/IvrC4Dq # aTuv/DDtBEyO3991bWORPdGdVk5Pv4BXIqF4ETIheu9BCrE/+6jMpF3BoYibV3FW # TkhFwELJm3ZbCoBIa/15n8G9bW1qyVJzEw16UM0xghXqMIIV5gIBATCBlTB+MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNy # b3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExAhMzAAAAxOmJ+HqBUOn/AAAAAADE # MA0GCWCGSAFlAwQCAQUAoIHcMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwG # CisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCBTQ/sP # uJP1oeFG37+H9vqx5plWIJE42bN32bjjtXY4UTBwBgorBgEEAYI3AgEMMWIwYKAq # gCgARwB1AGEAcgBkAGUAZAAgAEYAYQBiAHIAaQBjACAAVABvAG8AbABzoTKAMGh0 # dHBzOi8vZ2l0aHViLmNvbS9NaWNyb3NvZnQvR3VhcmRlZEZhYnJpY1Rvb2xzLzAN # BgkqhkiG9w0BAQEFAASCAQAAjdTR8abyq3EdZg1oD8u4FgWTgJ+UQzppcoiHQWvV # pEnTv93eixLa78dNr4qcp+yh9iLOEt2E43OvN+pAwr8TRRvZDZ2TBW+TuhHL4OxG # ENV+hvyRtpUJSG5k0nKhijSs5BTbLN7wgpwPSxh6K9r+N/wrCWmMcQZ83sv8l/Tz # OR/r1RTl0LzYh15TavOAsB7p/4lGvWkBQtuwPnQiYkWCjoahRiVgY/yTYdhVXfej # xCYuG1RdjF+Pq5QsbenMH306KzR1HqK33kvvpBCbA9f+LrQl67PaJlxCOsY4s7x4 # kIpkg2AxkvXleuK7fofeqTHdSUgTqOEZ+voHZ2U5mayroYITRjCCE0IGCisGAQQB # gjcDAwExghMyMIITLgYJKoZIhvcNAQcCoIITHzCCExsCAQMxDzANBglghkgBZQME # AgEFADCCATkGCyqGSIb3DQEJEAEEoIIBKASCASQwggEgAgEBBgorBgEEAYRZCgMB # MDEwDQYJYIZIAWUDBAIBBQAEIIr3tgp2OCQUE4n6bfn0yhrfe4WQZPHu7CuFIV8q # GcNIAgZaTqtk+A4YEzIwMTgwMTI1MTcyMTI0Ljc3MVowBIACAfSggbikgbUwgbIx # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xDDAKBgNVBAsTA0FP # QzEnMCUGA1UECxMebkNpcGhlciBEU0UgRVNOOjZCRjYtMkQ1Mi05MkMxMSUwIwYD # VQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloIIOzTCCBnEwggRZoAMC # AQICCmEJgSoAAAAAAAIwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENl # cnRpZmljYXRlIEF1dGhvcml0eSAyMDEwMB4XDTEwMDcwMTIxMzY1NVoXDTI1MDcw # MTIxNDY1NVowfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAO # BgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEm # MCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwggEiMA0GCSqG # SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpHQ28dxGKOiDs/BOX9fp/aZRrdFQQ1aUK # AIKF++18aEssX8XD5WHCdrc+Zitb8BVTJwQxH0EbGpUdzgkTjnxhMFmxMEQP8WCI # hFRDDNdNuDgIs0Ldk6zWczBXJoKjRQ3Q6vVHgc2/JGAyWGBG8lhHhjKEHnRhZ5Ff # gVSxz5NMksHEpl3RYRNuKMYa+YaAu99h/EbBJx0kZxJyGiGKr0tkiVBisV39dx89 # 8Fd1rL2KQk1AUdEPnAY+Z3/1ZsADlkR+79BL/W7lmsqxqPJ6Kgox8NpOBpG2iAg1 # 6HgcsOmZzTznL0S6p/TcZL2kAcEgCZN4zfy8wMlEXV4WnAEFTyJNAgMBAAGjggHm # MIIB4jAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQU1WM6XIoxkPNDe3xGG8Uz # aFqFbVUwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8G # A1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9lJBb186aGMQw # VgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9j # cmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoGCCsGAQUF # BwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3Br # aS9jZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcnQwgaAGA1UdIAEB/wSB # lTCBkjCBjwYJKwYBBAGCNy4DMIGBMD0GCCsGAQUFBwIBFjFodHRwOi8vd3d3Lm1p # Y3Jvc29mdC5jb20vUEtJL2RvY3MvQ1BTL2RlZmF1bHQuaHRtMEAGCCsGAQUFBwIC # MDQeMiAdAEwAZQBnAGEAbABfAFAAbwBsAGkAYwB5AF8AUwB0AGEAdABlAG0AZQBu # AHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQAH5ohRDeLG4Jg/gXEDPZ2joSFvs+um # zPUxvs8F4qn++ldtGTCzwsVmyWrf9efweL3HqJ4l4/m87WtUVwgrUYJEEvu5U4zM # 9GASinbMQEBBm9xcF/9c+V4XNZgkVkt070IQyK+/f8Z/8jd9Wj8c8pl5SpFSAK84 # Dxf1L3mBZdmptWvkx872ynoAb0swRCQiPM/tA6WWj1kpvLb9BOFwnzJKJ/1Vry/+ # tuWOM7tiX5rbV0Dp8c6ZZpCM/2pif93FSguRJuI57BlKcWOdeyFtw5yjojz6f32W # apB4pm3S4Zz5Hfw42JT0xqUKloakvZ4argRCg7i1gJsiOCC1JeVk7Pf0v35jWSUP # ei45V3aicaoGig+JFrphpxHLmtgOR5qAxdDNp9DvfYPw4TtxCd9ddJgiCGHasFAe # b73x4QDf5zEHpJM692VHeOj4qEir995yfmFrb3epgcunCaw5u+zGy9iCtHLNHfS4 # hQEegPsbiSpUObJb2sgNVZl6h3M7COaYLeqN4DMuEin1wC9UJyH3yKxO2ii4sanb # lrKnQqLJzxlBTeCG+SqaoxFmMNO7dDJL32N79ZmKLxvHIa9Zta7cRDyXUHHXodLF # VeNp3lfB0d4wwP3M5k37Db9dT+mdHhk4L7zPWAUu7w2gUDXa7wknHNWzfjUeCLra # NtvTX4/edIhJEjCCBNkwggPBoAMCAQICEzMAAACkvT3FljMeiUsAAAAAAKQwDQYJ # KoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcNMTYw # OTA3MTc1NjUwWhcNMTgwOTA3MTc1NjUwWjCBsjELMAkGA1UEBhMCVVMxEzARBgNV # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv # c29mdCBDb3Jwb3JhdGlvbjEMMAoGA1UECxMDQU9DMScwJQYDVQQLEx5uQ2lwaGVy # IERTRSBFU046NkJGNi0yRDUyLTkyQzExJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1l # LVN0YW1wIFNlcnZpY2UwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCw # K5fEm5bGITRq5cTlMrYt+/M/P40ZFqxpvwbqaxoFhCgUK329U99XH8xEfrWsafmN # eBrTaJmGVZC60y0yqpwIEnyUiCaSvaZ6CXz3YzbWihPBKS6Wd82AREgnxMdGnGPh # U8WnOAuNtbsSKU5twZl8QeAqAShXUP+QIq2/WCQ0z08xla6Wh9J6hOSV2zVlmndP # AIUg003nlcbBAnqRdbCk1cj9wtoiabp9YqNvhIjRAOleW63vwRbSYyFf/lCVouw3 # 2uqaM1SXGkd9zsvsUt29tQXaZtWYU13b9OeAIJPY7sg1Buke/x+HgJN3CIrSzUAO # 4jVHN7+pCkduYf11Q5qXAgMBAAGjggEbMIIBFzAdBgNVHQ4EFgQULm5uaXCOkRIv # v3ogDMW2AdSC8sQwHwYDVR0jBBgwFoAU1WM6XIoxkPNDe3xGG8UzaFqFbVUwVgYD # VR0fBE8wTTBLoEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwv # cHJvZHVjdHMvTWljVGltU3RhUENBXzIwMTAtMDctMDEuY3JsMFoGCCsGAQUFBwEB # BE4wTDBKBggrBgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9j # ZXJ0cy9NaWNUaW1TdGFQQ0FfMjAxMC0wNy0wMS5jcnQwDAYDVR0TAQH/BAIwADAT # BgNVHSUEDDAKBggrBgEFBQcDCDANBgkqhkiG9w0BAQsFAAOCAQEAKZn2DHYGLLOG # S1cjbuDpK1AzTgxlclx/ZvO1+v7MExqCN8ZHLEFOA0NTbCBhfTxLid3sC9XiVJul # 34bNofldG8/WmoDVVAzyU5hYn52Sh811l/gtPpY3JmgRCrNSb8itGWLGuJQ059Bs # oSq/ZtVGfDbs0yZQDwi36e0A8vGfIKwEMnTHjq7KYOGDzODhotCR9nO5m5g+KxhP # Cl2+42WtbjxX4ZYooWAKGRbFnl5Xwi1XvJgHBIbROoj9I4NsLozTXALHArqUYakW # aAeNoMbqWB3L0HX0XW+Fz4wQM0kheCM5Kuyeq6+WSR0H1bPZJH4Rr7PFItEpzSth # qQ23LRtIVaGCA3cwggJfAgEBMIHioYG4pIG1MIGyMQswCQYDVQQGEwJVUzETMBEG # A1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWlj # cm9zb2Z0IENvcnBvcmF0aW9uMQwwCgYDVQQLEwNBT0MxJzAlBgNVBAsTHm5DaXBo # ZXIgRFNFIEVTTjo2QkY2LTJENTItOTJDMTElMCMGA1UEAxMcTWljcm9zb2Z0IFRp # bWUtU3RhbXAgU2VydmljZaIlCgEBMAkGBSsOAwIaBQADFQBhgZf3oouz70ZSQ5uJ # fYbA9z/AdKCBwTCBvqSBuzCBuDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw # b3JhdGlvbjEMMAoGA1UECxMDQU9DMScwJQYDVQQLEx5uQ2lwaGVyIE5UUyBFU046 # MzIxQS02MTNGLTQwNjUxKzApBgNVBAMTIk1pY3Jvc29mdCBUaW1lIFNvdXJjZSBN # YXN0ZXIgQ2xvY2swDQYJKoZIhvcNAQEFBQACBQDeFI/PMCIYDzIwMTgwMTI1MTcx # ODA3WhgPMjAxODAxMjYxNzE4MDdaMHcwPQYKKwYBBAGEWQoEATEvMC0wCgIFAN4U # j88CAQAwCgIBAAICAaYCAf8wBwIBAAICGBswCgIFAN4V4U8CAQAwNgYKKwYBBAGE # WQoEAjEoMCYwDAYKKwYBBAGEWQoDAaAKMAgCAQACAwehIKEKMAgCAQACAx6EgDAN # BgkqhkiG9w0BAQUFAAOCAQEASApt/uwdytJusY+uKi1f7Sp+UQkMZDd66S0p8a2l # E5a/C+D1THQybREel2KhgVUaXUeP48EJiVtE3rJ2q99+rWC2LjsYgxowvhGXpJSl # EKh8i3domK1PnuZ3XEL/8Mdln8r0Krt4+AeHNWGr0NiiHnQZFsYvzlEkr/2UExdf # zAQoQ9hAzMS7PfWNGMVPEwrNhH1HTFmvrOhU3CqmXyFmgp0jF85WZQn/LkJSvR9j # KOSNH6OALk0rVfYp9AnEAu5ba5u+GNnbDZbM+MRBqYRdGEUBjyC/pRQ60HL5nyjK # LRk1CacpwjT78PYblUrG0fTiNij/mTwFZGu1XprbCsQG7jGCAvUwggLxAgEBMIGT # MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMT # HU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAApL09xZYzHolLAAAA # AACkMA0GCWCGSAFlAwQCAQUAoIIBMjAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQ # AQQwLwYJKoZIhvcNAQkEMSIEIDgmyV06oKlvimX+7Qrc1NG7IRFQGsTIi1i0gu4j # zPMYMIHiBgsqhkiG9w0BCRACDDGB0jCBzzCBzDCBsQQUYYGX96KLs+9GUkObiX2G # wPc/wHQwgZgwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv # bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 # aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAA # AKS9PcWWMx6JSwAAAAAApDAWBBQoTuLFWo5T+7qTSuerRx/3HONNZTANBgkqhkiG # 9w0BAQsFAASCAQCTl4EONqk5iP17aW2HHXMYlwW626WSchA7EbJMRfJhrLFtfkf4 # dF5yrs1NyCZUykL9fWC3NOHWushYXarVS/do/6QdnwfYrNmNqu61MgUKoZRnSqPq # /aBVwFMlXUvEf0lBZeFODgQViGdrGBGZaz2yxtrYbJPW+bgITes0iCcH/WRq1vGG # cJbG4Zml1fhAGzHGbZO4V8PUmC40RTODMv26YUYSdEAcaoQECVxkXdBRAPTzF4WP # 6n0vFqz4mrQunyBPDFKA7DVZyli7LHx32TLXyxzS4fkM+GOhRlvFRHwvu6WuHO0y # 1Isn1qh1djRcic74RC1fuARLC/rngGg6deTY # SIG # End signature block |