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 # MIIncAYJKoZIhvcNAQcCoIInYTCCJ10CAQExDzANBglghkgBZQMEAgEFADB5Bgor # 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/AUmQN5W4n101cY2L4A7GTQG1h32HHAvfQESWP0xghn9MIIZ+QIBATBu # MFcxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # KDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMjQCEzMAAAIc # +s3Fm+gvfsQAAAAAAhwwDQYJYIZIAWUDBAIBBQCgga4wGQYJKoZIhvcNAQkDMQwG # CisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZI # hvcNAQkEMSIEIAe0OEJvJzJ2XDsr/Bkt3973yLDH/Ns4ic4VKW7LQl1zMEIGCisG # AQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEBBQAEggEAsTYBSQrpiHLtrYy7b3nW # 8i4LnCWfyZ0Ecs7X0O4IPQcS4dLcJZVYM1CSG7rxgCc/tQe9yg0HEdCRV3x1rWz4 # ylmreRYURHgY7LsLyRyj3NTpRy/Bl71sqBXD5kT/U6mnRW6xzB6eiqwODx0Tgqst # lTN/G937VXTk0gmG9KXZpcKCTpKQgCBMpdCfbMm2+hyWs9vSXOi61clAmz0tVLLe # nRGX1Ro75ila7ntRHQH8MrSyAbMDxmza7hfD4QC19kqqQl4RNT3Jw1YiLNFugzRD # fVmDiSRY19KgVLyX5mAyuCEd/UWLzH110rp44yCd2LWDyV0Gk6Y7xURI/AzImt+U # HqGCF68wgherBgorBgEEAYI3AwMBMYIXmzCCF5cGCSqGSIb3DQEHAqCCF4gwgheE # AgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFZBgsqhkiG9w0BCRABBKCCAUgEggFEMIIB # QAIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFlAwQCAQUABCB+cAobO+afO69X4zyK # KJoQ2U9n6UiVzSc+9wOh+/PCvAIGahCT2bPoGBIyMDI2MDUyNzEwMzIwMy44NVow # BIACAfSggdmkgdYwgdMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u # MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp # b24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRl # ZDEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjJEMUEtMDVFMC1EOTQ3MSUwIwYD # VQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloIIR/jCCBygwggUQoAMC # AQICEzMAAAIS0QgGPMoYT6oAAQAAAhIwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UE # BhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc # BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0 # IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcNMjUwODE0MTg0ODE1WhcNMjYxMTEzMTg0 # ODE1WjCB0zELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV # BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEtMCsG # A1UECxMkTWljcm9zb2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMScwJQYD # VQQLEx5uU2hpZWxkIFRTUyBFU046MkQxQS0wNUUwLUQ5NDcxJTAjBgNVBAMTHE1p # Y3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEBAQUAA4IC # DwAwggIKAoICAQCvTNOgOSlZC2xl6RLRxXSq4N0pJMaSi+8FpkfQ4AgSLQ7cJw7v # smJfxzgSmr26JsdVlnVb8ui58TXva+RFc75Bg21ZaisQInYBxlDXRukCW2SFk2Je # VUnwvdDOEqCteuYySbYn7+DzVTbck9w7jBccR+2idPfCl3//3fquObDEybs2RuzK # Bsl/7gBUEw2vhow9CEF3vqh3QowHpau/IQY45TTz3c4W59LkQN7LifjhaBrEkTRW # e/f846+47DkqnUo+qONgn4v3LAu4Ey1wN8uk1A7+HV4USgytuIQzrtM582Vd/FsP # PgyWxi8uKjGB3ZfN7LVDGtXX6L4nJUMkyJJ72Ao67OmJBAUTX0NQ+CyJ3KMtPNcS # FJUsGVGivR4JV/uALpF+Tw1jes7ayCA4vv29TkW4MpOH+wg61xQd4cLjaMEYH619 # 0oLUo4FH5SU31o7ODyalQ0jYWCpC+KtU/2mlt4xR++nbAD2+jJOJFa8ODMLGjzGU # nWexxhMchCuaQX2P8JrQgOa3x+86frieeUk4ZRhlgcwLWXTG2CRhMTURJSqqRrAq # TuKpvF2cvLJxL51H7NrE+50wMutAXPyWB/L2huTQPwcLZ3OFalg2fmF2Sg8TOAKp # BQ6ny/dCP+x3hqfK6l7kqgKSMAE4/fKFJDhb1n4OsmeEz+tuxcLuhO/bpwIDAQAB # o4IBSTCCAUUwHQYDVR0OBBYEFFIFuVLFvlxgpg7C939V+hc5+7feMB8GA1UdIwQY # MBaAFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCGTmh0dHA6 # Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyMFRpbWUt # U3RhbXAlMjBQQ0ElMjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4wXAYIKwYB # BQUHMAKGUGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWlj # cm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwGA1UdEwEB # /wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQDAgeAMA0G # CSqGSIb3DQEBCwUAA4ICAQBedICSZfv1FzuXFLXA6jNcntXSi7kOe1Vw4mmUiQOO # 8Q3wEhCmHhT6BI4tm5VF4//P1h1SJSRw/AvU8zbjn+QRfUR6CnE1zwcMDqEoIeDQ # As0Ry7GcY6WAouiRd9R6vUPmGJbw8AEz1H6d6K15OA9ppGUkW8ZNi+i1zS6oaIRL # mExCEvxE0WGlL1FwhYe2dAYSet05S8ICGzgV5WJrByYtMq/7XzvRz8x5MeLRAN15 # H7v9aDiqGQnaTWIQcl2Zh/yshXQ1EFlx1FNN0d57flJc/md40J8CSMMi5nJxG53q # PM8sI0uI1jMZcGzDMnKCPgR5I5FPvAy9oW4p5EBehcLfDBi+AiwjXOfcMZjrblj7 # JlKLxQ1I0e6uLfpm/1r5Di8nAOlgrfpLRMHal/vEuKIffaeTtgrvdGD3xbp9nYg2 # 7NjNnsaGC999+SPgRReDUTQR99jWkSqRukNM/uH8MGq3Og9ezLDYxSH7vK9ZyrYE # ZlK5xroJjpfiKgy5zk9amya846WLsBE0DsBvyQp0JzaA0MtpyCWzB5kRz39VAHqG # Wz9voqTfaeTC4cTEqVp6hJWsoHlT3GWnX5zwn/sYmPhHDsCJDy1yn5aZ/IPwrFfZ # CUpsOLhJSOeCW/jrXtHz5r9wNYnJoy3zbv1aft/bIx503uR8YhDlJmPrpF2F2Vk6 # rzCCB3EwggVZoAMCAQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZIhvcNAQEL # BQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH # EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNV # BAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDEwMB4X # DTIxMDkzMDE4MjIyNVoXDTMwMDkzMDE4MzIyNVowfDELMAkGA1UEBhMCVVMxEzAR # BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p # Y3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3Rh # bXAgUENBIDIwMTAwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDk4aZM # 57RyIQt5osvXJHm9DtWC0/3unAcH0qlsTnXIyjVX9gF/bErg4r25PhdgM/9cT8dm # 95VTcVrifkpa/rg2Z4VGIwy1jRPPdzLAEBjoYH1qUoNEt6aORmsHFPPFdvWGUNzB # RMhxXFExN6AKOG6N7dcP2CZTfDlhAnrEqv1yaa8dq6z2Nr41JmTamDu6GnszrYBb # fowQHJ1S/rboYiXcag/PXfT+jlPP1uyFVk3v3byNpOORj7I5LFGc6XBpDco2LXCO # Mcg1KL3jtIckw+DJj361VI/c+gVVmG1oO5pGve2krnopN6zL64NF50ZuyjLVwIYw # XE8s4mKyzbnijYjklqwBSru+cakXW2dg3viSkR4dPf0gz3N9QZpGdc3EXzTdEonW # /aUgfX782Z5F37ZyL9t9X4C626p+Nuw2TPYrbqgSUei/BQOj0XOmTTd0lBw0gg/w # EPK3Rxjtp+iZfD9M269ewvPV2HM9Q07BMzlMjgK8QmguEOqEUUbi0b1qGFphAXPK # Z6Je1yh2AuIzGHLXpyDwwvoSCtdjbwzJNmSLW6CmgyFdXzB0kZSU2LlQ+QuJYfM2 # BjUYhEfb3BvR/bLUHMVr9lxSUV0S2yW6r1AFemzFER1y7435UsSFF5PAPBXbGjfH # CBUYP3irRbb1Hode2o+eFnJpxq57t7c+auIurQIDAQABo4IB3TCCAdkwEgYJKwYB # BAGCNxUBBAUCAwEAATAjBgkrBgEEAYI3FQIEFgQUKqdS/mTEmr6CkTxGNSnPEP8v # BO4wHQYDVR0OBBYEFJ+nFV0AXmJdg/Tl0mWnG1M1GelyMFwGA1UdIARVMFMwUQYM # KwYBBAGCN0yDfQEBMEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWljcm9zb2Z0 # LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0bTATBgNVHSUEDDAKBggrBgEF # BQcDCDAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD # VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvXzpoYxDBW # BgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny # bC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYIKwYBBQUH # AQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtp # L2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDANBgkqhkiG9w0BAQsF # AAOCAgEAnVV9/Cqt4SwfZwExJFvhnnJL/Klv6lwUtj5OR2R4sQaTlz0xM7U518Jx # Nj/aZGx80HU5bbsPMeTCj/ts0aGUGCLu6WZnOlNN3Zi6th542DYunKmCVgADsAW+ # iehp4LoJ7nvfam++Kctu2D9IdQHZGN5tggz1bSNU5HhTdSRXud2f8449xvNo32X2 # pFaq95W2KFUn0CS9QKC/GbYSEhFdPSfgQJY4rPf5KYnDvBewVIVCs/wMnosZiefw # C2qBwoEZQhlSdYo2wh3DYXMuLGt7bj8sCXgU6ZGyqVvfSaN0DLzskYDSPeZKPmY7 # T7uG+jIa2Zb0j/aRAfbOxnT99kxybxCrdTDFNLB62FD+CljdQDzHVG2dY3RILLFO # Ry3BFARxv2T5JL5zbcqOCb2zAVdJVGTZc9d/HltEAY5aGZFrDZ+kKNxnGSgkujhL # mm77IVRrakURR6nxt67I6IleT53S0Ex2tVdUCbFpAUR+fKFhbHP+CrvsQWY9af3L # wUFJfn6Tvsv4O+S3Fb+0zj6lMVGEvL8CwYKiexcdFYmNcP7ntdAoGokLjzbaukz5 # m/8K6TT4JDVnK+ANuOaMmdbhIurwJ0I9JZTmdHRbatGePu1+oDEzfbzL6Xu/OHBE # 0ZDxyKs6ijoIYn/ZcGNTTY3ugm2lBRDBcQZqELQdVTNYs6FwZvKhggNZMIICQQIB # ATCCAQGhgdmkgdYwgdMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u # MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp # b24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRl # ZDEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjJEMUEtMDVFMC1EOTQ3MSUwIwYD # VQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoD # FQDlUcGviHTQQ3uNR1DfdIT6puT/wKCBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0 # YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBCwUAAgUA7cEAtjAiGA8yMDI2MDUyNzA1 # MzM0MloYDzIwMjYwNTI4MDUzMzQyWjB3MD0GCisGAQQBhFkKBAExLzAtMAoCBQDt # wQC2AgEAMAoCAQACAgmXAgH/MAcCAQACAhP3MAoCBQDtwlI2AgEAMDYGCisGAQQB # hFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAw # DQYJKoZIhvcNAQELBQADggEBAGBpvrO8uXagYZCZwNF84B08+OqFbEZoUsg9nPQ8 # KiwzHV9Zsd2KERCCEOH843cWWZCofwjKPjftqAJCS35LTQ+hOR8bLsMXQcTU3W+2 # JMVaRh6Q2xSFxDjSvYS8dpCq8tSAzUrRdnh4cYNmIZ1o7hkVL7WDCdN47ODv1l8g # FiEUKdh1siR0WLuoA4aZOoGk52XBgaYJhfjr+at6pNhw+/VWiSvX9l5kIGjYP6ij # u8hrbTNZjJkMt7jImiIXRXuefIhUA7/Fn3UwjLXHOLHqfVtovYNuLgYZPX+iMowd # hBRKt5a+qeD7Ii7TzwNLvrk27Xw9yoe8A/AItczW9RABwS8xggQNMIIECQIBATCB # kzB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD # Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAhLRCAY8yhhPqgAB # AAACEjANBglghkgBZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJ # EAEEMC8GCSqGSIb3DQEJBDEiBCBIchYW7nE1ZQEVDKY0rXfhKno3634jQQIUJ6pW # 7zGh5DCB+gYLKoZIhvcNAQkQAi8xgeowgecwgeQwgb0EIHP5fka87taeCScfgFl5 # hT4HMEvUnLmgnzBWVM0jF9iDMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNV # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv # c29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAg # UENBIDIwMTACEzMAAAIS0QgGPMoYT6oAAQAAAhIwIgQgDOB+dzMnfCpQQmo+IvIr # rkuJQwawnA5fDVk0qtSis9AwDQYJKoZIhvcNAQELBQAEggIAF4LTDhllVET4I8Ks # 8j+igT4CuU6dKGMDt295Nz9yrf9d2xSyqk4pRKynvJzApXnxY9yMnKcaOd2WHYQl # Tvzp41eJouYiFLOvpc2EREP+4RocsncNxjsvsr1NhlEsDqtEMN3JC+fAlbIH7vOk # 0V2Ut0iYiMDMByZ7/w7VtQOaIlRN2/kJSC1QxUqZSZ2fAQOu56yRCk51oNMpcbYX # nonUgmdB9Cc2v5Nk43/GuBXNnQrIHlsYnDUpCXwdjzjmadUmRKyaXRlAXT5RIo7Q # fQk7b9Fu/YllnFZXLTqRD7J3WDFi2gwQh+2TMoAn/xkTRjcVR3j8ylTUEJtPwR02 # HBQJ2/MiqHh+LNdrejxQD+MDG4n6H4tFMXi3qWi9MI/RHkCz/1RdyosE59Ear+kR # UHlVLjIv9W0I+moMc9UhR/hHPoQQBqc37aIPj/qi8YznsnK15WwgyNIkpEk4ydcz # JJ1ygHyhA29yBFgmI0nu/VXCNexPgZ0ZCQ97a6WIVUpFR8E1qjSwORN/vzRs6tkV # SgeeTIrFXCqwZyItKL5C4FpsrHgTO92uTHgxUcguuq+fLdbWD7p2NzopD6QqPn5b # ToyjGWWfBX2ichSyu+whgzYUDD29jtbNjK3ZTXJJwAam+R/5GQ7Pjf0yuOnYTvxH # 9JFXgufimGGUJbmbFdVHq8RYeFE= # SIG # End signature block |