PureStorage.RunCommandLauncher.ps1
|
Import-Module PureStorage.RunCommandWrapper $MICROSOFT_AVS_VMFS_VERSION = "1.0.207" function Get-DefaultAzureSubscriptionId { # Azure Resource Manager cmdlets use this settings by default when making Azure Resource Manager requests. # To list all of subscription, it would be Get-AzContext -ListAvailable # In our case, getting default account is sufficient $DefaultAzContext = Get-AzContext if ($DefaultAzContext.Subscription) { return $DefaultAzContext.Subscription.Id } else { throw "Could not find default Azure subscription. Please make sure you are logging in to Azure using 'Connect-AzAccount'. If you have multiple subscriptions, please use 'Select-AzSubscription' to select the subscription you want to use." } } function Invoke-AvsScriptExecution { param ( [string]$AVSCloudName, [string]$SubscriptionId, [string]$AVSResourceGroup, [string]$RunCommandId, [string]$RunCommandExecName, [int]$TimeoutInMinutes = 10, [string]$ProductName, [string]$ProductVersion, [hashtable]$CommandParameters, [Parameter(Mandatory=$true)] $Logger ) # Convert Timeout to ISO 8601 duration format $Timeout = "P0Y0M0DT0H$TimeoutInMinutes`M0S" # Construct JSON payload for script execution $Payload = @{ properties = @{ scriptCmdletId = $RunCommandId timeout = $Timeout parameters = @() } } foreach ($key in $CommandParameters.Keys) { $value = $CommandParameters[$key] # Convert large numbers (above 2^53) to string to avoid 'Number ...M' issue if ($value -is [UInt64] -or ($value -is [int64] -and $value -ge [math]::Pow(2,53))) { $value = "$value" } $Payload.properties.parameters += @{ name = $key type = "Value" value = $value } } $JsonPayload = $Payload | ConvertTo-Json -Depth 10 # REST API call to create script execution $ExecutionUri = "https://management.azure.com/subscriptions/$SubscriptionId/resourceGroups/$AVSResourceGroup/providers/Microsoft.AVS/privateClouds/$AVSCloudName/scriptExecutions/$($RunCommandExecName)?api-version=2022-05-01" $Logger.LogDebug("Invoking script execution: $ExecutionUri`nRun Command ID: $RunCommandId`nPayload: $($JsonPayload)") $ExecutionResponse = Invoke-AzRestMethod -Uri $ExecutionUri -Method Put -Payload $JsonPayload if ($ExecutionResponse.StatusCode -lt 200 -or $ExecutionResponse.StatusCode -gt 299) { throw "Failed to create script execution. Status code: $($ExecutionResponse.StatusCode), Response: $($ExecutionResponse.Content)" } $ExecutionId = ($ExecutionResponse.Content | ConvertFrom-Json -Depth 10).name $Logger.LogDebug("Script Execution ID: $ExecutionId") # Poll for completion with a timeout $StartTime = Get-Date $TimeoutLimit = New-TimeSpan -Minutes $TimeoutInMinutes $LastStatusLogTime = $StartTime $Status = "Unknown" $ExecutionOutput = $null do { Start-Sleep -Seconds 5 # Poll every 5 seconds $Elapsed = (Get-Date) - $StartTime # Fetch script execution status and output in a single call $StatusResponse = Invoke-AzRestMethod -Uri $ExecutionUri -Method Get if ($StatusResponse.StatusCode -eq 200) { $ResponseData = $StatusResponse.Content | ConvertFrom-Json -Depth 10 $Status = $ResponseData.properties.provisioningState $ExecutionOutput = $ResponseData.properties.output } # Log status every 60 seconds to avoid excessive logging if ((Get-Date) - $LastStatusLogTime -ge (New-TimeSpan -Seconds 60)) { $Logger.LogDebug("Current Status: $Status") $LastStatusLogTime = Get-Date } if ($Status -eq "Succeeded") { $Logger.LogDebug("Script executed successfully!") break } elseif ($Status -eq "Failed") { Invoke-CollectAvsScriptExecutionLog -ExecutionId $ExecutionId -SubscriptionId $SubscriptionId -AVSResourceGroup $AVSResourceGroup -AVSCloudName $AVSCloudName -Logger $Logger $FailureReason = $ResponseData.properties.failureReason throw "Script execution failed! Execution ID: $ExecutionId, URI: $ExecutionUri, Details: $FailureReason" } } while ($Elapsed -lt $TimeoutLimit) # If timeout is reached, terminate if ($Elapsed -ge $TimeoutLimit) { Invoke-CollectAvsScriptExecutionLog -ExecutionId $ExecutionId -SubscriptionId $SubscriptionId -AVSResourceGroup $AVSResourceGroup -AVSCloudName $AVSCloudName -Logger $Logger throw "Timeout reached! Script execution did not complete in $TimeoutInMinutes minutes. Execution ID: $ExecutionId, URI: $ExecutionUri, Last known status: $Status" } $Logger.LogInfo("Script Execution Completed. Execution ID: $ExecutionId, URI: $ExecutionUri") if ($ExecutionOutput) { $Logger.LogInfo("Script Output: $ExecutionOutput") } return $ExecutionOutput } function Invoke-CollectAvsScriptExecutionLog { param ( [Parameter(Mandatory=$true)] [string]$ExecutionId, [Parameter(Mandatory=$true)] [string]$SubscriptionId, [Parameter(Mandatory=$true)] [string]$AVSResourceGroup, [Parameter(Mandatory=$true)] [string]$AVSCloudName, [Parameter(Mandatory=$true)] $Logger ) $Url = "https://management.azure.com/subscriptions/$SubscriptionId/resourceGroups/$AVSResourceGroup/providers/Microsoft.AVS/privateClouds/$AVSCloudName/scriptExecutions/$ExecutionId/getExecutionLogs?api-version=2025-09-01" $Body = @("Output", "Error", "Warning", "Information") | ConvertTo-Json $Logger.LogDebug("Collecting AVS script execution logs for Execution ID: $ExecutionId") $Response = Invoke-AzRestMethod -Uri $Url -Method POST -Payload $Body if ($Response.StatusCode -lt 200 -or $Response.StatusCode -gt 299) { $Logger.LogWarning("Failed to collect AVS script execution logs. Status code: $($Response.StatusCode), Response: $($Response.Content)") return } $LogData = $Response.Content | ConvertFrom-Json $SeverityMap = [ordered]@{ "Output" = $LogData.properties.output "Information" = $LogData.properties.information "Warning" = $LogData.properties.warnings "Error" = $LogData.properties.errors } foreach ($Severity in $SeverityMap.Keys) { $Messages = $SeverityMap[$Severity] if ($Messages -and $Messages.Count -gt 0) { $Logger.LogInfo("[RunCommand Execution log $Severity] : $($Messages -join "`n")") } } } function Invoke-RunScript { Param ( [Parameter(Mandatory=$true)] [String] $RunCommandName, [Parameter(Mandatory=$true)] [ValidateSet("Microsoft.AVS.VMFS")] [String] $RunCommandModule, [Parameter(Mandatory=$true)] $Parameters, [Parameter(Mandatory=$false)] [String]$AVSCloudName, [Parameter(Mandatory=$false)] [String]$AVSResourceGroup, [Parameter(Mandatory=$false)] [switch]$GetNamedOutputs, [Parameter(Mandatory=$false)] [int] $TimeoutInMinutes = 10, [Parameter(Mandatory=$true)] $Logger ) $SubscriptionId = Get-DefaultAzureSubscriptionId $Logger.LogInfo("Using Azure default subscription: $SubscriptionId") # Determine which RunCommand version to use $UseAvsClient = $env:CBS_AVS_SCRIPT_EXECUTION_VIA_AVS_CLIENT -eq "true" # Microsoft recommends using the stable version if ($RunCommandModule -eq "Microsoft.AVS.VMFS") { $RunCommandPackageVersion = $MICROSOFT_AVS_VMFS_VERSION } else { throw "Unknown RunCommandModule $RunCommandModule" } # Allow overriding the version via an environment variable $EnvVariableName = ($RunCommandModule -replace "\.", "_") + '_Version' $UpdatedVersion = [Environment]::GetEnvironmentVariable($EnvVariableName) if ($UpdatedVersion) { $Logger.LogWarning("Using customized AVS Run Command version $UpdatedVersion for Module ($RunCommandModule). Please consult with Pure Storage Support before using this version") $RunCommandPackageVersion = $UpdatedVersion } # Test RunCommand availability Test-RunCommandPackageAvailability -SubscriptionId $SubscriptionId -RunCommandModule $RunCommandModule -RunCommandPackageVersion $RunCommandPackageVersion -AVSCloudName $AVSCloudName -AVSResourceGroup $AVSResourceGroup -Logger $Logger # Construct command execution details $CmdletId = "/subscriptions/$SubscriptionId/resourceGroups/$AVSResourceGroup/providers/Microsoft.AVS/privateClouds/$AVSCloudName/scriptPackages/$RunCommandModule@$RunCommandPackageVersion/scriptCmdlets/$RunCommandName" $RandomID = [System.Guid]::NewGuid().ToString().Substring(0,8) $RunCmdExecutionName = "$RunCommandName-PureStorage.RunCommandWrapper-$RandomID" $ProductName = "PureStorage.CBS.AVS" $ProductVersion = (Get-Module $ProductName).Version.ToString() $Logger.LogInfo("Invoking RunCommand $RunCmdExecutionName with ID $CmdletId (use AvsClient: $UseAvsClient) ...") if ($UseAvsClient) { # Use the original AvsClient-based function Invoke-AvsScript -AVSCloudName $AVSCloudName -SubscriptionId $SubscriptionId -AVSResourceGroup $AVSResourceGroup ` -RunCommandId $CmdletId -RunCommandExecName $RunCmdExecutionName -TimeoutInMinutes $TimeoutInMinutes ` -ProductName $ProductName -ProductVersion $ProductVersion ` -CommandParameters $Parameters -ErrorVariable stopError } else { # Use the new REST API-based function Invoke-AvsScriptExecution -AVSCloudName $AVSCloudName -SubscriptionId $SubscriptionId -AVSResourceGroup $AVSResourceGroup ` -RunCommandId $CmdletId -RunCommandExecName $RunCmdExecutionName -TimeoutInMinutes $TimeoutInMinutes ` -ProductName $ProductName -ProductVersion $ProductVersion ` -CommandParameters $Parameters -ErrorVariable stopError -Logger $Logger } if ($stopError) { $Logger.LogInfo("RunCommand $RunCmdExecutionName failed with error:") throw $stopError } if ($GetNamedOutputs) { $Output = Get-RunCommandNamedOutput -RunCmdExecutionName $RunCmdExecutionName -AvsPrivateCloudName $AVSCloudName -AvsResourceGroupName $AVSResourceGroup -Logger $Logger Set-Variable -Name NamedOutputs -Value $Output -Scope Script } } Export-ModuleMember -Function Invoke-RunScript # SIG # Begin signature block # MIIpRQYJKoZIhvcNAQcCoIIpNjCCKTICAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAldRDQoLPfOjtn # 7fGFmiaLYC1o/6ihMSmfvKvi/zPimaCCDfIwggbmMIIEzqADAgECAhB3vQ4DobcI # +FSrBnIQ2QRHMA0GCSqGSIb3DQEBCwUAMFMxCzAJBgNVBAYTAkJFMRkwFwYDVQQK # ExBHbG9iYWxTaWduIG52LXNhMSkwJwYDVQQDEyBHbG9iYWxTaWduIENvZGUgU2ln # bmluZyBSb290IFI0NTAeFw0yMDA3MjgwMDAwMDBaFw0zMDA3MjgwMDAwMDBaMFkx # CzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMS8wLQYDVQQD # EyZHbG9iYWxTaWduIEdDQyBSNDUgQ29kZVNpZ25pbmcgQ0EgMjAyMDCCAiIwDQYJ # KoZIhvcNAQEBBQADggIPADCCAgoCggIBANZCTfnjT8Yj9GwdgaYw90g9z9DljeUg # IpYHRDVdBs8PHXBg5iZU+lMjYAKoXwIC947Jbj2peAW9jvVPGSSZfM8RFpsfe2vS # o3toZXer2LEsP9NyBjJcW6xQZywlTVYGNvzBYkx9fYYWlZpdVLpQ0LB/okQZ6dZu # bD4Twp8R1F80W1FoMWMK+FvQ3rpZXzGviWg4QD4I6FNnTmO2IY7v3Y2FQVWeHLw3 # 3JWgxHGnHxulSW4KIFl+iaNYFZcAJWnf3sJqUGVOU/troZ8YHooOX1ReveBbz/IM # BNLeCKEQJvey83ouwo6WwT/Opdr0WSiMN2WhMZYLjqR2dxVJhGaCJedDCndSsZlR # Qv+hst2c0twY2cGGqUAdQZdihryo/6LHYxcG/WZ6NpQBIIl4H5D0e6lSTmpPVAYq # gK+ex1BC+mUK4wH0sW6sDqjjgRmoOMieAyiGpHSnR5V+cloqexVqHMRp5rC+QBmZ # y9J9VU4inBDgoVvDsy56i8Te8UsfjCh5MEV/bBO2PSz/LUqKKuwoDy3K1JyYikpt # WjYsL9+6y+JBSgh3GIitNWGUEvOkcuvuNp6nUSeRPPeiGsz8h+WX4VGHaekizIPA # tw9FbAfhQ0/UjErOz2OxtaQQevkNDCiwazT+IWgnb+z4+iaEW3VCzYkmeVmda6tj # cWKQJQ0IIPH/AgMBAAGjggGuMIIBqjAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAww # CgYIKwYBBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU2rONwCSQ # o2t30wygWd0hZ2R2C3gwHwYDVR0jBBgwFoAUHwC/RoAK/Hg5t6W0Q9lWULvOljsw # gZMGCCsGAQUFBwEBBIGGMIGDMDkGCCsGAQUFBzABhi1odHRwOi8vb2NzcC5nbG9i # YWxzaWduLmNvbS9jb2Rlc2lnbmluZ3Jvb3RyNDUwRgYIKwYBBQUHMAKGOmh0dHA6 # Ly9zZWN1cmUuZ2xvYmFsc2lnbi5jb20vY2FjZXJ0L2NvZGVzaWduaW5ncm9vdHI0 # NS5jcnQwQQYDVR0fBDowODA2oDSgMoYwaHR0cDovL2NybC5nbG9iYWxzaWduLmNv # bS9jb2Rlc2lnbmluZ3Jvb3RyNDUuY3JsMFYGA1UdIARPME0wQQYJKwYBBAGgMgEy # MDQwMgYIKwYBBQUHAgEWJmh0dHBzOi8vd3d3Lmdsb2JhbHNpZ24uY29tL3JlcG9z # aXRvcnkvMAgGBmeBDAEEATANBgkqhkiG9w0BAQsFAAOCAgEACIhyJsav+qxfBsCq # jJDa0LLAopf/bhMyFlT9PvQwEZ+PmPmbUt3yohbu2XiVppp8YbgEtfjry/RhETP2 # ZSW3EUKL2Glux/+VtIFDqX6uv4LWTcwRo4NxahBeGQWn52x/VvSoXMNOCa1Za7j5 # fqUuuPzeDsKg+7AE1BMbxyepuaotMTvPRkyd60zsvC6c8YejfzhpX0FAZ/ZTfepB # 7449+6nUEThG3zzr9s0ivRPN8OHm5TOgvjzkeNUbzCDyMHOwIhz2hNabXAAC4ShS # S/8SS0Dq7rAaBgaehObn8NuERvtz2StCtslXNMcWwKbrIbmqDvf+28rrvBfLuGfr # 4z5P26mUhmRVyQkKwNkEcUoRS1pkw7x4eK1MRyZlB5nVzTZgoTNTs/Z7KtWJQDxx # pav4mVn945uSS90FvQsMeAYrz1PYvRKaWyeGhT+RvuB4gHNU36cdZytqtq5NiYAk # CFJwUPMB/0SuL5rg4UkI4eFb1zjRngqKnZQnm8qjudviNmrjb7lYYuA2eDYB+sGn # iXomU6Ncu9Ky64rLYwgv/h7zViniNZvY/+mlvW1LWSyJLC9Su7UpkNpDR7xy3bzZ # v4DB3LCrtEsdWDY3ZOub4YUXmimi/eYI0pL/oPh84emn0TCOXyZQK8ei4pd3iu/Y # TT4m65lAYPM8Zwy2CHIpNVOBNNwwggcEMIIE7KADAgECAgxcuW61kTkv+4t8zgQw # DQYJKoZIhvcNAQELBQAwWTELMAkGA1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNp # Z24gbnYtc2ExLzAtBgNVBAMTJkdsb2JhbFNpZ24gR0NDIFI0NSBDb2RlU2lnbmlu # ZyBDQSAyMDIwMB4XDTI0MDMxMTE0MDQxMloXDTI3MDMxMjE0MDQxMlowcjELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFDASBgNVBAcTC1NhbnRhIENs # YXJhMRswGQYDVQQKExJQdXJlIFN0b3JhZ2UsIEluYy4xGzAZBgNVBAMTElB1cmUg # U3RvcmFnZSwgSW5jLjCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMCQ # rioSn48IvHpTg5dofsUYj/pNTDidwjYUrcxVu78NoyhSweG8FhcxDi/SI40+8Fcc # l3D5ZoqpjkFnGhzSwmpxU3J4AP7+fdTZht9eWD1I5qKY07esYwdPDV4yg+csPfdG # PqI2XjRfT5UC3YkXQeUrX8KQZldD4KqvgxzpYcuBwsgHbTb/eArpi68YgFR2jgZG # yZigfy8RuJMrL1thcBOe/VWjUyK21wVT8cuunBYFaStLHhsRBRMDcZBDuTSGC4ev # E6oaCqlQbdMl9YFJ64mDQsKlCxrr7rmLVtcVzKGwmjp4b2xRwE+RmTh6JtrUL9Wx # /3a3UzgAnDNimfwp85zoL48kyLtHqQ3FI8tVKGm+aBOgBZfmURoy7fbp4zKhGgqF # bpOmILO16i4f999YsEEJQgIF3CtyH1R60/ZZWlDmoeeEgjAGrnd14muU5Hk3Cksr # 43uPUAg+fV78Y0fDV85ibm42ZwwPuz6MI4HhYNUlGzRwIQ31vjaGuAMWHNqFKkcO # 0JuIeHQ/gFKPnYIxnGC9H9R4Kw/uMezqtnYJwGU2epB/ABl/w7U4NgU2ZOxWB5BF # y4frZ3f+hNgbjFUjMaXnVFotOJxXntzjdSl4znw8DaKiC5ooChteZMITG9p078p/ # TUsOJQbUtFADSY1hsfCfB7t+gJSNt5peS9GOZIMVAgMBAAGjggGxMIIBrTAOBgNV # HQ8BAf8EBAMCB4AwgZsGCCsGAQUFBwEBBIGOMIGLMEoGCCsGAQUFBzAChj5odHRw # Oi8vc2VjdXJlLmdsb2JhbHNpZ24uY29tL2NhY2VydC9nc2djY3I0NWNvZGVzaWdu # Y2EyMDIwLmNydDA9BggrBgEFBQcwAYYxaHR0cDovL29jc3AuZ2xvYmFsc2lnbi5j # b20vZ3NnY2NyNDVjb2Rlc2lnbmNhMjAyMDBWBgNVHSAETzBNMEEGCSsGAQQBoDIB # MjA0MDIGCCsGAQUFBwIBFiZodHRwczovL3d3dy5nbG9iYWxzaWduLmNvbS9yZXBv # c2l0b3J5LzAIBgZngQwBBAEwCQYDVR0TBAIwADBFBgNVHR8EPjA8MDqgOKA2hjRo # dHRwOi8vY3JsLmdsb2JhbHNpZ24uY29tL2dzZ2NjcjQ1Y29kZXNpZ25jYTIwMjAu # Y3JsMBMGA1UdJQQMMAoGCCsGAQUFBwMDMB8GA1UdIwQYMBaAFNqzjcAkkKNrd9MM # oFndIWdkdgt4MB0GA1UdDgQWBBSzJ9KiDCa3UBiAajy+Iioj5kQjzDANBgkqhkiG # 9w0BAQsFAAOCAgEAHsFQixeQEcoHurq9NWSUt4S39Q+UGP6crmVq3Wwy9g23YbdW # g+SgMxoLUqdoDfA4k4B6Dyoo0jEQzn2kxnsnT9lNHKrcZHH88dv0hjfiH2qAiQWa # zPjS3LhK2J6nhpyipJPpyRaSQG4x4aG0NB2D4WUfUz9CGAYsERJGww/wkTaaxMip # ttKDTaI1C49u1igDfRzIO+Q8vuyyBFLiYTno/df97xtjNC+KxxFhDhl/4tawK6kw # xaVzCMAfj48I67Wbo4DMH6pM1s19as7c3qp92i3MylGKsB6+u+o7UkbSdLNkS4AL # I33CJOUc+GoK3Nt5IXXCFJTQFHBXkBdAur3gmlXEm8vlNG/1Sbxr0H7T1e7ABGH/ # 48o/+PeMLuCc72EeK5dJ4cX9NEQ3QnTsZHwGnYzjEOvOvP0s1c7yNsDbcUHoIqQv # b5xS5aqMU5G+8sdPQ1nwpPf7gGaEEbAVW4w51Pam42qeN9HIPa+ZinXnsN02Kk1Q # w0QwUqzaQy9W/gIquI0KOjw0LmoW9M/8S0lrjpEq2eEeUw9WQLhhUEIirFxGPtjq # iCLiiS9CZ+kf2vWLJKUspkYv+OHT3q805Zg1dJsBFAzEYUFLb1mhmigDEO9bsMor # jECIL2ijE5zHtbGkalrrsPWu8tiDT/B7P9GSYzKfOOy4PoOIfWSK0IxlS7Ixghqp # MIIapQIBATBpMFkxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52 # LXNhMS8wLQYDVQQDEyZHbG9iYWxTaWduIEdDQyBSNDUgQ29kZVNpZ25pbmcgQ0Eg # MjAyMAIMXLlutZE5L/uLfM4EMA0GCWCGSAFlAwQCAQUAoIGXMBkGCSqGSIb3DQEJ # AzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMCsG # CisGAQQBgjcCAQwxHTAboRmAF2h0dHBzOi8vcHVyZXN0b3JhZ2UuY29tMC8GCSqG # SIb3DQEJBDEiBCA+oigZHpDl+DoLC5L0rJZcKCSYdc2JhauEmMS9gCCq/jANBgkq # hkiG9w0BAQEFAASCAgApq5pZ2yWGZh2JwUrCeScgfSsmDHjKIJK+ehoOk27pZLFb # jP6qkJRjNVzyjRpaInyuXulCgUMeTUNHxnuSnehhFlr62WVRM3V8cA7LPzcqFse1 # RsjqCoLyGstHrSWzX1Rg0+ny8P0uSC62zjorxucNktikaAjv9YDRdIAHU3imWp5i # R//n6PJFhXtMNAiEMRxl2gZc+VlPcfTT/aQG1w4Fd7ywWJmaS6popjKMYbZrNCEr # FM0mmeJYdcXhiyF9mYWqEH6BZViM3swx4dR1lbfR7C7Zgs00Vw5BSR1kbM4rgidc # TvDqJ6/G2KRTE/+Deil43LphqMqEpONIch99d6GqGkpsaAAZ6JDVA6EIqj9P1CJ7 # BHQzwP5sDxfqHkoeGonbMNzYYLOQMlWlON8V6wGGLRdqMVcJ/8A1JKFtR4N72t4a # 7LKSwuIC1GA2chN9iFy9ZVFh233ji6cvy4JeGGw3IyKaPBNgeqhO1DZhKTEY4b79 # LHreYyF4QdY7ZOc7fAc5Zl1/OP97yY1WADrcIXVi/z8AnNt1R+JbRvShBHJl81nV # E/XAS/t2wMiHzaGRHR79DKHK9AcXF8Fp/62zr/iVdKQeaDfGdJDNjBETQsGtSUXN # feD6nlNyTaqHiZ44qtLlHXbdPDbxZRec/IM1Y8/RPbgRcsUwt6nSrJ7/rujYMKGC # F3cwghdzBgorBgEEAYI3AwMBMYIXYzCCF18GCSqGSIb3DQEHAqCCF1AwghdMAgED # MQ8wDQYJYIZIAWUDBAIBBQAweAYLKoZIhvcNAQkQAQSgaQRnMGUCAQEGCWCGSAGG # /WwHATAxMA0GCWCGSAFlAwQCAQUABCA/v25kqF9ML/oArxfFdPM8dsZPLihSvJQu # 4HXROv0SGgIRAM+Zty8to/8iWMbORHDzNbQYDzIwMjYwOTMwMTM0NTMxWqCCEzow # ggbtMIIE1aADAgECAhAIT9wzT35FTtvDD4/5khg1MA0GCSqGSIb3DQEBCwUAMGkx # CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4 # RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1lU3RhbXBpbmcgUlNBNDA5NiBTSEEyNTYg # MjAyNSBDQTEwHhcNMjYwODA1MDAwMDAwWhcNMzcxMTA0MjM1OTU5WjBjMQswCQYD # VQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xOzA5BgNVBAMTMkRpZ2lD # ZXJ0IFNIQTI1NiBSU0E0MDk2IFRpbWVzdGFtcCBSZXNwb25kZXIgMjAyNiAxMIIC # IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtnum8sn+zUr41JtMZbP9OMYw # +HwJDpG5xkIu/lqcfNYmMX81YmsUiHLbh9ykpeWBGKTLhYBrAN9Tdg/QEzG32XcO # bmgIblnr0CoQ3WSAeDZ6nH6X6VkFyYkJw3QBJREwvm4UhLzSxmwPA7cFKRTEOMsm # EEj6qJk/dqLEAL+oQYuOwE2UuiX1Vnul8YReIyWd4kgLn9gq6LNXM0UplkR6jL/Q # Hxmb6fMoGBJYbnaUI7XD6cKDpekK2SVMld4iDbzeHDtOaaxldH5IxuNusQ69nd8/ # ZXEiB5Hbxj3RlK13cX1W4DlFXKdv/CEhM8Cj1vvlmvhNroyPdRGbbpBlgyf8Wdu5 # N6ByhFwURn0U6ozlPoxN22v+fviUhP+6DR547OZnpBMWDfei1f5sVGwiiW/KQTWO # K97g+4RJpPzPNV4VYMAwO2jM2Aty2QYPVmOQTJm0msuXnJrSbl2gf9JylpkJlWXq # k1Q4LJsxz+TELoQCZIljbgvTJgoPU2R12ydv8i1UqL/adelA0y7U9Pmmtbze9Xx3 # rtajC5SzQd1jgfwAwsa90v9YcSPdmeoyoBBA/27cCL237l5DTYYPDLQ4ON3OLTGW # nvRb6jDrf/T75gMRfUzSLCBQfBusm9+mSWRlC/Df6S/e9Q8i13CuhzOT2Jx+V/nl # bXM4QoBwlUAhelwwJT0CAwEAAaOCAZUwggGRMAwGA1UdEwEB/wQCMAAwHQYDVR0O # BBYEFBTJY4owLtRK+26U8+bjQH717M3iMB8GA1UdIwQYMBaAFO9vU0rp5AZ8esri # kFb2L9RJ7MtOMA4GA1UdDwEB/wQEAwIHgDAWBgNVHSUBAf8EDDAKBggrBgEFBQcD # CDCBlQYIKwYBBQUHAQEEgYgwgYUwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp # Z2ljZXJ0LmNvbTBdBggrBgEFBQcwAoZRaHR0cDovL2NhY2VydHMuZGlnaWNlcnQu # Y29tL0RpZ2lDZXJ0VHJ1c3RlZEc0VGltZVN0YW1waW5nUlNBNDA5NlNIQTI1NjIw # MjVDQTEuY3J0MF8GA1UdHwRYMFYwVKBSoFCGTmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0 # LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFRpbWVTdGFtcGluZ1JTQTQwOTZTSEEyNTYy # MDI1Q0ExLmNybDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1sBwEwDQYJ # KoZIhvcNAQELBQADggIBAI3FOmEenVIK35msCYB+fShAsWvSYvLBItoNdAgQ2jIq # rGsVsluXMJU/+mRebBc52s6lbKAvOVPXaizmKkMLLflEEKDZQx4CkS2t8aHPjkXh # a3hYZ010htFa3dhNgmalH5vuWvh3tTCf4frTS7gPtGc4Z/xaPhQ2AB1mR8eEe/Wb # H0RWHvVIl6VwQ3+g5FKNfN2N/DWJkf13w2H+2GfqEfbd35Ww8CvoYBjLNIDTadcP # WdgsjsiOaK/7EsKJgLjUNIVgvcaFOLLQ/GlrA+0ZHJoFUbOr5SJN8zykPspXIXlp # DJY/gqFUZRROeab9GVgmhbdOJcD/63RhxPahFUGbckRONqMe6DYAv6/mOG0pWd3c # PStsdcS7buj5DyniwRY8yooMH6ptx5vpP/pZzBPBeZD2U4IsthyxB5Jaa8qrOkB5 # z160TXiM5ADMspZ0TfD9MJoq0tFpFPssKRFhWeEDYPvcUuN7U7lvcdHl4ezQ3NT/ # 7Ffs1sR1yh/LRbdZ3B3Vc6q2WmD8mDC0p9kzl2o73iVtS946IkEj7FkRsZGww1te # YxERROC745xrtjvcw9ZyyUjHZWGRIpJeMNsPquCDf0fkyHtB+J4AiNZqCQk23rxh # +KbpyMTNVKItJ5l92Svl20U9NbqMBOVYl1h54NEYLJq1/xHWFKPNK903zJZA9P2D # MIIGtDCCBJygAwIBAgIQDcesVwX/IZkuQEMiDDpJhjANBgkqhkiG9w0BAQsFADBi # MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 # d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3Qg # RzQwHhcNMjUwNTA3MDAwMDAwWhcNMzgwMTE0MjM1OTU5WjBpMQswCQYDVQQGEwJV # UzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRy # dXN0ZWQgRzQgVGltZVN0YW1waW5nIFJTQTQwOTYgU0hBMjU2IDIwMjUgQ0ExMIIC # IjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtHgx0wqYQXK+PEbAHKx126NG # aHS0URedTa2NDZS1mZaDLFTtQ2oRjzUXMmxCqvkbsDpz4aH+qbxeLho8I6jY3xL1 # IusLopuW2qftJYJaDNs1+JH7Z+QdSKWM06qchUP+AbdJgMQB3h2DZ0Mal5kYp77j # YMVQXSZH++0trj6Ao+xh/AS7sQRuQL37QXbDhAktVJMQbzIBHYJBYgzWIjk8eDrY # hXDEpKk7RdoX0M980EpLtlrNyHw0Xm+nt5pnYJU3Gmq6bNMI1I7Gb5IBZK4ivbVC # iZv7PNBYqHEpNVWC2ZQ8BbfnFRQVESYOszFI2Wv82wnJRfN20VRS3hpLgIR4hjzL # 0hpoYGk81coWJ+KdPvMvaB0WkE/2qHxJ0ucS638ZxqU14lDnki7CcoKCz6eum5A1 # 9WZQHkqUJfdkDjHkccpL6uoG8pbF0LJAQQZxst7VvwDDjAmSFTUms+wV/FbWBqi7 # fTJnjq3hj0XbQcd8hjj/q8d6ylgxCZSKi17yVp2NL+cnT6Toy+rN+nM8M7LnLqCr # O2JP3oW//1sfuZDKiDEb1AQ8es9Xr/u6bDTnYCTKIsDq1BtmXUqEG1NqzJKS4kOm # xkYp2WyODi7vQTCBZtVFJfVZ3j7OgWmnhFr4yUozZtqgPrHRVHhGNKlYzyjlroPx # ul+bgIspzOwbtmsgY1MCAwEAAaOCAV0wggFZMBIGA1UdEwEB/wQIMAYBAf8CAQAw # HQYDVR0OBBYEFO9vU0rp5AZ8esrikFb2L9RJ7MtOMB8GA1UdIwQYMBaAFOzX44LS # cV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEF # BQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp # Z2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGlnaWNlcnQu # Y29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4oDagNIYy # aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5j # cmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEB # CwUAA4ICAQAXzvsWgBz+Bz0RdnEwvb4LyLU0pn/N0IfFiBowf0/Dm1wGc/Do7oVM # Y2mhXZXjDNJQa8j00DNqhCT3t+s8G0iP5kvN2n7Jd2E4/iEIUBO41P5F448rSYJ5 # 9Ib61eoalhnd6ywFLerycvZTAz40y8S4F3/a+Z1jEMK/DMm/axFSgoR8n6c3nuZB # 9BfBwAQYK9FHaoq2e26MHvVY9gCDA/JYsq7pGdogP8HRtrYfctSLANEBfHU16r3J # 05qX3kId+ZOczgj5kjatVB+NdADVZKON/gnZruMvNYY2o1f4MXRJDMdTSlOLh0HC # n2cQLwQCqjFbqrXuvTPSegOOzr4EWj7PtspIHBldNE2K9i697cvaiIo2p61Ed2p8 # xMJb82Yosn0z4y25xUbI7GIN/TpVfHIqQ6Ku/qjTY6hc3hsXMrS+U0yy+GWqAXam # 4ToWd2UQ1KYT70kZjE4YtL8Pbzg0c1ugMZyZZd/BdHLiRu7hAWE6bTEm4XYRkA6T # l4KSFLFk43esaUeqGkH/wyW4N7OigizwJWeukcyIPbAvjSabnf7+Pu0VrFgoiovR # Diyx3zEdmcif/sYQsfch28bZeUz2rtY/9TCA6TD8dC3JE3rYkrhLULy7Dc90G6e8 # BlqmyIjlgp2+VqsS9/wQD7yFylIz0scmbKvFoW2jNrbM1pD2T7m3XDCCBY0wggR1 # oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZIhvcNAQEMBQAwZTELMAkGA1UE # BhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2lj # ZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJRCBSb290IENBMB4X # DTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjELMAkGA1UEBhMCVVMxFTAT # BgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEh # MB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MIICIjANBgkqhkiG9w0B # AQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBpM+zCpyUuySE98orYWcLh # Kac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR0uRf1gU8Ug9SH8aeFaV+ # vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0O21x4i0MG+4g1ckgHWMp # Lc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53yEioZldXn1RYjgwrt0+n # MNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4x7axxLVqGDgDEI3Y1Dek # LgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3VdeGbZOjFEmjNAvwjXWkmk # wuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1CdoeJl2l6SPDgohIbZpp0 # yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJhbesz2cXfSwQAzH0clcOP # 9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz0YkH4b235kOkGLimdwHh # D5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNBERJb5RBQ6zHFynIWIgnf # fEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+CiaZ9eRpL5gdLfXZqbId # 5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOzX44LS # cV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3zbcgP # MA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0 # dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2NhY2Vy # dHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDBFBgNV # HR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRB # c3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgwBgYEVR0gADANBgkqhkiG9w0B # AQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cHvZqsoYcs7IVeqRq7IviHGmlU # Iu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8UgPITtAq3votVs/59PesMHqa # i7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTnf+hZqPC/Lwum6fI0POz3A8eH # qNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxUjG/voVA9/HYJaISfb8rbII01 # YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8jLfR+cWojayL/ErhULSd+2DrZ # 8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDGCA3wwggN4AgEBMH0waTELMAkGA1UE # BhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2Vy # dCBUcnVzdGVkIEc0IFRpbWVTdGFtcGluZyBSU0E0MDk2IFNIQTI1NiAyMDI1IENB # MQIQCE/cM09+RU7bww+P+ZIYNTANBglghkgBZQMEAgEFAKCB0TAaBgkqhkiG9w0B # CQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI2MDkzMDEzNDUzMVow # KwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQUUdmr2gNJc9hPQmaspIJI5rNpxDkwLwYJ # KoZIhvcNAQkEMSIEIA16lBs9mrZWEW4LhSKw7CfipO2gSzjGvZoYdDBcg26pMDcG # CyqGSIb3DQEJEAIvMSgwJjAkMCIEIC2gnaf0Ex+f5y22xebpyWVnVa8EPx6nQswN # ISDhQev8MA0GCSqGSIb3DQEBAQUABIICAHLM/4aAjbxiwwS+i2DRJ7gx2gSHsYbr # LUrJQ2WCIE1L8y0sLn7IjyWk62+wOHLqMJ9vZsBm2FiNvTUyUkA9W64DFi7GGyob # WNvQzY69jdPhR2lsSGT4U9LBGuBDx4L3bhnzRwt5dhweAr4qKE69394mJNQSTQwy # +kOuBjGe5y5JCh80yuU/Rvv3VWFrdndjGeqq2vf1HNIVfNbtncbEO2QNQzbPxht9 # XL7rA8Urxcfc+2C4YPQHRe2/sSGwJ4+xrLmm7xehIaJI2JtdNoNqpD5bFiLg1XO2 # 619TPigw+BuevAV/bMMl4OY5GJ3rPJNjNrM+vfyoA4ucyupzxfPAzV/9MZDU18Wa # oLKYNjrjwFOgzVpQlga4fM2+/920vla6ZPy4GpD+6XTBy1xOO5cktt1AueKomMRX # qdadG7ItsBhxRLK2YsBerakM8pXeNeVGjT1ulGnGh2z3NWCwh2dCbrLVpXrtMoQW # V3Vn3Wh37XanQM7MGWRQzGl5dqjeOz3vtlY8cXM3clG/4n6bSk/PjJOKrXpKFa2q # 80sNwEZ9UQ1U6RktkUMNYZyYU5M7LWdmqII8VPkXy21iwkAKKlk2Jqew1r7xtp24 # DeLS6ATTBSiAJs7XXLJwVXak6YC7mJezlj1GHM2bhMsuhVLE0GsMl2e+KO0b9WaT # MTSnBAva8CB4 # SIG # End signature block |