Migrate.Autorest/custom/Helper/AzLocalCommonHelper.ps1
|
function CheckResourceGraphModuleDependency { [Microsoft.Azure.PowerShell.Cmdlets.Migrate.DoNotExportAttribute()] param() process { $module = Get-Module -ListAvailable | Where-Object { $_.Name -eq "Az.ResourceGraph" } if ($null -eq $module) { $message = "Az.ResourceGraph Module must be installed to run this command. Please run 'Install-Module -Name Az.ResourceGraph' to install and continue." throw $message } } } function CheckResourcesModuleDependency { [Microsoft.Azure.PowerShell.Cmdlets.Migrate.DoNotExportAttribute()] param() process { $module = Get-Module -ListAvailable | Where-Object { $_.Name -eq "Az.Resources" } if ($null -eq $module) { $message = "Az.Resources Module must be installed to run this command. Please run 'Install-Module -Name Az.Resources' to install and continue." throw $message } } } function CheckStorageModuleDependency { [Microsoft.Azure.PowerShell.Cmdlets.Migrate.DoNotExportAttribute()] param() process { $module = Get-Module -ListAvailable | Where-Object { $_.Name -eq "Az.Storage" } if ($null -eq $module) { $message = "Az.Storage Module must be installed to run this command. Please run 'Install-Module -Name Az.Storage' to install and continue." throw $message } } } function GetARGQueryForArcResourceBridge { [Microsoft.Azure.PowerShell.Cmdlets.Migrate.DoNotExportAttribute()] param( [Parameter(Mandatory)] [System.String] # Specifies HCI Cluster Id. ${HCIClusterID} ) process { $query = @" resources | where type == 'microsoft.extendedlocation/customlocations' | mv-expand ClusterId = properties['clusterExtensionIds'] | extend ClusterId = toupper(tostring(ClusterId)) | extend CustomLocation = toupper(tostring(id)) | extend resourceBridgeID = toupper(tostring(properties['hostResourceId'])) | extend customLocationRegion = location | join ( kubernetesconfigurationresources | where type == 'microsoft.kubernetesconfiguration/extensions' | where properties['ConfigurationSettings']['HCIClusterID'] =~ '$HCIClusterID' | project ClusterId = id | extend ClusterId = toupper(tostring(ClusterId)) ) on ClusterId | join ( resources | where type == 'microsoft.resourceconnector/appliances' | where properties['provisioningState'] == 'Succeeded' | extend statusOfTheBridge = properties['status'] | extend resourceBridgeID = toupper(tostring(id)) ) on resourceBridgeID "@ return $query } } function IsReservedOrTrademarked { [Microsoft.Azure.PowerShell.Cmdlets.Migrate.DoNotExportAttribute()] param( [Parameter(Mandatory)] [System.String] # Specifies VM name. ${Value} ) $uppercased = $Value.ToUpper(); # cannot be exactly one of these, but could be slightly different (e.g. hololens2) $reservedWords = @( "ACCESS", "APP_CODE", "APP_THEMES", "APP_DATA", "APP_GLOBALRESOURCES", "APP_LOCALRESOURCES", "APP_WEBREFERENCES", "APP_BROWSERS", "AZURE", "BING", "BIZSPARK", "BIZTALK", "CORTANA", "DIRECTX", "DOTNET", "DYNAMICS", "EXCEL", "EXCHANGE", "FOREFRONT", "GROOVE", "HOLOLENS", "HYPERV", "KINECT", "LYNC", "MSDN", "O365", "OFFICE", "OFFICE365", "ONEDRIVE", "ONENOTE", "OUTLOOK", "POWERPOINT", "SHAREPOINT", "SKYPE", "VISIO", "VISUALSTUDIO" ) # The following words can't be used as either a whole word or a substring in the name: $microsoft = "MICROSOFT"; $windows = "WINDOWS"; # The following words can't be used at the start of a resource name, but can be used later in the name: $startLogin = "LOGIN"; $startXbox = "XBOX"; if ($uppercased.startsWith($startLogin) -or $uppercased.startsWith($startXbox)) { return $true; } if ($uppercased.contains($microsoft) -or $uppercased.contains($windows)) { return $true; } foreach ($reservedName in $reservedWords) { if ($uppercased -eq $reservedName) { return $true; } } return $false; } function GenerateHashForArtifact { [Microsoft.Azure.PowerShell.Cmdlets.Migrate.DoNotExportAttribute()] param( [Parameter(Mandatory)] [System.String] # Specifies resource group name. ${Artifact} ) $hashCode = 0 $artifactLength = $Artifact.Length $tempItemLength = 0 if ($artifactLength -gt 0) { while ($tempItemLength -lt $artifactLength) { $hashCode = ((($hashCode -shl 5) - $hashCode) + $Artifact[$tempItemLength++] -bor 0) # Treat as Double, then convert to Bytes, then convert back to Int32 to match JavaScript behavior $hashCode = [System.BitConverter]::ToInt32([System.BitConverter]::GetBytes($hashCode), 0) } } if ($hashCode -lt 0) { return -1 * $hashCode } else { return $hashCode } } function InvokeAzMigrateGetCommandWithRetries { [Microsoft.Azure.PowerShell.Cmdlets.Migrate.DoNotExportAttribute()] param( [Parameter(Mandatory)] [System.String] # Specifies the name of Az.Migrate command. ${CommandName}, [Parameter(Mandatory)] [System.Collections.Hashtable] # Specifies the parameters for Az.Migrate command. ${Parameters}, [Parameter()] [System.Int32] # Specifies the maximum number of retries. ${MaxRetryCount} = 3, [Parameter()] [System.Int32] # Specifies the delay between retries in seconds. ${RetryDelayInSeconds} = 30, [Parameter()] [System.String] # Specifies the error message to throw if command fails. ${ErrorMessage} = "Internal Az.Migrate commands failed to execute." ) process { # Filter out ErrorAction and ErrorVariable from the parameters $params = @{} foreach ($key in $Parameters.Keys) { if ($key -ne "ErrorAction" -and $key -ne "ErrorVariable") { $params[$key] = $Parameters[$key] } } # Extract user-specified ErrorAction and ErrorVariable or defaults # but do not include them in $params if ($Parameters.ContainsKey("ErrorVariable")) { $errorVariable = $Parameters["ErrorVariable"] } else { $errorVariable = "notPresent" } if ($Parameters.ContainsKey("ErrorAction")) { $errorAction = $Parameters["ErrorAction"] } else { $errorAction = "Continue" } for ($i = 0; $i -le $MaxRetryCount; $i++) { try { $result = & $CommandName @params -ErrorVariable $errorVariable -ErrorAction $errorAction if ($null -eq $result) { throw $ErrorMessage } break } catch { if ($i -lt $MaxRetryCount) { Start-Sleep -Seconds $RetryDelayInSeconds } else { throw "Get command failed after $MaxRetryCount retries. Error: $($_.Exception)" } } } return $result } } function Test-ReplicationPrequisites { [Microsoft.Azure.PowerShell.Cmdlets.Migrate.DoNotExportAttribute()] param ( [Parameter(Mandatory)] [string] ${ResourceGroupName}, [Parameter(Mandatory)] [string] ${VaultName}, [Parameter(Mandatory)] [string] ${ProtectedItemName}, [Parameter(Mandatory)] [System.String] ${MigrationType} ) # Check if the VM is already protected $protectedItem = Az.Migrate.Internal\Get-AzMigrateProtectedItem ` -ResourceGroupName $ResourceGroupName ` -VaultName $VaultName ` -Name $ProtectedItemName ` -ErrorAction SilentlyContinue if ($null -ne $protectedItem) { throw $VmReplicationValidationMessages.AlreadyInReplication } # Check the VM power status if ($Machine.PowerStatus -eq $PowerStatus.OffVMware -or $Machine.PowerStatus -eq $PowerStatus.OffHyperV) { throw $VmReplicationValidationMessages.VmPoweredOff } # Hyper-V scenario checks if ($MigrationType -eq $AzLocalInstanceTypes.HyperVToAzLocal) { # Hyper-V VMs with 'otherguestfamily' OS type and missing OS name could also mean Hyper-V Integration Services are not running if ([string]::IsNullOrEmpty($Machine.OperatingSystemDetailOSType) -or ($Machine.OperatingSystemDetailOSType -eq $OsTypes.OtherGuestFamily -and [string]::IsNullOrEmpty($Machine.GuestOSDetailOsname))) { throw $VmReplicationValidationMessages.HyperVIntegrationServicesNotRunning } # Hyper-V VMs on cluster should be highly available if (![string]::IsNullOrEmpty($Machine.ClusterId)) { if ($Machine.HighAvailability -eq $HighAvailability.NO) { throw $VmReplicationValidationMessages.VmNotHighlyAvailable } elseif ($Machine.HighAvailability -ne $HighAvailability.YES) { # Unknown or unexpected value throw $VmReplicationValidationMessages.VmUnknownHighlyAvailable } } } # VMware scenario checks if ($MigrationType -eq $AzLocalInstanceTypes.VMwareToAzLocal) { # VMware tools should be running to support static ip migration if ($Machine.VMwareToolsStatus -eq $VMwareToolsStatus.NotRunning) { Write-Warning $VmReplicationValidationMessages.VmWareToolsNotRunning } if ($Machine.VMwareToolsStatus -eq $VMwareToolsStatus.NotInstalled) { Write-Warning $VmReplicationValidationMessages.VmWareToolsNotInstalled } } # Only OS type of windowsguest and linuxguest are supported for Hyper-V and VMware scenarios if ($Machine.OperatingSystemDetailOSType -ne $OsTypes.WindowsGuest -and $Machine.OperatingSystemDetailOSType -ne $OsTypes.LinuxGuest) { Write-Warning $VmReplicationValidationMessages.OsTypeNotSupported } } function Test-AzureResourceIdFormat { [Microsoft.Azure.PowerShell.Cmdlets.Migrate.DoNotExportAttribute()] param( [Parameter(Mandatory = $true)] [string] $Data, [Parameter(Mandatory = $true)] [string] $Format ) try { if ([string]::IsNullOrWhiteSpace($Data)) { return $false } # Find where format string starts (after first /) $firstTokenEnd = $Format.IndexOf("/", 1) if ($firstTokenEnd -eq -1) { return $false } $formatPrefix = $Format.Substring(0, $firstTokenEnd) $matchIndex = $Data.ToLower().IndexOf($formatPrefix.ToLower()) if ($matchIndex -eq -1) { return $false } $processData = $Data.Substring($matchIndex) $processFormat = $Format $tokens = @() $counter = 0 while ($true) { $markerPattern = "{$counter}" $markerStartIndex = $processFormat.IndexOf($markerPattern) if ($markerStartIndex -eq -1) { break } $markerEndIndex = $processData.IndexOf("/", $markerStartIndex) if ($markerEndIndex -eq -1) { $token = $processData.Substring($markerStartIndex) if ([string]::IsNullOrWhiteSpace($token)) { return $false } $tokens += $token } else { $token = $processData.Substring($markerStartIndex, $markerEndIndex - $markerStartIndex) if ([string]::IsNullOrWhiteSpace($token)) { return $false } $tokens += $token $processData = $processData.Substring($markerEndIndex) $processFormat = $processFormat.Substring($markerStartIndex + $markerPattern.Length) } $counter++ } # Verify format matches with the extracted tokens $formatWithTokens = $Format for ($i = 0; $i -lt $tokens.Count; $i++) { $formatWithTokens = $formatWithTokens -replace "\{$i\}", $tokens[$i] } return $Data.ToLower() -like $formatWithTokens.ToLower() } catch { return $false } } function New-AzMigrateSolutionNotFoundException { [Microsoft.Azure.PowerShell.Cmdlets.Migrate.DoNotExportAttribute()] param( [Parameter(Mandatory)] [string] ${Name}, [Parameter(Mandatory)] [string] ${ResourceGroupName}, [Parameter(Mandatory)] [string] ${ProjectName} ) return "No Azure Migrate Solution '$Name' found in resource group '$ResourceGroupName' and project '$ProjectName'. Please verify your appliance setup." } function New-ReplicationVaultNotFoundInAMHSolutionException { [Microsoft.Azure.PowerShell.Cmdlets.Migrate.DoNotExportAttribute()] param( [Parameter(Mandatory)] [string] ${VaultId} ) return "Invalid replication vault ID '$VaultId' found in Azure Migrate Solution 'Servers-Migration-ServerMigration_DataReplication'. Please reach out to Microsoft support for assistance if this issue persists." } function New-InvalidResourceIdProvidedException { [Microsoft.Azure.PowerShell.Cmdlets.Migrate.DoNotExportAttribute()] param( [Parameter(Mandatory)] [string] ${ResourceId}, [Parameter(Mandatory)] [ValidateSet("MigrateProject", "Job", "ProtectedItem", "ResourceGroup", "DiscoveredMachine", "StorageContainer", "LogicalNetwork")] [string] ${ResourceType}, [Parameter(Mandatory)] [string] ${Format} ) return "Invalid '$ResourceType' Id '$ResourceId' provided. Please provide a valid '$ResourceType' ARM id with format '$Format'." } function New-AzMigrateSiteNotFoundException { [Microsoft.Azure.PowerShell.Cmdlets.Migrate.DoNotExportAttribute()] param( [Parameter(Mandatory)] [string] ${Name}, [Parameter(Mandatory)] [string] ${ResourceGroupName}, [Parameter(Mandatory)] [string] ${SiteType} ) return "Machine site '$Name' with Type '$SiteType' not found. Please verify in your Azure Migrate project resource group '$ResourceGroupName' and re-run this command if exists." } function New-AzMigrateProtectedItemNotFoundException { [Microsoft.Azure.PowerShell.Cmdlets.Migrate.DoNotExportAttribute()] param( [Parameter(Mandatory)] [string] ${Id} ) return "Replication item is not found with Id '$Id'. Re-run this command if exists." } function New-AzMigrateDiscoveredMachineNotFoundException { [Microsoft.Azure.PowerShell.Cmdlets.Migrate.DoNotExportAttribute()] param( [Parameter(Mandatory)] [string] ${Name}, [Parameter(Mandatory)] [string] ${ResourceGroupName}, [Parameter(Mandatory)] [string] ${SiteName} ) return "Machine '$Name' not found in resource group '$ResourceGroupName' and site '$SiteName'." } function New-OffAzureResourceNotFoundException { [Microsoft.Azure.PowerShell.Cmdlets.Migrate.DoNotExportAttribute()] param( [Parameter(Mandatory)] [ValidateSet("HyperV", "VMware")] [string] ${Scenario}, [Parameter(Mandatory)] [string] [ValidateSet("Host", "Cluster", "VCenter")] ${Type}, [Parameter(Mandatory)] [string] ${Name}, [Parameter(Mandatory)] [string] ${ResourceGroupName}, [Parameter(Mandatory)] [string] ${SiteName} ) return "'$Scenario' '$Name' not found in resource group '$ResourceGroupName' and site '$SiteName'." } # SIG # Begin signature block # MIIncQYJKoZIhvcNAQcCoIInYjCCJ14CAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCe8ruON0EKIfHr # eYHUJR3hU+9GrIjgKZ1+quPDu/C8ZKCCDMkwggYEMIID7KADAgECAhMzAAACHPrN # xZvoL37EAAAAAAIcMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNVBAYTAlVTMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBD # b2RlIFNpZ25pbmcgUENBIDIwMjQwHhcNMjYwNDE2MTg1OTQxWhcNMjcwNDE1MTg1 # OTQxWjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYD # VQQDExVNaWNyb3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IB # DwAwggEKAoIBAQDVsZfgOKmM31HPfoWOoNEiw0SlCiIxUMC0I9NMWbucKOw/e9lP # oAoehQVu6SG65V4EPzrYsnBnFPNoi4/HoOdjhz1qkrEt4I6tEcxXU6oOeY9zGveC # /3iBeuhLYxM3M/PkcUoebF+Nednm8OkdSPoDu8imViHPQq/8CQUu0WRR4rE+dMRf # rpVqfmNi2qWCX94T4MsepijGVkwE//tJg0ryAiYdHT34LSnlG/RSBZmQRGWZ5g8j # qnKjRParSqMft1gvjuUTVgtWNZfgcLFSK5Wa0myrq8OPcgTGGsRgun+tnSS+IxDT # xVsAPH1OzvPjwomguByhUe/OcvUN0D5Wmp7xAgMBAAGjggGqMIIBpjAOBgNVHQ8B # Af8EBAMCB4AwHwYDVR0lBBgwFgYKKwYBBAGCN0wIAQYIKwYBBQUHAwMwHQYDVR0O # BBYEFNoH7a2YDjOSwpkp6DHcmUS7J+0yMFQGA1UdEQRNMEukSTBHMS0wKwYDVQQL # EyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxFjAUBgNVBAUT # DTIzMDAxMis1MDc1NjkwHwYDVR0jBBgwFoAUf1k/VCHarU/vBeXmo9ctBpQSCDEw # YAYDVR0fBFkwVzBVoFOgUYZPaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9w # cy9jcmwvTWljcm9zb2Z0JTIwQ29kZSUyMFNpZ25pbmclMjBQQ0ElMjAyMDI0LmNy # bDBtBggrBgEFBQcBAQRhMF8wXQYIKwYBBQUHMAKGUWh0dHA6Ly93d3cubWljcm9z # b2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwQ29kZSUyMFNpZ25pbmcl # MjBQQ0ElMjAyMDI0LmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IC # AQAUnEqhaRXe0T3hIJjvdQErEkrA/7bByjn6t5IArODkkRjzkYwtKMc2yYj2quaN # rLutWw2YZcngKPy1b71YyDJQTy4NDRwaSh9Tw5thrk3NmcPrAHia5vtcBJ1CgtKK # 7mQbIcQ22d/N3813ayCDDFewu1+jsZmX+r/aTEqaOM4TVxVtRSkuCy8nAXKuChOK # Li/zA4XuH8iEYqIsj2YoNaeSxVmeGiERXpKdo3dDmYi0kO5w2D8VS4c3+9h6gElY # BaAAg/dYErBg27qT3vv0zRDJhJufvCNylA8S7/+8H5E/PV5cng6na9VV/w9OV3qu # uND6zdGa2EX38Glp50F9AIQk3p2xXmcvorDeM4XJ7UlWYBi6g80J1SSOQnInCYFE # msfUNn3+1AaTJKSJL83quKArTac2pKhu0Yzzzrzo6HrsRiQKzpnRBb1/dMa6P3hz # 75XbMRBctNsFhZC07WCmjExdLg2eHW5uV0TY8D5+6wozJf7vF3+WHkYPO85Z+BC6 # U4FkNbYNycZ9cE4j1tXRdyDCfml6c0HWPHjNVDObrv9lKt3qUqFpX38VCqVCyNOO # 1UcXfQiVjJw32U2WUKZjt/neJKHEBsm9kFsLuWzkQ53+qcaSaytmsCnk2gOglrlD # 5d3kKyvvAw+rzm0lT8K38P6PLxfZQHhu4W8dV7Av8N2ZmDCCBr0wggSloAMCAQIC # EzMAAAA5O7Y3Gb8GHWcAAAAAADkwDQYJKoZIhvcNAQEMBQAwgYgxCzAJBgNVBAYT # AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBS # b290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDExMB4XDTI0MDgwODIwNTQxOFoX # DTM2MDMyMjIyMTMwNFowVzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29m # dCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQ # Q0EgMjAyNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANgBnB7jOMeq # lRYHNa265v4IY9fH8TKhemHfPINe1gpLaV3dhg324WwH06LcHbpnsBukCDNitryo # 0dtS/EW6I/yEL/bLSY8hKpbfQuWusBPr9qazYcDxCW/qnjb5JsI1s8bNOg3bVATv # QVL4tcf03aTycsz8QeCdM0l/yHRObJ9QqazM1r6VPEOJ7LL+uEEb73w6QCuhs89a # 1uv1zerOYMnsneRRwCbpyW11IcggU0cRKDDq1pjVJzIbIF6+oiXXbReOsgeI8zu1 # FyQfK0fVkaya8SmVHQ/tOf23mZ4W9k0Ri22QW9p3UgSC5OUDktKxxcCmGL6tXLfO # GSWHIIV4YrTJTT6PNty5REojHJuZHArkF9VnHTERWoTjAzfI3kP+5b4alUdhgAZ7 # ttOu1bVnXfHaqPYl2rPs20ji03LOVWsh/radgE17es5hL+t6lV0eVHrVhsssROWJ # uz2MXMCt7iw7lFPG9LXKGjsmonn2gotGdHIuEg5JnJMJVmixd5LRlkmgYRZKzhxS # CwyoGIq0PhaA7Y+VPct5pCHkijcIIDm0nlkK+0KyepolcqGm0T/GYQRMhHJlGOOm # VQop36wUVUYklUy++vDWeEgEo4s7hxN6mIbf2MSIQ/iIfMZgJxC69oukMUXCrOC3 # SkE/xIkgpfl22MM1itkZ35nNXkMolU1lAgMBAAGjggFOMIIBSjAOBgNVHQ8BAf8E # BAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFH9ZP1Qh2q1P7wXl5qPX # LQaUEggxMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMA8GA1UdEwEB/wQFMAMB # Af8wHwYDVR0jBBgwFoAUci06AjGQQ7kUBU7h6qfHMdEjiTQwWgYDVR0fBFMwUTBP # oE2gS4ZJaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMv # TWljUm9vQ2VyQXV0MjAxMV8yMDExXzAzXzIyLmNybDBeBggrBgEFBQcBAQRSMFAw # TgYIKwYBBQUHMAKGQmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMv # TWljUm9vQ2VyQXV0MjAxMV8yMDExXzAzXzIyLmNydDANBgkqhkiG9w0BAQwFAAOC # AgEAFJQfOChP7onn6fLIMKrSlN1WYKwDFgAddymOUO3FrM8d7B/W/iQ6DxXsDn7D # 5W4wMwYeLystcEqfkjz4NURRgazyMu5yRzQh4LqjA4tStTcJh1opExo7nn5PuPBY # nbu0+THSuVHTe0VTTPVhily/piFrDo3axQ9P4C+Ol5yet+2gTfekICS5xS+cYfSI # vgn0JksVBVMYVI5QFu/qhnLhsEFEUzG8fvv0hjgkO+lkpV9ty6GkN4vdnd7ya6Q6 # aR9y34aiM1qmxaxBi6OUnyNl6fkuun/diTFnYDLTppOkr/mg5WSfCiDVMNCxtj4w # PKC5OmHm1DQIt/MNokbbH3UGsFP1QbzsLocuSqLCvH09Io3fDPTmscR9Y75G4qX7 # RTX8AdBPo0I6OEojf39zuFZt0qOHm65YWQE69cZM2ueE1MB05dNNgHK9gTE7zKvK # /fg8B2qjW88MT/WF5V5uvZGtqa9FSL2RazArA+rDPuf6JGYz4HpgMZHB4S6szWSK # YBv0VisCzfxgeU+dquXW9bd0auYlOB58DPcOYKdc3Se94g+xL4pcEhbB54JOgAkw # YTu/9dLeH2pDqeJZAABVDWRQCaXfO5LgyKwKCLYXpigrZYCjUSBcr+Ve8PFWMhVT # Ql0v4q8J/AUmQN5W4n101cY2L4A7GTQG1h32HHAvfQESWP0xghn+MIIZ+gIBATBu # MFcxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # KDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMjQCEzMAAAIc # +s3Fm+gvfsQAAAAAAhwwDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwG # CisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZI # hvcNAQkEMSIEIAe0OEJvJzJ2XDsr/Bkt3973yLDH/Ns4ic4VKW7LQl1zMEIGCisG # AQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEBBQAEggEAsTYBSQrpiHLtrYy7b3nW # 8i4LnCWfyZ0Ecs7X0O4IPQcS4dLcJZVYM1CSG7rxgCc/tQe9yg0HEdCRV3x1rWz4 # ylmreRYURHgY7LsLyRyj3NTpRy/Bl71sqBXD5kT/U6mnRW6xzB6eiqwODx0Tgqst # lTN/G937VXTk0gmG9KXZpcKCTpKQgCBMpdCfbMm2+hyWs9vSXOi61clAmz0tVLLe # nRGX1Ro75ila7ntRHQH8MrSyAbMDxmza7hfD4QC19kqqQl4RNT3Jw1YiLNFugzRD # fVmDiSRY19KgVLyX5mAyuCEd/UWLzH110rp44yCd2LWDyV0Gk6Y7xURI/AzImt+U # HqGCF7AwghesBgorBgEEAYI3AwMBMYIXnDCCF5gGCSqGSIb3DQEHAqCCF4kwgheF # AgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFaBgsqhkiG9w0BCRABBKCCAUkEggFFMIIB # QQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFlAwQCAQUABCB+cAobO+afO69X4zyK # KJoQ2U9n6UiVzSc+9wOh+/PCvAIGaev4NkGfGBMyMDI2MDQyOTEwMjE0Mi40NjRa # MASAAgH0oIHZpIHWMIHTMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv # bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 # aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0 # ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjo0MzFBLTA1RTAtRDk0NzElMCMG # A1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaCCEf4wggcoMIIFEKAD # AgECAhMzAAACHUvAkoc4hX45AAEAAAIdMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNV # BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w # HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29m # dCBUaW1lLVN0YW1wIFBDQSAyMDEwMB4XDTI1MDgxNDE4NDgzM1oXDTI2MTExMzE4 # NDgzM1owgdMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD # VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xLTAr # BgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEnMCUG # A1UECxMeblNoaWVsZCBUU1MgRVNOOjQzMUEtMDVFMC1EOTQ3MSUwIwYDVQQDExxN # aWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNlMIICIjANBgkqhkiG9w0BAQEFAAOC # Ag8AMIICCgKCAgEAorSgaAA8oOl4ph574zw29egUN8DDepRHLX8FM1zHNJmXG6Kr # SqUKwzcKafopuYdPTETTCvb9aJfESuAU0iGNUFI/D6R0kvdfpe2oPX+E3sbTQvGi # 4JPH5qdIYUaJ45V/4bqe8eNvbWzpC+ZKjH193DeiI1XAI918JoQmBhlEXo/Ton17 # 21luZJgincsf5LjMY3jX84WyXUSX3dsS7h/7xVI+w1yjg7pa+0y3o/me2Tsv6UJU # dSTQap5ORGSfCnclnP1z3IiiWIWr3Vo7aIPWsgJzq3m5GxpxUHCQk8qzUhk50y/u # B+LGE3WIK2C77iy9iFsSfSLUnyMEzGRDW9mXHT4PH7Ozz6CHqQEiNvwcHqlvlCh1 # pHQh1NXQSAqOoVBs5mi6easf6yxWTfe5DrR79503r8pU6VqC2Y9XMRU4wH9QbYXY # sIUZ33Jmndy22W1LBDAbxBPQHCBlncGDU3BgdhVUVLe80mggFO98FdkWho67w4kP # dCTRkvdvkY8PrQYE/nQjHXCa0g7LcMttZb6ejMHfQ+tUWXv6+nZ4Ynkr2OkaxclF # Cw4RIYNMWD26AWbQj/WEdzga18fKtw66L5gzXPza6jFBfPJeKE3H8QAuwpirmH4m # s+5nUjNNQOmNgqJn0U1+3Yn7ClswD79YN0r3fdbYBMDApBZJpNlK7q7HXRsCAwEA # AaOCAUkwggFFMB0GA1UdDgQWBBSEWfBxNEamZtXm8gl92Yq80jfxXTAfBgNVHSME # GDAWgBSfpxVdAF5iXYP05dJlpxtTNRnpcjBfBgNVHR8EWDBWMFSgUqBQhk5odHRw # Oi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNyb3NvZnQlMjBUaW1l # LVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcmwwbAYIKwYBBQUHAQEEYDBeMFwGCCsG # AQUFBzAChlBodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01p # Y3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNydDAMBgNVHRMB # Af8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMA4GA1UdDwEB/wQEAwIHgDAN # BgkqhkiG9w0BAQsFAAOCAgEAkdweB4yxvLspLKq0D+miyD4Q0EcxVFpNZuJxiR54 # gWRkeTDDuymNeB03JhlsBpbwSYJ5uZSgDBCvwHED2VL8lJpFlOprJzxsXWC2NTfA # +O+PO5Fk5jw6LHh6jeBADDEdQAx3Hqi7Zm0JwvQ93z5f6dtxkm29WqOcHYXRXfAQ # wy1hSrLXyfeblqR66jpP/9n0fCkWU4ggsUjQpQ2Ngj1DV09J4Y3y7p9Nd81+Xs6q # Yo++7RKm8qiB/5NDeigOLjlAeFgiEXIRUJW+mJyqpQw+OORlaqcFjR8Hu0G+/7bM # dek68YX+kPpDBk7Ue+I/xgiYJ1xcDRBn/vczLtN72+RIlD4UgXYLuBSCk//pDEPX # 5z39Cr+rkc6E4Y28FPk4BhloAyvp628P4xfElQY8TcxraUbZShypocE6ny95D1K1 # BkltZmrHVKCxmglnuOlM15NKIrXFlXCzdqpCtIwQ417wNAVF/QDPvzzbumPdTi6f # b0tLbScYobV6zvbBsMsKEME4Tj1b9oIXC8dybJq4nbboEXYpRwi1QAbpSNrn+PxG # W9uf1q63FnMJu4gm3Oh63njW/iVf723quzyHrSijWMgY0HiRiHQi0Jyu0h8MdhRU # p7mxbmLQckPiOFwAlIaUN/k725y/aLWpkRU6fqmLlEOyH5WpyLd23AYy9r8v+Qob # a6swggdxMIIFWaADAgECAhMzAAAAFcXna54Cm0mZAAAAAAAVMA0GCSqGSIb3DQEB # CwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYD # VQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAe # Fw0yMTA5MzAxODIyMjVaFw0zMDA5MzAxODMyMjVaMHwxCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0 # YW1wIFBDQSAyMDEwMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA5OGm # TOe0ciELeaLL1yR5vQ7VgtP97pwHB9KpbE51yMo1V/YBf2xK4OK9uT4XYDP/XE/H # ZveVU3Fa4n5KWv64NmeFRiMMtY0Tz3cywBAY6GB9alKDRLemjkZrBxTzxXb1hlDc # wUTIcVxRMTegCjhuje3XD9gmU3w5YQJ6xKr9cmmvHaus9ja+NSZk2pg7uhp7M62A # W36MEBydUv626GIl3GoPz130/o5Tz9bshVZN7928jaTjkY+yOSxRnOlwaQ3KNi1w # jjHINSi947SHJMPgyY9+tVSP3PoFVZhtaDuaRr3tpK56KTesy+uDRedGbsoy1cCG # MFxPLOJiss254o2I5JasAUq7vnGpF1tnYN74kpEeHT39IM9zfUGaRnXNxF803RKJ # 1v2lIH1+/NmeRd+2ci/bfV+AutuqfjbsNkz2K26oElHovwUDo9Fzpk03dJQcNIIP # 8BDyt0cY7afomXw/TNuvXsLz1dhzPUNOwTM5TI4CvEJoLhDqhFFG4tG9ahhaYQFz # ymeiXtcodgLiMxhy16cg8ML6EgrXY28MyTZki1ugpoMhXV8wdJGUlNi5UPkLiWHz # NgY1GIRH29wb0f2y1BzFa/ZcUlFdEtsluq9QBXpsxREdcu+N+VLEhReTwDwV2xo3 # xwgVGD94q0W29R6HXtqPnhZyacaue7e3PmriLq0CAwEAAaOCAd0wggHZMBIGCSsG # AQQBgjcVAQQFAgMBAAEwIwYJKwYBBAGCNxUCBBYEFCqnUv5kxJq+gpE8RjUpzxD/ # LwTuMB0GA1UdDgQWBBSfpxVdAF5iXYP05dJlpxtTNRnpcjBcBgNVHSAEVTBTMFEG # DCsGAQQBgjdMg30BATBBMD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29m # dC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9yeS5odG0wEwYDVR0lBAwwCgYIKwYB # BQUHAwgwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8G # A1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9lJBb186aGMQw # VgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9j # cmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoGCCsGAQUF # BwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3Br # aS9jZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcnQwDQYJKoZIhvcNAQEL # BQADggIBAJ1VffwqreEsH2cBMSRb4Z5yS/ypb+pcFLY+TkdkeLEGk5c9MTO1OdfC # cTY/2mRsfNB1OW27DzHkwo/7bNGhlBgi7ulmZzpTTd2YurYeeNg2LpypglYAA7AF # vonoaeC6Ce5732pvvinLbtg/SHUB2RjebYIM9W0jVOR4U3UkV7ndn/OOPcbzaN9l # 9qRWqveVtihVJ9AkvUCgvxm2EhIRXT0n4ECWOKz3+SmJw7wXsFSFQrP8DJ6LGYnn # 8AtqgcKBGUIZUnWKNsIdw2FzLixre24/LAl4FOmRsqlb30mjdAy87JGA0j3mSj5m # O0+7hvoyGtmW9I/2kQH2zsZ0/fZMcm8Qq3UwxTSwethQ/gpY3UA8x1RtnWN0SCyx # TkctwRQEcb9k+SS+c23Kjgm9swFXSVRk2XPXfx5bRAGOWhmRaw2fpCjcZxkoJLo4 # S5pu+yFUa2pFEUep8beuyOiJXk+d0tBMdrVXVAmxaQFEfnyhYWxz/gq77EFmPWn9 # y8FBSX5+k77L+DvktxW/tM4+pTFRhLy/AsGConsXHRWJjXD+57XQKBqJC4822rpM # +Zv/Cuk0+CQ1ZyvgDbjmjJnW4SLq8CdCPSWU5nR0W2rRnj7tfqAxM328y+l7vzhw # RNGQ8cirOoo6CGJ/2XBjU02N7oJtpQUQwXEGahC0HVUzWLOhcGbyoYIDWTCCAkEC # AQEwggEBoYHZpIHWMIHTMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv # bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 # aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0 # ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjo0MzFBLTA1RTAtRDk0NzElMCMG # A1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcGBSsOAwIa # AxUAuoO+BKbfXzqyfi9GLEdWHkCLeT+ggYMwgYCkfjB8MQswCQYDVQQGEwJVUzET # MBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMV # TWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1T # dGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQsFAAIFAO2bvHEwIhgPMjAyNjA0Mjgy # MzA4MzNaGA8yMDI2MDQyOTIzMDgzM1owdzA9BgorBgEEAYRZCgQBMS8wLTAKAgUA # 7Zu8cQIBADAKAgEAAgIHlQIB/zAHAgEAAgISPTAKAgUA7Z0N8QIBADA2BgorBgEE # AYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAIDB6EgoQowCAIBAAIDAYag # MA0GCSqGSIb3DQEBCwUAA4IBAQBlX3AoucLDJhGsNJYLvfZfXt03uH0vb+1a9e3j # wxvJ+csH9ZFr/3Jj6+9wOZamNeR9+eweFqZY1kbQIGUpDza6Lzu92Hu583yEVAgy # awGoI481wDw9nhgCzKzh1Q6uzOl8u2QpjTbk/blqWa+t7U8DUQDqHiIJAEanTLsy # n50MWovue53bVZXn1uRtG9A4RFr3kC1jCjmISkczAuXHI9zWTg5oHD1Ys1dCwx5v # pfJTU4QVEDBTJkUfjKVhFozTNc+tD2+DYvHk+TFttX4yvjXzlU302Y6aJ6SjS5jv # 7DT30OdIK5U1Kmi5nkoLDINnl5Q8j34EBfVWUaoE3IomH2IQMYIEDTCCBAkCAQEw # gZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT # B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UE # AxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAIdS8CShziFfjkA # AQAAAh0wDQYJYIZIAWUDBAIBBQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0B # CRABBDAvBgkqhkiG9w0BCQQxIgQgJjqKZ+jo7ia6GP4nzMteHCsraJXaQxhzADz/ # A/u91PkwgfoGCyqGSIb3DQEJEAIvMYHqMIHnMIHkMIG9BCCxtpXMXEiLJzrqM77e # p4rTNwrMOj6gpWN9hZvpj5QFUTCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w # IFBDQSAyMDEwAhMzAAACHUvAkoc4hX45AAEAAAIdMCIEICVwcofDY/Q4FHm98a3t # hNVfPEkV/0gMvnPyKIBtk2zmMA0GCSqGSIb3DQEBCwUABIICAKF3YvfNwiS8vdYf # xHAAB7fT7B9JuT/sF7hJ4CBQ0pY/CqGmCL/Mb08trZiw1Wz+BsEqhBdY2x+g+dri # 8o0Oj/lHqzaVYKKyyZl4cgpos6+fLzr+ozJyHloIpO2gaFpkP9K+VIoR4VSL82uY # iHCFLT5gI12842ekLCXvS6+6hwrm8dKFgiqMhmPxQbFvTisfxZz2xHYuPREVwf0j # ywmrz/FSgH+BYp0AjoXBUf9DxVf/U+3A7XX+eYwl87Bx6ygGabsF/Asoyv9D5EBp # wMSawNo+kel6LXSScmZgdAzKuW9iuwGWJyZTKydIZdzXEuBEK1Fq7DPgcxSCz5pX # kGqI6BaiZyYlmpuaI7gnFgF4lUiPplN+PN2lUOO5X8oY9hKfkkQfP2h8w2uWKave # tEKp3KiDOXZP0+hkDa1pqfFnqRRJiARBKHwTNXvBV3xkLFEnJ0ATT50Ua3NGNGsA # calzHKP2hw2GljIKufy+gozFlPLcFHCOcKJfoQLGjABa8q1QR7WBHcA7CRxNaqoA # 0QYmYfEHuBUNOMGwLgdlyJcDf+opB/ZhUHJXJIr8DR/uzzMRwPQLR4l+b/xqC8ig # IAXZAAmGwQWw1OhiTUlxb5B5ZE1Ni6eD6eVV7/0kQZJehQpTWlVgqHLmTGzeWuqF # iV102ve7FvdAUVQbJ4WV0yofXVwR # SIG # End signature block |