Upgrade-Gen1ToTL.ps1 (1,141 lines of code) (raw):
<#
.NOTES
============================================================================================================
Copyright (c) Microsoft Corporation. All rights reserved.
File: Upgrade-Gen1ToTL.ps1
Purpose: Gen1 to Trusted launch upgrade
Pre-Reqs: Windows PowerShell version 7.2+ and Azure PowerShell Module version 12.2+
Version: 3.0.1
============================================================================================================
DISCLAIMER
============================================================================================================
This script is not supported under any Microsoft standard support program or service.
This script is provided AS IS without warranty of any kind.
Microsoft further disclaims all implied warranties including, without limitation, any
implied warranties of merchantability or of fitness for a particular purpose.
The entire risk arising out of the use or performance of the script
and documentation remains with you. In no event shall Microsoft, its authors,
or anyone else involved in the creation, production, or delivery of the
script be liable for any damages whatsoever (including, without limitation,
damages for loss of business profits, business interruption, loss of business
information, or other pecuniary loss) arising out of the use of or inability
to use the sample scripts or documentation, even if Microsoft has been
advised of the possibility of such damages.
============================================================================================================
.SYNOPSIS
Upgrades Azure VM from Gen1 to Trusted Launch Configuration with OS State preserved.
Script Version - 3.0.1
.DESCRIPTION
PREREQUISITES:
1. Az.Compute, Az.Accounts PowerShell Module
2. Current Gen 1 VM is running.
3. VM Contributor rights on resource group.
4. If backup is enabled, Gen1 VM backup is configured with Enhanced policy.
1. Existing backup can be migrated to Enhanced policy using preview https://aka.ms/formBackupPolicyMigration.
5. ASR is not enabled for Gen1 VM. ASR currently does not supports Trusted launch VMs.
6. Azure IaaS VM Agent should be installed and healthy.
7. For Linux VM only, On-board to Gen1 to Trusted launch VM private preview at https://aka.ms/Gen1ToTLUpgrade.
STEPS:
1. Create csv with VMName, ResourceGroupName, EnableSecureBoot parameters.
2. Execute PowerShell script which will:
1. Check if current VM Size is compatible with Trusted launch.
2. Execute MBR to GPT OS Disk boot partition conversion.
3. De-allocate or Stop VM.
4. Update VM to Gen2-Trusted launch.
5. Start VM.
3. Validate health of workload and virtual machine.
.PARAMETER subscriptionId
Subscription ID for Gen1 VM.
.PARAMETER tenantDomain
Primary AAD Domain Name for authentication. (For example, contoso.onmicrosoft.com)
.PARAMETER csvLocation
Local file path location of csv containing vmName, vmResourceGroupName, enableSecureBoot details.
.PARAMETER batchSize
(Optional) Number of machines which should be processed in parallel. Default set to 5.
.PARAMETER useCloudshell
(Optional) Use cloud shell in Azure Portal for script execution.
.PARAMETER vmName
(Csv input parameter) Resource Name of Gen1 VM to be upgraded
.PARAMETER vmResourceGroupName
(Csv input parameter) Resource Group for Gen1 VM.
.PARAMETER enableSecureBoot
(Csv input parameter) If target Trusted Launch VM should be deployed with Secure Boot enabled (TRUE) or disabled (FALSE). This option should be disabled if VM is hosting custom or unsigned boot drivers which cannot be attested.
.EXAMPLE
.\Upgrade-Gen1ToTL.ps1 -subscriptionId $subscriptionId -tenantDomain contoso.onmicrosoft.com -csvLocation "C:\Temp\sampleCsv.csv"
Upgrade all VMs provided in csv from Gen1 to Trusted launch with specific parameter values.
.LINK
https://aka.ms/TrustedLaunch
.LINK
https://aka.ms/TrustedLaunchUpgrade
#>
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = "High")]
param (
[Parameter(Mandatory = $true, HelpMessage = "Azure Subscription Id or Guid")]
[string][ValidateNotNullOrEmpty()]$subscriptionId,
[Parameter(Mandatory = $true, HelpMessage = "Azure Tenant domain")]
[string][ValidateNotNullOrEmpty()]$tenantDomain,
[Parameter(Mandatory=$false, HelpMessage = "The cloud environment where the VM exists.")]
[ValidateSet("AzureCloud","AzureChinaCloud","AzureUSGovernment")]
[string]$environment='AzureCloud',
[Parameter(Mandatory = $true, HelpMessage = "Location of csv containing Gen1 VM(s) details - vmName, vmResourceGroupName, EnableSecureBoot.")]
[string][ValidateNotNullOrEmpty()]$csvLocation,
[Parameter(Mandatory = $false, HelpMessage = "Number of machines which should be processed in parallel. Default set to 5.")]
[int][ValidateNotNullOrEmpty()]$batchSize,
[Parameter(Mandatory = $false, HelpMessage = "Use cloud shell in Azure Portal for script execution.")]
[switch]$useCloudshell
)
#region - Validate Pre-Requisites
try {
New-Variable -Name 'ERRORLEVEL' -Value 0 -Scope Script -Force
$PSVersion = $PSVersionTable.PSVersion
if ($PSVersion.Major -gt 7 -or ($PSVersion.Major -eq 7 -and $PSVersion.Minor -ge 2)) {
$messagetxt = "[Common] INFO: PowerShell version is greater than 7.2"
Write-Output $messageTxt
} else {
$messagetxt = "[Common] ERROR: PowerShell version is not greater than 7.2 and does not meets requirements."
Write-Error $messagetxt
Set-Variable -Name ERRORLEVEL -Value -1 -Scope Script -Force
}
if ($useCloudshell) {
$workingDirectory = [system.string]::concat((Get-Location).Path, "/Gen1-TrustedLaunch-Upgrade")
} else {
if ((Test-Path $env:UserProfile -ErrorAction SilentlyContinue) -eq $true) {
$workingDirectory = "$env:UserProfile\Gen1-TrustedLaunch-Upgrade"
} else {
$messageTxt = "[Common] INFO: User profile directory not found. Defaulting to script execution location."
Write-Output $messagetxt
$workingDirectory = [system.string]::concat((Get-Location).Path, "\Gen1-TrustedLaunch-Upgrade")
}
}
if ((Test-Path $workingDirectory) -eq $true) {
$messageTxt = "[Common] INFO: Working Directory Already Setup $workingDirectory"
Write-Output $messageTxt
}
else {
$messageTxt = "[Common] INFO: Setting up working dir $workingDirectory"
Write-Output $messageTxt
New-Item -ItemType Directory -Path (Split-Path $workingDirectory -Parent) -Name (Split-Path $workingDirectory -Leaf) -ErrorAction 'Stop' | Out-Null
}
If ($useSignedScript -and !($outputStorageAccountName)) {
$messagetxt = "[Common] ERROR: Output storage account name is required if useSignedScript is set."
Write-Error $messageTxt
Set-Variable -Name ERRORLEVEL -Value -1 -Scope Script -Force
}
$azPsModule = @(@{
ModuleName = 'Az.Accounts'
Version = [version]"2.8.0"
},
@{
ModuleName = 'Az.Compute'
Version = [version]"6.0.0"
},
@{
ModuleName = 'Az.Storage'
Version = [version]"5.8.0"
})
foreach ($azModule in $azPsModule) {
$module = Get-Module -ListAvailable -Name $azModule.ModuleName
# Check if the module is available
if ($module) {
# Check if the module version is greater than or equal to the minimum version
if ($module.Version -ge $azModule.Version) {
$messagetxt = "[Common] INFO: Module $($azModule.ModuleName) with minimum version $($azModule.Version) is available."
Write-Output $messageTxt
}
else {
$messagetxt = "[Common] WARN: Module $($azModule.ModuleName) is available, but its version is lower than the minimum version $($azModule.Version). Upgrading module on local machine."
Write-warning $messageTxt
Update-Module $($azModule.ModuleName) -ErrorAction 'Stop' -Confirm:$false -Force
}
}
else {
$messagetxt = "[Common] WARN: Module $($azModule.ModuleName) is not available, proceeding with $($azModule.ModuleName) install."
Write-warning $messageTxt
Install-Module -Name $($azModule.ModuleName) -Repository PSGallery -Force -Confirm:$false -ErrorAction 'Stop'
}
}
}
catch [system.exception] {
$messageTxt = '[Common] ERROR: Error Exception Occurred' + "`n$($psitem.Exception.Message)" + "`nError Caused By: $(($psitem.InvocationInfo.Line).Trim())"
Write-Output $messageTxt
$ERRORLEVEL = -1
}
#endregion
#region - Connect Azure Subscription
If ($ERRORLEVEL -eq 0) {
try {
$messageTxt = "[Common] INFO: Connecting to Subscription $subscriptionId under $tenantDomain"
Write-Output $messageTxt
Update-AzConfig -EnableLoginByWam $false -ErrorAction 'Stop'
#region - Enable-AzAccount()
if ($useCloudshell) {
Set-AzContext -SubscriptionId $subscriptionId -tenant $tenantDomain -ErrorAction 'Stop'
} else {
$azureProfile = "$workingDirectory\AzureProfile-$subscriptionId.json"
$paramTestPath = @{
Path = $($azureProfile)
ErrorAction = 'Stop'
}
if (Test-Path @paramTestPath) {
$messageTxt = "[Common] INFO: Clearing previously cached Azure profile JSON"
Write-Output $messageTxt
Remove-Item -Path $azureProfile -Force -Confirm:$false -ErrorAction 'Stop' | Out-Null
}
$paramTestPath = @{
Path = $workingDirectory
PathType = 'Container'
ErrorAction = 'Stop'
}
if (-not (Test-Path @paramTestPath)) {
$paramNewItem = @{
Path = $workingDirectory
ItemType = 'directory'
ErrorAction = 'Stop'
}
New-Item @paramNewItem | Out-Null
}
$paramConnectAzAccount = @{
subscriptionId = $subscriptionID
Tenant = $tenantDomain
ErrorAction = 'Stop'
}
if ($environment) {
$paramConnectAzAccount.Add('Environment', $environment)
}
Connect-AzAccount @paramConnectAzAccount
$paramSaveAzContext = @{
Path = $($azureProfile)
Force = $true
ErrorAction = 'Stop'
}
Save-AzContext @paramSaveAzContext | Out-Null
}
#endregion
#region - Check for feature registration
If ((Get-AzProviderFeature -ProviderNamespace "Microsoft.Compute" -FeatureName "Gen1ToTLMigrationPreview").RegistrationState -ne "Registered") {
$messageTxt = "[Common] WARN: Feature Gen1ToTLMigrationPreview is not registered. Registering now."
Write-Warning $messagetxt
Register-AzProviderFeature -ProviderNamespace "Microsoft.Compute" -FeatureName "Gen1ToTLMigrationPreview" -ErrorAction 'Stop'
do {
$registrationState = (Get-AzProviderFeature -ProviderNamespace "Microsoft.Compute" -FeatureName "Gen1ToTLMigrationPreview").RegistrationState
$messagetxt = "[Common] INFO: Registration state: $registrationState"
Write-Output $messagetxt
Start-Sleep -Seconds 10
} while ($registrationState -ne "Registered")
} else {
$messagetxt = "[Common] INFO: Feature Gen1ToTLMigrationPreview is already registered."
Write-Output $messagetxt
}
#endregion
}
catch [System.Exception] {
$messageTxt = '[Common] ERROR: Error Exception Occurred' + "`n$($psitem.Exception.Message)" + "`nError Caused By: $(($psitem.InvocationInfo.Line).Trim())"
Write-Output $messageTxt
Set-Variable -Name ERRORLEVEL -Value -1 -Scope Script -Force
}
}
#endregion
if ($ERRORLEVEL -eq 0) {
#region - Main script
if (-not $batchSize) {
[int]$batchSize = 5
}
$importVmArray = Import-Csv $csvLocation -ErrorAction 'Stop'
foreach ($element in $importVmArray) {
$element | Add-Member -MemberType NoteProperty -Name 'subscriptionId' -Value $subscriptionId
$element | Add-Member -MemberType NoteProperty -Name 'tenantDomain' -Value $tenantDomain
if ($useCloudshell) {
$element | Add-Member -MemberType NoteProperty -Name 'useCloudShell' -Value $true
}
if ($useSignedScript) {
$element | Add-Member -MemberType NoteProperty -Name 'useSignedScript' -Value $true
$element | Add-Member -MemberType NoteProperty -Name 'storageAccountName' -Value $outputStorageAccountName
}
}
$importVmArray | ForEach-Object -ThrottleLimit $batchSize -Parallel {
#region - Functions
function Get-ErrorLevel {
<#
.SYNOPSIS
Get ERRORLEVEL variable value
.DESCRIPTION
Get ERRORLEVEL variable value
.OUTPUTS
None.
.NOTES
#>
#region - Get ERRORLEVEL variable value
$script:ERRORLEVEL
#endregion
}
function Set-ErrorLevel {
<#
.SYNOPSIS
Set ERRORLEVEL variable value
.DESCRIPTION
Set ERRORLEVEL variable value
.PARAMETER level
ERRORLEVEL level [int] parameter.
.OUTPUTS
$ERRORLEVEL
.NOTES
#>
param
(
[Parameter(Mandatory = $false)]
[int]$level = 0
)
#region - Set Errorlevel
$script:ERRORLEVEL = $level
#endregion
}
function Write-InitLog {
param
(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$logDirectory,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$vmName
)
try {
$logStamp = (Get-Date -Format yy.MM.dd-HH.mm.ss)
$script:logFile = "$logDirectory\$($vmName)-Gen1-TL-Upgrade-" + $logStamp + '.log'
} catch [system.exception] {
$messageTxt = "[$vmName] ERROR: Error Exception Occurred `nWrite-InitLog() `n$($psitem.Exception.Message)"
Write-Output $messageTxt
Set-ErrorLevel -1
return $ERRORLEVEL
}
}
function Write-LogEntry {
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
$logMessage,
[Parameter(Mandatory = $false)]
[int]$logSeverity = 1,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[String]$logComponent
)
try {
$time = Get-Date -Format 'HH:mm:ss.ffffff'
$date = Get-Date -Format 'MM-dd-yyyy'
$message = "<![LOG[$logMessage" + "]LOG]!><time=`"$time`" date=`"$date`" component=`"$logComponent`" context=`"`" type=`"$logSeverity`" thread=`"`" file=`"`">"
$paramOutFile = @{
Append = $true
Encoding = 'UTF8'
FilePath = $logFile
NoClobber = $true
}
$message | Out-File @paramOutFile
} catch [system.exception] {
$messageTxt = "[$vmName] ERROR: Error Exception Occurred `nWrite-LogEntry() `n$($psitem.Exception.Message)"
Write-Output $messageTxt
Set-ErrorLevel -1
return $ERRORLEVEL
}
}
#endregion
$importVm = $_
$vmName = $importVm.vmName
$vmResourceGroupName = $importVm.vmResourceGroupName
$subscriptionId = $importVm.subscriptionID
$tenantDomain = $importVm.tenantDomain
$useCloudshell = $importVm.useCloudShell
$outputStorageAccountName = $importVm.storageAccountName
$useSignedScript = $importVm.useSignedScript
if ($importVm.enableSecureBoot) {
$enableSecureBoot = [system.convert]::ToBoolean($importVm.enableSecureBoot)
}
else { $enableSecureBoot = $true }
[bool]$gen2Vm = $false
[bool]$tlVm = $false
#region - Validate Pre-Requisites
try {
Set-Errorlevel 0 | Out-Null
Get-Errorlevel | Out-Null
if ($useCloudshell) {
$workingDirectory = [system.string]::concat((Get-Location).Path, "/Gen1-TrustedLaunch-Upgrade")
} else {
if ((Test-Path $env:UserProfile -ErrorAction SilentlyContinue) -eq $true) {
$workingDirectory = "$env:UserProfile\Gen1-TrustedLaunch-Upgrade"
} else {
$messageTxt = "[$vmName] INFO: User profile directory not found. Defaulting to script execution location."
Write-Output $messagetxt
$workingDirectory = [system.string]::concat((Get-Location).Path, "\Gen1-TrustedLaunch-Upgrade")
}
}
Write-InitLog -logDirectory $workingDirectory -vmName $vmName
$messageTxt = "[$vmName] INFO: Script Version: 3.0.1"
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "Setup-PreRequisites"
$inputParam = @{
'VM name' = $vmName
'Resource group name' = $vmResourceGroupName
'Subscription ID' = $subscriptionId
'Tenant Domain' = $tenantDomain
'Use Cloud Shell' = $useCloudshell
'Use Signed Script' = $useSignedScript
'Output Storage Account Name' = $outputStorageAccountName
'Enable Secure Boot' = $enableSecureBoot
}
$messageTxt = $inputParam.GetEnumerator() | ForEach-Object {"$($PSItem.Key) = $($PSItem.Value)"}
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "Setup-PreRequisites"
$messageTxt = "[$vmName] INFO: Processing VM $vmName under resource group $vmResourceGroupName with Secure boot $($importVm.enableSecureBoot)"
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "Setup-PreRequisites"
}
catch [system.exception] {
$messageTxt = "[$vmName]" + ' ERROR: Error Exception Occurred' + "`n$($psitem.Exception.Message)" + "`nError Caused By: $(($psitem.InvocationInfo.Line).Trim())"
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 1 -logComponent "Setup-PreRequisites"
Set-ErrorLevel -1
return $ERRORLEVEL
}
#endregion
#region - Connect Azure Subscription
If ($ERRORLEVEL -eq 0) {
try {
$messageTxt = "[$vmName] INFO: Connecting to Subscription $subscriptionId under $tenantDomain"
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "Connect-AzSubscription"
#region - Enable-AzAccount()
If ($useCloudshell -eq $true) {
Set-AzContext -SubscriptionId $subscriptionId -tenant $tenantDomain -ErrorAction 'Stop'
} else {
$azureProfile = "$workingDirectory\AzureProfile-$subscriptionId.json"
$paramTestPath = @{
Path = $($azureProfile)
ErrorAction = 'Stop'
}
if (Test-Path @paramTestPath) {
$paramImportAzContext = @{
Path = $($azureProfile)
ErrorAction = 'Stop'
}
Import-AzContext @paramImportAzContext | Out-Null
} else {
$paramTestPath = @{
Path = $workingDirectory
PathType = 'Container'
ErrorAction = 'Stop'
}
if (-not (Test-Path @paramTestPath)) {
$paramNewItem = @{
Path = $workingDirectory
ItemType = 'directory'
ErrorAction = 'Stop'
}
New-Item @paramNewItem | Out-Null
}
$paramConnectAzAccount = @{
subscriptionId = $subscriptionID
Tenant = $tenantDomain
ErrorAction = 'Stop'
}
if ($environment) {
$paramConnectAzAccount.Add('Environment', $environment)
}
Connect-AzAccount @paramConnectAzAccount
$paramSaveAzContext = @{
Path = $($azureProfile)
Force = $true
ErrorAction = 'Stop'
}
Save-AzContext @paramSaveAzContext | Out-Null
}
}
#endregion
}
catch [System.Exception] {
$messageTxt = "[$vmName]" + ' ERROR: Error Exception Occurred' + "`n$($psitem.Exception.Message)" + "`nError Caused By: $(($psitem.InvocationInfo.Line).Trim())"
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 1 -logComponent "Connect-AzSubscription"
Set-ErrorLevel -1
return $ERRORLEVEL
}
}
#endregion
#region - Current VM Configuration
If ($ERRORLEVEL -eq 0) {
try {
$messageTxt = "[$vmName] INFO: Mapping existing configuration for $vmName under $vmResourceGroupName"
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "Get-AzVM"
$paramGetAzVm = @{
ResourceGroupName = $vmResourceGroupName
Name = $vmName
ErrorAction = 'Stop'
}
$currentVm = Get-AzVM @paramGetAzVm
$CurrentVMConfig = @{
osdisk = $currentvm.StorageProfile.OsDisk
vmsize = $currentvm.HardwareProfile.VmSize
location = $currentVm.Location
securityType = $currentVm.SecurityProfile.SecurityType
}
$osDiskParam = @{
ResourceGroupName = $currentVm.ResourceGroupName
Name = $CurrentVMConfig.osdisk.Name
ErrorAction = 'Stop'
}
$currentOsDisk = Get-AzDisk @osDiskParam
$currentOsDiskConfig = @{
sku = $currentOsDisk.sku.Name
diskSize = $currentOsDisk.DiskSizeGB
HyperVGen = $currentOsDisk.HyperVGeneration
osType = $currentOsDisk.OsType
encryption = $currentOsDisk.Encryption
}
if ($currentOsDiskConfig.HyperVGen -eq "V2") {
if ($CurrentVMConfig.securityType) {
$messagetxt = "[$vmName] INFO: VM $vmName under resource group $vmResourceGroupName is already Trusted launch, no further action required."
Write-Output $messagetxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "Get-AzVM"
[bool]$tlVm = $true
[bool]$gen2Vm = $true
} else {
$messageTxt = "[$vmName] INFO: VM $vmName under resource group $vmResourceGroupName is running as Gen2. MBR2GPT conversion will be skipped."
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "Get-AzVM"
[bool]$gen2Vm = $true
}
}
$paramGetAzVm = @{
ResourceGroupName = $vmResourceGroupName
Name = $vmName
Status = $true
ErrorAction = 'Stop'
}
$currentOs = Get-AzVM @paramGetAzVm
$messageTxt = "[$vmName] INFO: OS Type of Source VM is $($currentOsDiskConfig.osType) and OS Name is $($currentOs.OsName)."
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "Get-AzVM"
}
catch [System.Exception] {
$messageTxt = "[$vmName]" + ' ERROR: Error Exception Occurred' + "`n$($psitem.Exception.Message)" + "`nError Caused By: $(($psitem.InvocationInfo.Line).Trim())"
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 1 -logComponent "Get-AzVM"
Set-ErrorLevel -1
return $ERRORLEVEL
}
}
#endregion
#region - Validate SKU Support
If ($ERRORLEVEL -eq 0) {
try {
If ($tlVm -eq $false) {
$messageTxt = "[$vmName] INFO: Validating VM SKU $($CurrentVMConfig.vmsize) for $vmname is supported for Gen2 & Trusted launch"
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "Validate-VMSize"
$gen2Support = $null
$tlvmSupport = $null
$skuDetail = Get-AzComputeResourceSku -Location $($CurrentVMConfig.location) -ErrorAction 'Stop' | `
Where-Object { $psitem.Name -eq $($CurrentVMConfig.vmsize) }
$gen2Support = $skuDetail | Select-Object -Property Capabilities -ExpandProperty Capabilities | Where-Object { $psitem.Name -eq "HyperVGenerations" }
$tlvmSupport = $skuDetail | Select-Object -Property Capabilities -ExpandProperty Capabilities | Where-Object { $psitem.Name -eq "TrustedLaunchDisabled" }
if (($gen2Support.value.Split(",")[-1] -eq "V2") -and !($tlvmSupport)) {
$messageTxt = "[$vmName] INFO: VM SKU $($CurrentVMConfig.vmsize) supported for Gen2 & Trusted launch."
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "Validate-VMSize"
} else {
$messageTxt = "[$vmName] ERROR: VM SKU $($CurrentVMConfig.vmsize) not supported for Gen2 or Trusted launch. Update VM Size to Gen2-Trusted launch Supported SKU. For more details, https://aka.ms/TrustedLaunch"
Write-Error $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 1 -logComponent "Validate-VMSize"
Set-ErrorLevel -1
return $ERRORLEVEL
}
}
} catch [system.exception] {
$messageTxt = "[$vmName] ERROR: Error Exception Occurred `n$($psitem.Exception.Message)"
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 1 -logComponent "Validate-VMSize"
Set-ErrorLevel -1
return $ERRORLEVEL
}
}
#endregion
#region - MBR to GPT Validation
if ($ERRORLEVEL -eq 0) {
try {
if ($gen2Vm -eq $false) {
if ($currentOsDiskConfig.osType -ne "Linux") {
if ($currentOs.OsName.Contains("2016")) {
$messagetxt = "[$vmName] ERROR: Windows Server 2016 does not supports native MBR to GPT upgrade. Terminating script."
Write-Error $messagetxt
Write-LogEntry -logMessage $messageTxt -logSeverity 1 -logComponent "MBR-GPT-Validation"
Set-ErrorLevel -1
} else {
$messageTxt = "[$vmName] INFO: Validating MBR to GPT conversion support for $vmname"
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "MBR-GPT-Validation"
$paramSetAzVMRunCommand = @{
ResourceGroupName = $vmResourceGroupName
VMName = $vmName
Location = $CurrentVMConfig.location
RunCommandName = 'managedRuncommand'
SourceScript = "MBR2GPT /validate /allowFullOS"
TimeoutInSecond = 120
Erroraction = 'Stop'
}
Set-AzVMRunCommand @paramSetAzVMRunCommand | Out-Null
$paramGetAzVmRunCommand = @{
ResourceGroupName = $vmResourceGroupName
VMName = $vmName
RunCommandName = 'managedRuncommand'
Expand = 'InstanceView'
ErrorAction = 'Stop'
}
$checkCmdOutput = (Get-AzVMRunCommand @paramGetAzVmRunCommand).InstanceView
if ($checkCmdOutput.Error.Length -gt 0 -or $checkCmdOutput.Output.Length -eq 0) {
$messagetxt = "[$vmName] ERROR: MBR to GPT support validation for Windows $vmname failed. Terminating script execution."
Write-Error $messagetxt
Write-LogEntry -logMessage $messageTxt -logSeverity 1 -logComponent "MBR-GPT-Validation"
$messageTxt = "[$vmName] INFO: Fetching setupact.log for $vmname"
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "MBR-GPT-Validation"
$paramSetAzVMRunCommand = @{
ResourceGroupName = $vmResourceGroupName
VMName = $vmName
Location = $CurrentVMConfig.location
RunCommandName = 'managedRuncommand'
SourceScript = "Get-Content C:\WINDOWS\setupact.log"
TimeoutInSecond = 120
Erroraction = 'Stop'
}
Set-AzVMRunCommand @paramSetAzVMRunCommand | Out-Null
$paramGetAzVmRunCommand = @{
ResourceGroupName = $vmResourceGroupName
VMName = $vmName
RunCommandName = 'managedRuncommand'
Expand = 'InstanceView'
ErrorAction = 'Stop'
}
$setupActOutput = (Get-AzVMRunCommand @paramGetAzVmRunCommand).InstanceView
$setupFileStamp = (Get-Date -Format yy.MM.dd-HH.mm.ss)
if ($useCloudshell) {
$setupActOutFile = [system.string]::concat($workingDirectory, "/", $vmName, "-",$setupFileStamp, "-mbr2gpt-validate-setupact.log")
} else {
$setupActOutFile = [system.string]::concat($workingDirectory, "\", $vmName, "-",$setupFileStamp, "-mbr2gpt-validate-setupact.log")
}
$messageTxt = "[$vmName] INFO: Writing setupact.log to $setupActOutFile"
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "MBR-GPT-Validation"
$setupActOutput.Output | Out-File -FilePath $setupActOutFile -Force -ErrorAction 'Stop'
$messageTxt = "[$vmName] INFO: Fetching setuperr.log for $vmname"
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "MBR-GPT-Validation"
$paramSetAzVMRunCommand = @{
ResourceGroupName = $vmResourceGroupName
VMName = $vmName
Location = $CurrentVMConfig.location
RunCommandName = 'managedRuncommand'
SourceScript = "Get-Content C:\Windows\setuperr.log"
TimeoutInSecond = 120
Erroraction = 'Stop'
}
Set-AzVMRunCommand @paramSetAzVMRunCommand | Out-Null
$paramGetAzVmRunCommand = @{
ResourceGroupName = $vmResourceGroupName
VMName = $vmName
RunCommandName = 'managedRuncommand'
Expand = 'InstanceView'
ErrorAction = 'Stop'
}
$setupErrOutput = (Get-AzVMRunCommand @paramGetAzVmRunCommand).InstanceView
if ($useCloudshell) {
$setupErrOutFile = [system.string]::concat($workingDirectory, "/", $vmName, "-",$setupFileStamp, "-mbr2gpt-validate-setuperr.log")
} else {
$setupErrOutFile = [system.string]::concat($workingDirectory, "\", $vmName, "-",$setupFileStamp, "-mbr2gpt-validate-setuperr.log")
}
$messageTxt = "[$vmName] INFO: Writing setuperr.log to $setupErrOutFile"
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "MBR-GPT-Validation"
$setupErrOutput.Output | Out-File -FilePath $setupErrOutFile -Force -ErrorAction 'Stop'
Set-ErrorLevel -1
} else {
$messagetxt = "[$vmName] INFO: MBR to GPT support validation for Windows $vmname completed successfully."
Write-Output $messagetxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "MBR-GPT-Validation"
}
}
} else {
$checkLinuxCmd = @'
bootDevice=$(echo "/dev/$(lsblk -no pkname $(df /boot | awk 'NR==2 {print $1}'))") && diskType=$(blkid $bootDevice -o value -s PTTYPE) && efiPartition=$(fdisk -l $bootDevice | grep EFI | awk '{print $1}') && biosPartition=$(fdisk -l $bootDevice | grep -i 'BIOS Boot' | awk '{print $1}') && grep -qs '/boot/efi' /etc/fstab && echo 'Boot device: '$bootDevice', disk type: '$diskType', EFI partition: '$efiPartition', BIOS partition: '$biosPartition', /boot/efi present in /etc/fstab'|| echo 'Boot device: '$bootDevice', disk type: '$diskType', EFI partition: '$efiPartition', BIOS partition: '$biosPartition', /boot/efi missing in /etc/fstab'
'@
if ($useCloudshell) {
$checkCmdFile = [system.string]::concat($workingDirectory, "/gen2LinuxCheckCmd.txt")
} else {
$checkCmdFile = [system.string]::concat($workingDirectory, "\gen2LinuxCheckCmd.txt")
}
if (-not (Test-Path $checkCmdFile -ErrorAction 'SilentlyContinue')) {
$messageTxt = "[$vmName] INFO: Writing validation script to gen2LinuxCheckCmd.txt"
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "MBR-GPT-Validation"
$stream = [System.IO.StreamWriter]::new($checkCmdFile)
$stream.WriteLine($checkLinuxCmd)
$stream.Close()
}
$messageTxt = "[$vmName] INFO: Executing validation script for $($vmName)"
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "MBR-GPT-Validation"
$paramSetAzVMRunCommand = @{
ResourceGroupName = $vmResourceGroupName
VMName = $vmName
Location = $CurrentVMConfig.location
RunCommandName = 'managedRuncommand'
SourceScript = (Get-Content $checkCmdFile)
TimeoutInSecond = 120
Erroraction = 'Stop'
}
Set-AzVMRunCommand @paramSetAzVMRunCommand | Out-Null
$paramGetAzVmRunCommand = @{
ResourceGroupName = $vmResourceGroupName
VMName = $vmName
RunCommandName = 'managedRuncommand'
Expand = 'InstanceView'
ErrorAction = 'Stop'
}
$checkCmdOutput = (Get-AzVMRunCommand @paramGetAzVmRunCommand).InstanceView.Output
$messageTxt = "[$vmName] INFO: $checkCmdOutput"
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "MBR-GPT-Validation"
$diskTypeCheck = $false
$efiPartitionCheck = $false
$bootEfiCheck = $false
if ($checkCmdOutput -match "disk type:\s*(\w+),") {
$diskType = $matches[1]
if ($diskType -eq "gpt") {
$diskTypeCheck = $true
}
}
if ($checkCmdOutput -match "EFI partition:\s*([^,]+),") {
$efiPartition = $matches[1]
if ($efiPartition -ne "") {
$efiPartitionCheck = $true
}
}
if ($checkCmdOutput -match "/boot/efi present") {
$bootEfiCheck = $true
}
if ($diskTypeCheck -and $efiPartitionCheck -and $bootEfiCheck) {
$messagetxt = "[$vmName] INFO: EFI partition for Linux VM $vmname validated successfully."
Write-Output $messagetxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "MBR-GPT-Validation"
} else {
$messagetxt = "[$vmName] ERROR: EFI partition for Linux VM $vmname not found. Terminating script execution."
Write-Error $messagetxt
Write-LogEntry -logMessage $messageTxt -logSeverity 1 -logComponent "MBR-GPT-Validation"
Set-ErrorLevel -1
}
}
}
}
catch [System.Exception] {
$messageTxt = "[$vmName]" + ' ERROR: Error Exception Occurred' + "`n$($psitem.Exception.Message)" + "`nError Caused By: $(($psitem.InvocationInfo.Line).Trim())"
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 1 -logComponent "MBR-GPT-Validation"
Set-ErrorLevel -1
return $ERRORLEVEL
}
}
#endregion
#region - MBR to GPT conversion
if ($ERRORLEVEL -eq 0) {
try {
if ($gen2Vm -eq $false) {
if ($currentOsDiskConfig.osType -eq "Linux") {
$messageTxt = "[$vmName] INFO: No MBR to GPT conversion required for Linux VM $($vmName)."
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 2 -logComponent "MBR-GPT-Execution"
}
else {
$messageTxt = "[$vmName] INFO: Executing MBR to GPT conversion on $vmname"
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "MBR-GPT-Execution"
$messageTxt = "[$vmName] INFO: Validating MBR to GPT conversion support for $vmname"
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "MBR-GPT-Execution"
$paramSetAzVMRunCommand = @{
ResourceGroupName = $vmResourceGroupName
VMName = $vmName
Location = $CurrentVMConfig.location
RunCommandName = 'managedRuncommand'
SourceScript = "MBR2GPT /convert /allowFullOS"
TimeoutInSecond = 120
Erroraction = 'Stop'
}
Set-AzVMRunCommand @paramSetAzVMRunCommand | Out-Null
$paramGetAzVmRunCommand = @{
ResourceGroupName = $vmResourceGroupName
VMName = $vmName
RunCommandName = 'managedRuncommand'
Expand = 'InstanceView'
ErrorAction = 'Stop'
}
$convertCmdOutput = (Get-AzVMRunCommand @paramGetAzVmRunCommand).InstanceView
if ($convertCmdOutput.Error.Length -gt 0 -or $convertCmdOutput.Output.Length -eq 0) {
$messagetxt = "[$vmName] ERROR: MBR to GPT conversion for Windows $vmname failed. Terminating script execution."
Write-Error $messagetxt
Write-LogEntry -logMessage $messageTxt -logSeverity 1 -logComponent "MBR-GPT-Execution"
$messageTxt = "[$vmName] INFO: Fetching setupact.log for $vmname"
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "MBR-GPT-Execution"
$paramSetAzVMRunCommand = @{
ResourceGroupName = $vmResourceGroupName
VMName = $vmName
Location = $CurrentVMConfig.location
RunCommandName = 'managedRuncommand'
SourceScript = "Get-Content C:\WINDOWS\setupact.log"
TimeoutInSecond = 120
Erroraction = 'Stop'
}
Set-AzVMRunCommand @paramSetAzVMRunCommand | Out-Null
$paramGetAzVmRunCommand = @{
ResourceGroupName = $vmResourceGroupName
VMName = $vmName
RunCommandName = 'managedRuncommand'
Expand = 'InstanceView'
ErrorAction = 'Stop'
}
$setupActOutput = (Get-AzVMRunCommand @paramGetAzVmRunCommand).InstanceView
$setupFileStamp = (Get-Date -Format yy.MM.dd-HH.mm.ss)
if ($useCloudshell) {
$setupActOutFile = [system.string]::concat($workingDirectory, "/", $vmName, "-",$setupFileStamp, "-mbr2gpt-convert-setupact.log")
} else {
$setupActOutFile = [system.string]::concat($workingDirectory, "\", $vmName, "-",$setupFileStamp, "-mbr2gpt-convert-setupact.log")
}
$messageTxt = "[$vmName] INFO: Writing setupact.log to $setupActOutFile"
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "MBR-GPT-Execution"
$setupActOutput.Output | Out-File -FilePath $setupActOutFile -Force -ErrorAction 'Stop'
$messageTxt = "[$vmName] INFO: Fetching setuperr.log for $vmname"
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "MBR-GPT-Execution"
$paramSetAzVMRunCommand = @{
ResourceGroupName = $vmResourceGroupName
VMName = $vmName
Location = $CurrentVMConfig.location
RunCommandName = 'managedRuncommand'
SourceScript = "Get-Content C:\WINDOWS\setuperr.log"
TimeoutInSecond = 120
Erroraction = 'Stop'
}
Set-AzVMRunCommand @paramSetAzVMRunCommand | Out-Null
$paramGetAzVmRunCommand = @{
ResourceGroupName = $vmResourceGroupName
VMName = $vmName
RunCommandName = 'managedRuncommand'
Expand = 'InstanceView'
ErrorAction = 'Stop'
}
$setupErrOutput = (Get-AzVMRunCommand @paramGetAzVmRunCommand).InstanceView
if ($useCloudshell) {
$setupErrOutFile = [system.string]::concat($workingDirectory, "/", $vmName, "-",$setupFileStamp, "-mbr2gpt-convert-setuperr.log")
} else {
$setupErrOutFile = [system.string]::concat($workingDirectory, "\", $vmName, "-",$setupFileStamp, "-mbr2gpt-convert-setuperr.log")
}
$messageTxt = "[$vmName] INFO: Writing setuperr.log to $setupErrOutFile"
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "MBR-GPT-Execution"
$setupErrOutput.Output | Out-File -FilePath $setupErrOutFile -Force -ErrorAction 'Stop'
Set-ErrorLevel -1
} else {
$messagetxt = "[$vmName] INFO: MBR to GPT conversion for Windows $vmname completed successfully."
Write-Output $messagetxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "MBR-GPT-Execution"
}
}
}
}
catch [System.Exception] {
$messageTxt = "[$vmName]" + ' ERROR: Error Exception Occurred' + "`n$($psitem.Exception.Message)" + "`nError Caused By: $(($psitem.InvocationInfo.Line).Trim())"
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 1 -logComponent "MBR-GPT-Execution"
Set-ErrorLevel -1
return $ERRORLEVEL
}
}
#endregion
#region - Upgrade VM to Trusted launch
if ($ERRORLEVEL -eq 0) {
try {
if ($tlvm -eq $false) {
$messageTxt = "[$vmName] INFO: De-allocating $vmname"
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "Upgrade-AzVM"
$paramStopAzVm = @{
ResourceGroupName = $vmResourceGroupName
Name = $vmName
Force = $true
Confirm = $false
ErrorAction = 'Stop'
}
Stop-AzVm @paramStopAzVm | Out-Null
$messageTxt = "[$vmName] INFO: Updating security type for $vmname to Trusted launch"
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "Upgrade-AzVM"
$paramUpdateAzVm = @{
ResourceGroupName = $vmResourceGroupName
VM = $currentVm
SecurityType = 'TrustedLaunch'
EnableVtpm = $true
ErrorAction = 'Stop'
}
if ($enableSecureBoot -eq $true) {
$paramUpdateAzVm.Add('EnableSecureBoot', $true)
}
else { $paramUpdateAzVm.Add('EnableSecureBoot', $false) }
Update-AzVM @paramUpdateAzVm | Out-Null
$messageTxt = "[$vmName] INFO: Starting $vmname"
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "Upgrade-AzVM"
$paramStartAzVm = @{
ResourceGroupName = $vmResourceGroupName
Name = $vmName
ErrorAction = 'Stop'
}
Start-AzVM @paramStartAzVm | Out-Null
}
}
catch [System.Exception] {
$messageTxt = "[$vmName]" + ' ERROR: Error Exception Occurred' + "`n$($psitem.Exception.Message)" + "`nError Caused By: $(($psitem.InvocationInfo.Line).Trim())"
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 1 -logComponent "Upgrade-AzVM"
Set-ErrorLevel -1
return $ERRORLEVEL
}
}
#endregion
#region - closure
if ($ERRORLEVEL -eq 0) {
$messageTxt = "[$vmName] INFO: Gen1 to Gen2-Trusted launch upgrade complete for $vmName."
Write-Output $messageTxt
Write-LogEntry -logMessage $messageTxt -logSeverity 3 -logComponent "Update-AzVM"
}
#endregion
}
#endregion
}
# SIG # Begin signature block
# MIIoQQYJKoZIhvcNAQcCoIIoMjCCKC4CAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCC3EYTq025wflVs
# 3pk2c1iF9hBvuagHYJ7/3UYL9ndTGaCCDYswggYJMIID8aADAgECAhMzAAAD9LjE
# XeFOcLZ+AAAAAAP0MA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjQwNzE3MjEwMjM1WhcNMjUwOTE1MjEwMjM1WjCBiDEL
# MAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v
# bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWlj
# cm9zb2Z0IDNyZCBQYXJ0eSBBcHBsaWNhdGlvbiBDb21wb25lbnQwggEiMA0GCSqG
# SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCv3P8bL08GKolFW7QNDVOF0aM4iqMxVvAW
# VM124/82xbjAraJkKxieMrQa1Fc95LVGgxmJIi5R6QKMz2MO9bnwC7kSkPqoZJil
# 26bRLY6jinjbwPpK3TzbW7z9bXfWw5bPFlt72NVIdXJ3xtHoYa+AOi++CF2Ry7+7
# o1AzvotJwG6lQSiCMKeMt8apqEF1f+QkDFEUv5tezw9748DeHW9orvo4IPzWa7vW
# QgljB08LKSnzTN9/Jot2coWpFv4YuEoJZmR2ofPJMnDUUruDORTXnxwhfvd/wUmI
# SoEysSqobkNV+qFuUmSShYrx8R1zHm7P6G/iRMIKYmSrIYBKUvndAgMBAAGjggFz
# MIIBbzAfBgNVHSUEGDAWBgorBgEEAYI3TBEBBggrBgEFBQcDAzAdBgNVHQ4EFgQU
# Dz4uMjS8YCSZaU0449GJYQ1ufyowRQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1p
# Y3Jvc29mdCBDb3Jwb3JhdGlvbjEWMBQGA1UEBRMNMjMxNTIyKzUwMjUxODAfBgNV
# HSMEGDAWgBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNo
# dHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0Ey
# MDExXzIwMTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZF
# aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQ
# Q0EyMDExXzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQEL
# BQADggIBABUCAiEn4g8i5T3VCP8160IY4ERdvZi5QZ2pSnBPW1dswVhLxkNTiCTV
# XKDjTQ4EwDBNSZZGJePz4+t86pKhlBON3S7wswf5fCovJLlIiKbw+E4TZeY6xAxd
# +5zV7Q2lsQhPHxiOY0PIGUE0KJfv/DQUulD8DrE0rru7yOO+DJI0muoK0BbHhRfd
# mAJhp2gbYRkarEIkhML9m3gR12mCBb69Vocm4IyOBivUPMjjvQMkERF7cR07k2uP
# 6dmpR8wtof9la0/K0wgiP5XuQUsAqgzhXrljH7dK7nqGrBDjJtrRdYfvVL+Rcz9i
# YZO280g2uNtac5em3HOEsactAL7XKqZ4o7s9sRyp/bTNLLRmhFMB729IL+Hi0YM7
# C8th3HZ5nP+77L46KUGip6QgRIJs+EO0YNW+AwgMxPfKpTx/Ggh8Z85kP7HLDZJk
# ZdPO/3cgVOTO4ax21vO2yMPCdfoGGr2ZLZw4SjEbGuOZJ22iGMV7tBvHk8nWAt3q
# +j/icAq99GA1nIPnw3jK3K9OwGqwA9eiWsO8/bHMm6s50UKIFupMKm6qObosaVBy
# R58rf8Cxumka7hPy1eSJSzQyA4UqYNTWuChsTfqgRLmLomS6yAu7t4r/bM4mGl+2
# Ki+avhQ4COm3jWWd0V6UGIP3T4zaKNs2GWFBIYsb/6XVvvi7pz/JMIIHejCCBWKg
# AwIBAgIKYQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3Qg
# Q2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYw
# NzA4MjEwOTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQ
# MA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u
# MSgwJgYDVQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIICIjAN
# BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGf
# Qhsqa+laUKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRg
# JGyvnkmc6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NE
# t13YxC4Ddato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnn
# Db6gE3e+lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+E
# GvKhL1nkkDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+t
# GSOEy/S6A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxh
# H2rhKEmdX4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AV
# s70b1FVL5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f
# 7Fufr/zdsGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3D
# KI8sj0A3T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9Jaw
# vEagbJjS4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1Ud
# DgQWBBRIbmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBi
# AEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRy
# LToCMZBDuRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3Js
# Lm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDEx
# XzIwMTFfMDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0
# cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDEx
# XzIwMTFfMDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/
# BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2Nz
# L3ByaW1hcnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAA
# bwBsAGkAYwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUA
# A4ICAQBn8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+
# vj/oCso7v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4H
# Limb5j0bpdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6
# aC6VoCo/KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiX
# mE0OPQvyCInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn
# +N4sOiBpmLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAq
# ZaPDXVJihsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5h
# YbXw3MYbBL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/
# RXceNcbSoqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXm
# r/r8i+sLgOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMyk
# XcGhiJtXcVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCGgwwghoIAgEB
# MIGVMH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH
# EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNV
# BAMTH01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAAP0uMRd4U5w
# tn4AAAAAA/QwDQYJYIZIAWUDBAIBBQCggbAwGQYJKoZIhvcNAQkDMQwGCisGAQQB
# gjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkE
# MSIEIDTp0KahYgUM0mifo1YcLwSvKz+XGc+LTZg6mqExVe1/MEQGCisGAQQBgjcC
# AQwxNjA0oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEcgBpodHRwczovL3d3dy5taWNy
# b3NvZnQuY29tIDANBgkqhkiG9w0BAQEFAASCAQCiaOAC1UpyqNoheCbEKcxqR81O
# EVAJezelJ92Tk/pEfg+B2WG0bDFiOP1oWUiGKnPzH9HnrSHIHG1RHRc3JrLa3lH6
# eXNI/pcGsQJbuQqMEU2T94GXRwgxQfK5vzCllSw315HkDVLeeVO5FAGYPiw3qmJL
# OsSQAO2frldGQdplWkTKO3Xxgd7FiFAcFv7BBzR2n5gOayNynMLDI0pNjP3weFkX
# ICT8CMQlv2DDm91KIVu1D01OVNfsyOwpv2LpMV5FewBbeSxOIYDg+J/cp74ySpgA
# W3wxUpyjmJQf5IwLukNk68mYUpeLHHvFZzTQ/YVy0K6u+ZYg8rEASQdFcLRhoYIX
# lDCCF5AGCisGAQQBgjcDAwExgheAMIIXfAYJKoZIhvcNAQcCoIIXbTCCF2kCAQMx
# DzANBglghkgBZQMEAgEFADCCAVIGCyqGSIb3DQEJEAEEoIIBQQSCAT0wggE5AgEB
# BgorBgEEAYRZCgMBMDEwDQYJYIZIAWUDBAIBBQAEIAyxaQouhqqohYXDPiJR7IbG
# 5OMkU/j7x79Cfp2KDyGmAgZnPydsZLsYEzIwMjQxMjA0MTYyNTA4LjAzOVowBIAC
# AfSggdGkgc4wgcsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw
# DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
# JTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMxJzAlBgNVBAsT
# Hm5TaGllbGQgVFNTIEVTTjo4RDAwLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9z
# b2Z0IFRpbWUtU3RhbXAgU2VydmljZaCCEeowggcgMIIFCKADAgECAhMzAAAB88UK
# Q64DzB0xAAEAAAHzMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w
# IFBDQSAyMDEwMB4XDTIzMTIwNjE4NDYwMloXDTI1MDMwNTE4NDYwMlowgcsxCzAJ
# BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k
# MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1pY3Jv
# c29mdCBBbWVyaWNhIE9wZXJhdGlvbnMxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVT
# Tjo4RDAwLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAg
# U2VydmljZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAP6fptrhK4H2
# JI7lYyFueCpgBv7Pch/M2lkhZL+yB9eGUtiYaexS2sZfc5VyD7ySsl2LG41Qw7tk
# A6oJmxdSM7PzNyfVpQPkPavY+HNUqMe2K9YaAaPjHnCpZ7VCi/e8zPxYewqx9p0i
# VaN8EydUpWiY7JtDv7aNzhp/OPZclBBKYT2NBGgGiAPCaplqR5icjHQSY665w+vr
# vhPr9hpM+IhiUZ/5dXa7qhAcCQwbnrFg9CKSK1COM1YcAN8GpsERqqmlqy3GlE1z
# iJ3ZLXFVDFxAZeOcCB55Vts9sCgQuFvD7PdV61HC4QUlHNPqFtYSC/P0sxg9JuKg
# cvzD5mJajfG7DdHt8myp7umqyePC+eI/ux8TW61+LuTQ1Bkym+I6z//bf0fp4Dog
# 5W0XzDrqKkTvURitxI2s4aVObm6qr6zI7W51k54ozTFjvbw1wYMWqeO4U9sQSbr5
# 61kp+1T2PEsJLOpc5U7N2oDw7ldrcTjWPezsyVMXhDsFitCZunGqFO9+4iVjAjYD
# N47c6K9x7MnAGPYVCBOJUdpy8xAOBIDsTm/K1qTT4wsGbQBxbgg96vwDiA4YP2hK
# mubIC7UnrAWQGt/ZKOf6J42roXHS1aPwimDe5C9y6DfuNJp0XqrWtQRqg8hqNkIZ
# WT6jnCfqu35zB0nf1ERTjdpYLCfQL5fHAgMBAAGjggFJMIIBRTAdBgNVHQ4EFgQU
# w2QV9qURUQyMDcCmhTH2oOsNCiQwHwYDVR0jBBgwFoAUn6cVXQBeYl2D9OXSZacb
# UzUZ6XIwXwYDVR0fBFgwVjBUoFKgUIZOaHR0cDovL3d3dy5taWNyb3NvZnQuY29t
# L3BraW9wcy9jcmwvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAo
# MSkuY3JsMGwGCCsGAQUFBwEBBGAwXjBcBggrBgEFBQcwAoZQaHR0cDovL3d3dy5t
# aWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBUaW1lLVN0YW1w
# JTIwUENBJTIwMjAxMCgxKS5jcnQwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAK
# BggrBgEFBQcDCDAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQELBQADggIBAN/E
# HI/80f7v29zeWI7hzudcz9QoVwCbnDrUXFHE/EJdFeWI2NnuwOo0/QPNRMFT21Lk
# OqSpFKIhXXmPurx7p6WDz9wPdu/Sxbgaj0AwviWEDkwGDfDMp2KF8nQT8cipwdfX
# WbC1ulOILayABSHv45mdv1PAkTulsQE8lBTHG4KJLn+vSzZBWKkGaL/wwRbZ4iLi
# Yn68cjkMJoAaihPgDXn/ug2P3PLNEAFNQgI02tLX0p+vIQ3l2HmSo4bhCBxr3Dov
# sIv5K65NmLRJnxmrrmIraFDwgwA5XF7AKkPiVkvo0OxU1LAE1c5SWzE4A7cbTA1P
# 5wG6D8cPjcHsTah1V+zofYRgJnFRLWuBF4Z3a6pDGBDbCsy5NvnKQ76p37ieFp//
# 1I3eB62ia1CfkjOF8KStpPUqdkXxMjfJ7Vnemd6vQKf+nXkfvA3AOQECJn7aLP01
# QR5gt8wab28SsNUENEyMawT8eqpjtBNJO0O9Tv7NnBE8aOJhhQVdP5WCR90eIWkr
# DjZeybQx8vlo5rfUXIIzXv+k9MgpNGIqwMXfvRLAjBkCNXOIP/1CEQUG72miMVQs
# 5m/O4vmJIQkhyqilUDB1s12uhmLYc3yd8OPMlrwIxORB5J9CxCkqvzc6EGYTcwXa
# zPyCp7eWhzTkNbwk29nfbwmmzcskIAu3StA8lic7MIIHcTCCBVmgAwIBAgITMwAA
# ABXF52ueAptJmQAAAAAAFTANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3Qg
# Q2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMjEwOTMwMTgyMjI1WhcNMzAw
# OTMwMTgzMjI1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQ
# MA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u
# MSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCAiIwDQYJ
# KoZIhvcNAQEBBQADggIPADCCAgoCggIBAOThpkzntHIhC3miy9ckeb0O1YLT/e6c
# BwfSqWxOdcjKNVf2AX9sSuDivbk+F2Az/1xPx2b3lVNxWuJ+Slr+uDZnhUYjDLWN
# E893MsAQGOhgfWpSg0S3po5GawcU88V29YZQ3MFEyHFcUTE3oAo4bo3t1w/YJlN8
# OWECesSq/XJprx2rrPY2vjUmZNqYO7oaezOtgFt+jBAcnVL+tuhiJdxqD89d9P6O
# U8/W7IVWTe/dvI2k45GPsjksUZzpcGkNyjYtcI4xyDUoveO0hyTD4MmPfrVUj9z6
# BVWYbWg7mka97aSueik3rMvrg0XnRm7KMtXAhjBcTyziYrLNueKNiOSWrAFKu75x
# qRdbZ2De+JKRHh09/SDPc31BmkZ1zcRfNN0Sidb9pSB9fvzZnkXftnIv231fgLrb
# qn427DZM9ituqBJR6L8FA6PRc6ZNN3SUHDSCD/AQ8rdHGO2n6Jl8P0zbr17C89XY
# cz1DTsEzOUyOArxCaC4Q6oRRRuLRvWoYWmEBc8pnol7XKHYC4jMYctenIPDC+hIK
# 12NvDMk2ZItboKaDIV1fMHSRlJTYuVD5C4lh8zYGNRiER9vcG9H9stQcxWv2XFJR
# XRLbJbqvUAV6bMURHXLvjflSxIUXk8A8FdsaN8cIFRg/eKtFtvUeh17aj54WcmnG
# rnu3tz5q4i6tAgMBAAGjggHdMIIB2TASBgkrBgEEAYI3FQEEBQIDAQABMCMGCSsG
# AQQBgjcVAgQWBBQqp1L+ZMSavoKRPEY1Kc8Q/y8E7jAdBgNVHQ4EFgQUn6cVXQBe
# Yl2D9OXSZacbUzUZ6XIwXAYDVR0gBFUwUzBRBgwrBgEEAYI3TIN9AQEwQTA/Bggr
# BgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9Eb2NzL1Jl
# cG9zaXRvcnkuaHRtMBMGA1UdJQQMMAoGCCsGAQUFBwMIMBkGCSsGAQQBgjcUAgQM
# HgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1Ud
# IwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0
# dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0Nl
# ckF1dF8yMDEwLTA2LTIzLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKG
# Pmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0
# XzIwMTAtMDYtMjMuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCdVX38Kq3hLB9nATEk
# W+Geckv8qW/qXBS2Pk5HZHixBpOXPTEztTnXwnE2P9pkbHzQdTltuw8x5MKP+2zR
# oZQYIu7pZmc6U03dmLq2HnjYNi6cqYJWAAOwBb6J6Gngugnue99qb74py27YP0h1
# AdkY3m2CDPVtI1TkeFN1JFe53Z/zjj3G82jfZfakVqr3lbYoVSfQJL1AoL8ZthIS
# EV09J+BAljis9/kpicO8F7BUhUKz/AyeixmJ5/ALaoHCgRlCGVJ1ijbCHcNhcy4s
# a3tuPywJeBTpkbKpW99Jo3QMvOyRgNI95ko+ZjtPu4b6MhrZlvSP9pEB9s7GdP32
# THJvEKt1MMU0sHrYUP4KWN1APMdUbZ1jdEgssU5HLcEUBHG/ZPkkvnNtyo4JvbMB
# V0lUZNlz138eW0QBjloZkWsNn6Qo3GcZKCS6OEuabvshVGtqRRFHqfG3rsjoiV5P
# ndLQTHa1V1QJsWkBRH58oWFsc/4Ku+xBZj1p/cvBQUl+fpO+y/g75LcVv7TOPqUx
# UYS8vwLBgqJ7Fx0ViY1w/ue10CgaiQuPNtq6TPmb/wrpNPgkNWcr4A245oyZ1uEi
# 6vAnQj0llOZ0dFtq0Z4+7X6gMTN9vMvpe784cETRkPHIqzqKOghif9lwY1NNje6C
# baUFEMFxBmoQtB1VM1izoXBm8qGCA00wggI1AgEBMIH5oYHRpIHOMIHLMQswCQYD
# VQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEe
# MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3Nv
# ZnQgQW1lcmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046
# OEQwMC0wNUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNl
# cnZpY2WiIwoBATAHBgUrDgMCGgMVAG76BizYtGFrmkU7v2DcuR/ApGcooIGDMIGA
# pH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT
# B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UE
# AxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwDQYJKoZIhvcNAQELBQAC
# BQDq+sh2MCIYDzIwMjQxMjA0MTIyNDIyWhgPMjAyNDEyMDUxMjI0MjJaMHQwOgYK
# KwYBBAGEWQoEATEsMCowCgIFAOr6yHYCAQAwBwIBAAICBv8wBwIBAAICEw8wCgIF
# AOr8GfYCAQAwNgYKKwYBBAGEWQoEAjEoMCYwDAYKKwYBBAGEWQoDAqAKMAgCAQAC
# AwehIKEKMAgCAQACAwGGoDANBgkqhkiG9w0BAQsFAAOCAQEADOtfE7oFKg4+gDu/
# tPyYywgkm35HzTXJdkBevryLr76FOu+ciZsMQ9Tghi1IMMvoeE+IBX5EaLK8qO0E
# kY7pTEAn7guhUyA3/6ViQlgXmRLeBbhVuJNg7h7wvyyv9L5AYXkOFrE3rGUo1fGf
# nf7seE5Y7v6hELF0V3zY6mjVEDCuejhb0QMy3N5Jg5OpsdIA80Hz0WdJgdoyGZtl
# AuoERN573I53gxwjSPKbLiWhMYdWsG5qVKqN6dWQT/xbIFDH3MJPcIDbRiglXgCd
# w6GaWHYjytA8hEsJENDtDeukGWdKmoybhfgqxSWuwYNYIdY9hDPhD4yfnBmAer4Z
# qaNarjGCBA0wggQJAgEBMIGTMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
# cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw
# AhMzAAAB88UKQ64DzB0xAAEAAAHzMA0GCWCGSAFlAwQCAQUAoIIBSjAaBgkqhkiG
# 9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJKoZIhvcNAQkEMSIEIGQXtAz59GoWWNBl
# 25jbA7HvL0/gweRHom8ieFN6zm8mMIH6BgsqhkiG9w0BCRACLzGB6jCB5zCB5DCB
# vQQgGLzZNIu24bhWSnzAGYmT9P5ECHzjWwb9oM7DGDo7YugwgZgwgYCkfjB8MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNy
# b3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAfPFCkOuA8wdMQABAAAB8zAi
# BCA6G+nwQLAG2J6I4eXZ6QswwRN2ChwnxgwUgA7QLoP8/jANBgkqhkiG9w0BAQsF
# AASCAgAGOtLybg7num6e3+s7ntU6cADNUhnP+jPRs1akxZtawgZAEla3Ckap4hjI
# kU1tbIsWZD1Qm2rQ2uuqhIMu/YKs3C8URxee9rjTlct11qlgPE0WLZXxeMSho/Eg
# cokpuF3z3BKIZQ+nHlVcjeaIzBvMcjsoWT4Z7xEwqg445mUnwYAFbAnu04Ho1mhj
# 7hDzuM5Sv/WrEUjBw5v4gfWpu42nZX3grEgyzatz0Vjjt58B2SOkj3IBbL3LZY9P
# s3oF8/odLULfN4X0Gt+hewKsr0S7DyifRG0t48w3sa1O6KNoS1iDpTK/G+xk/aR0
# xdisA6Q8J6hZS2aA5vNC7VObQVODlSvGqcMb/tZZZU6bdZ7WKpOMNhZl1kFEgdhX
# RXaXNa63WsUCWqMMcINeGnRfA14AqEbbJsYD/gtMzSmsx3UFFyLf71aGTxsfRDC5
# zpGZfdFImHBWFd+2PI6xwJ1evPy9Ut7EgmtBBOa4FOitbht9kpCkQBymlwx3zjgy
# PLXNwwjRd8aBUL1m+BarcV3eOJ8FiJ8lNfSBq8VY24ka1zzwMl0QbOe0eUzUUsVX
# RA54wqUAEco9KggRERnIWb9g+vytaRnEvD4BBHYihh4noZwgTkTRjGPJEbu5RULI
# lsDbUvWZG1qVyj6ioEa7BVSsq0/P2Hba2Ih8NgnoMB0Q60iuAA==
# SIG # End signature block