Framework/Helpers/WebRequestHelper.ps1
Set-StrictMode -Version Latest class WebRequestHelper { hidden static [string] $AzureManagementUri = "https://management.azure.com/"; hidden static [string] $GraphApiUri = "https://graph.windows.net/"; hidden static [string] $ClassicManagementUri = "https://management.core.windows.net/"; static [System.Object[]] InvokeGetWebRequest([string] $uri, [Hashtable] $headers) { return [WebRequestHelper]::InvokeWebRequest([Microsoft.PowerShell.Commands.WebRequestMethod]::Get, $uri, $headers, $null); } static [System.Object[]] InvokeGetWebRequest([string] $uri) { return [WebRequestHelper]::InvokeGetWebRequest($uri, [WebRequestHelper]::GetAuthHeaderFromUri($uri)); } hidden static [string] GetApplicationInsightsEndPoint() { $rmContext = [ContextHelper]::GetCurrentContext(); $azureEnv= $rmContext.Environment.Name if($azureEnv -eq "AzureUSGovernment") { return "https://dc.applicationinsights.us/v2/track" } elseif ($azureEnv -eq "AzureChinaCloud" ) { return "https://dc.applicationinsights.azure.cn/v2/track" } else { return "https://dc.services.visualstudio.com/v2/track" } } hidden static [string] GetLADataCollectorAPI() { $rmContext = [ContextHelper]::GetCurrentContext(); $azureEnv= $rmContext.Environment.Name if($azureEnv -eq "AzureUSGovernment") { return ".ods.opinsights.azure.us" } elseif ($azureEnv -eq "AzureChinaCloud" ) { return ".ods.opinsights.azure.cn" } else { return ".ods.opinsights.azure.com" } } hidden static [string] GetGraphUrl() { $rmContext = [ContextHelper]::GetCurrentContext(); $azureEnv= $rmContext.Environment.Name if(-not [string]::IsNullOrWhiteSpace($azureEnv) -and ($azureEnv -ne [Constants]::DefaultAzureEnvironment)) { return [ContextHelper]::GetCurrentContext().Environment.GraphUrl } return "https://graph.windows.net/" } hidden static [string] GetResourceManagerUrl() { $rmContext = [ContextHelper]::GetCurrentContext(); $azureEnv= $rmContext.Environment.Name if(-not [string]::IsNullOrWhiteSpace($azureEnv) -and ($azureEnv -ne [Constants]::DefaultAzureEnvironment)) { return [ContextHelper]::GetCurrentContext().Environment.ResourceManagerUrl } return "https://management.azure.com/" } hidden static [string] GetServiceManagementUrl() { $rmContext = [ContextHelper]::GetCurrentContext(); $azureEnv= $rmContext.Environment.Name if(-not [string]::IsNullOrWhiteSpace($azureEnv) -and ($azureEnv -ne [Constants]::DefaultAzureEnvironment)) { return [ContextHelper]::GetCurrentContext().Environment.ServiceManagementUrl } return "https://management.core.windows.net/" } hidden static [Hashtable] GetAuthHeaderFromUri([string] $uri) { [System.Uri] $validatedUri = $null; if([System.Uri]::TryCreate($uri, [System.UriKind]::Absolute, [ref] $validatedUri)) { $token = [ContextHelper]::GetAccessToken($validatedUri.GetLeftPart([System.UriPartial]::Authority)); # Validate if token is PAT using lenght (PAT has lengh of 52) else go with default bearer token if($token.length -eq 52) { $user = "" $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,$token))) return @{ "Authorization"= ("Basic " + $base64AuthInfo); "Content-Type"="application/json" }; } else { return @{ "Authorization"= ("Bearer " + $token); "Content-Type"="application/json" }; } } return @{ "Content-Type"="application/json" }; } hidden static [Hashtable] GetAuthHeaderFromUriPatch([string] $uri) { [System.Uri] $validatedUri = $null; if ([System.Uri]::TryCreate($uri, [System.UriKind]::Absolute, [ref] $validatedUri)) { $token = [ContextHelper]::GetAccessToken($validatedUri.GetLeftPart([System.UriPartial]::Authority)); $user = "" $base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user, $token))) return @{ "Authorization" = ("Basic " + $base64AuthInfo) }; } return @{}; } static [System.Object[]] InvokePostWebRequest([string] $uri, [Hashtable] $headers, [System.Object] $body) { return [WebRequestHelper]::InvokeWebRequest([Microsoft.PowerShell.Commands.WebRequestMethod]::Post, $uri, $headers, $body); } static [System.Object[]] InvokePostWebRequest([string] $uri, [System.Object] $body) { return [WebRequestHelper]::InvokePostWebRequest($uri, [WebRequestHelper]::GetAuthHeaderFromUri($uri), $body); } static [System.Object[]] InvokeWebRequest([Microsoft.PowerShell.Commands.WebRequestMethod] $method, [string] $uri, [System.Object] $body) { return [WebRequestHelper]::InvokeWebRequest($method, $uri, [WebRequestHelper]::GetAuthHeaderFromUri($uri), $body); } static [System.Object[]] InvokeWebRequest([Microsoft.PowerShell.Commands.WebRequestMethod] $method, [string] $uri, [Hashtable] $headers, [System.Object] $body) { return [WebRequestHelper]::InvokeWebRequest($method, $uri, $headers, $body, $Null); } static [System.Object[]] InvokeWebRequest([Microsoft.PowerShell.Commands.WebRequestMethod] $method, [string] $uri, [Hashtable] $headers, [System.Object] $body, [string] $contentType) { return [WebRequestHelper]::InvokeWebRequest($method, $uri, $headers, $body, $contentType, $false, $false) } static [System.Object[]] InvokeWebRequest([Microsoft.PowerShell.Commands.WebRequestMethod] $method, [string] $uri, [Hashtable] $headers, [System.Object] $body, [string] $contentType, [Hashtable] $propertiesToReplace) { $outputValues = @(); [System.Uri] $validatedUri = $null; $orginalUri = ""; while ([System.Uri]::TryCreate($uri, [System.UriKind]::Absolute, [ref] $validatedUri)) { if([string]::IsNullOrWhiteSpace($orginalUri)) { $orginalUri = $validatedUri.AbsoluteUri; } [int] $retryCount = 3 $success = $false; while($retryCount -gt 0 -and -not $success) { $retryCount = $retryCount -1; try { $requestResult = $null; if ($method -eq [Microsoft.PowerShell.Commands.WebRequestMethod]::Get) { $requestResult = Invoke-WebRequest -Method $method -Uri $validatedUri -Headers $headers -UseBasicParsing } elseif ($method -eq [Microsoft.PowerShell.Commands.WebRequestMethod]::Post -or $method -eq [Microsoft.PowerShell.Commands.WebRequestMethod]::Put) { if($uri.EndsWith("`$batch")) { $requestResult = Invoke-WebRequest -Method $method -Uri $validatedUri -Headers $headers -Body $body -ContentType $contentType -UseBasicParsing $success = $true $uri = [string]::Empty } else { $requestResult = Invoke-WebRequest -Method $method -Uri $validatedUri -Headers $headers -Body ($body | ConvertTo-Json -Depth 10 -Compress) -UseBasicParsing } } else { throw [System.ArgumentException] ("The web request method type '$method' is not supported.") } if ($null -ne $requestResult -and $requestResult.StatusCode -ge 200 -and $requestResult.StatusCode -le 399) { if (!$success -and $null -ne $requestResult.Content) { $resultContent = $requestResult.Content if($propertiesToReplace.Keys.Count -gt 0) { $propertiesToReplace.Keys | Foreach-Object { $resultContent = $resultContent.ToString().Replace($_, $propertiesToReplace[$_]) } } $json = ConvertFrom-Json $resultContent if ($null -ne $json) { if (($json | Get-Member -Name "value") -and $json.value) { $outputValues += $json.value; } else { $outputValues += $json; } if (($json | Get-Member -Name "nextLink") -and $json.nextLink) { $uri = $json.nextLink } elseif (($json | Get-Member -Name "@odata.nextLink") -and $json."@odata.nextLink") { $uri = $json."@odata.nextLink" } elseif($requestResult.Headers.ContainsKey('x-ms-continuation-NextPartitionKey')) { $nPKey = $requestResult.Headers["x-ms-continuation-NextPartitionKey"] $uri= $orginalUri + "&NextPartitionKey=$nPKey" } else { $uri = [string]::Empty; } } } } $success = $true; } catch { #eat the exception until it is in retry mode and throw once the retry is done if($retryCount -eq 0) { if([Helpers]::CheckMember($_,"Exception.Response.StatusCode") -and $_.Exception.Response.StatusCode -eq "Forbidden"){ throw ([SuppressedException]::new(("You do not have permission to view the requested resource."), [SuppressedExceptionType]::InvalidOperation)) } elseif ([Helpers]::CheckMember($_,"Exception.Message")){ throw ([SuppressedException]::new(($_.Exception.Message.ToString()), [SuppressedExceptionType]::InvalidOperation)) } else { throw; } } } } } return $outputValues; } static [System.Object[]] InvokeWebRequest([Microsoft.PowerShell.Commands.WebRequestMethod] $method, [string] $uri, [Hashtable] $headers, [System.Object] $body, [string] $contentType, [bool] $isRetryRequired, [bool] $returnRawResponse) { $outputValues = @(); [System.Uri] $validatedUri = $null; $orginalUri = ""; while ([System.Uri]::TryCreate($uri, [System.UriKind]::Absolute, [ref] $validatedUri)) { [int] $retryCount = 1 if($isRetryRequired) { $retryCount = 3 } if([string]::IsNullOrWhiteSpace($orginalUri)) { $orginalUri = $validatedUri.AbsoluteUri; } $success = $false; while($retryCount -gt 0 -and -not $success) { $retryCount = $retryCount -1; try { $requestResult = $null; if ($method -eq [Microsoft.PowerShell.Commands.WebRequestMethod]::Get) { $requestResult = Invoke-WebRequest -Method $method -Uri $validatedUri -Headers $headers -UseBasicParsing } elseif ($method -eq [Microsoft.PowerShell.Commands.WebRequestMethod]::Post -or $method -eq [Microsoft.PowerShell.Commands.WebRequestMethod]::Put -or [Microsoft.PowerShell.Commands.WebRequestMethod]::Patch) { if($uri.EndsWith("`$batch")) { $requestResult = Invoke-WebRequest -Method $method -Uri $validatedUri -Headers $headers -Body $body -ContentType $contentType -UseBasicParsing $success = $true $uri = [string]::Empty } elseif($uri.Contains("mspim")) { $requestResult = Invoke-WebRequest -Method $method -Uri $validatedUri -Headers $headers -Body $body -ContentType $contentType -UseBasicParsing } else { $requestResult = Invoke-WebRequest -Method $method -Uri $validatedUri -Headers $headers -Body ($body | ConvertTo-Json -Depth 10 -Compress) -UseBasicParsing } } else { throw [System.ArgumentException] ("The web request method type '$method' is not supported.") } if($returnRawResponse) { return $requestResult } if ($null -ne $requestResult -and $requestResult.StatusCode -ge 200 -and $requestResult.StatusCode -le 399) { if (!$success -and $null -ne $requestResult.Content) { $json = ConvertFrom-Json $requestResult.Content if ($null -ne $json) { if (($json | Get-Member -Name "value") -and $json.value) { $outputValues += $json.value; } else { $outputValues += $json; } if (($json | Get-Member -Name "nextLink") -and $json.nextLink) { $uri = $json.nextLink } elseif($requestResult.Headers.ContainsKey('x-ms-continuation-NextPartitionKey')) { $nPKey = $requestResult.Headers["x-ms-continuation-NextPartitionKey"] $uri= $orginalUri + "&NextPartitionKey=$nPKey" } else { $uri = [string]::Empty; } } } } $success = $true; } catch { #eat the exception until it is in retry mode and throw once the retry is done if($retryCount -eq 0) { if ($uri.Contains("mspim") -and [Helpers]::CheckMember($_,"ErrorDetails.Message")) { if( -not $returnRawResponse) { $err = $_.ErrorDetails.Message| ConvertFrom-Json throw ([SuppressedException]::new(($err), [SuppressedExceptionType]::Generic)) } else { throw $_; } } elseif([Helpers]::CheckMember($_,"Exception.Response.StatusCode") -and $_.Exception.Response.StatusCode -eq "Forbidden"){ throw ([SuppressedException]::new(("You do not have permission to view the requested resource."), [SuppressedExceptionType]::InvalidOperation)) } elseif ([Helpers]::CheckMember($_,"Exception.Message")){ throw ([SuppressedException]::new(($_.Exception.Message.ToString()), [SuppressedExceptionType]::InvalidOperation)) } else { throw; } } } } } return $outputValues; } } # SIG # Begin signature block # MIIhewYJKoZIhvcNAQcCoIIhbDCCIWgCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDONqRmDzOCBt9r # wNHqimaAzuq3roNcK7XJEV+TuU/cpaCCC28wggTrMIID06ADAgECAhMzAAAD53EW # vSG3L5ZCAAAAAAPnMA0GCSqGSIb3DQEBCwUAMHkxCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xIzAhBgNVBAMTGk1pY3Jvc29mdCBUZXN0aW5nIFBD # QSAyMDEwMB4XDTIwMDMwNDE5NTgzOVoXDTIxMDMwMzE5NTgzOVowfDELMAkGA1UE # BhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc # BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdQ29kZSBTaWdu # IFRlc3QgKERPIE5PVCBUUlVTVCkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK # AoIBAQC0dFU8yYFFisft2syLgnrgoEcOrrzraGs5owrAJ8YWyYuxhhk7UUJP0YAC # wuDAlPQMHrhnEhZsqmD7DfWGzz33gxe7hvcNpHdhItPpgXiVkh3thZrWz4jfHFGc # RMW1zyebGUJ16gN5cYWsI18Pax9tBZW1YZIef2hIQNU5Vr5QhVKZVAbaqZFqJRo+ # 51czrP44ZnofEMr3Z3HBmIS7C97kkFYS/G8JpkufIuDsTchX7dWduHhMbFIem+Zx # nT7mrsps0D5hXV3L9JPe8TFm1T0iwaFy6RWFaWPelibrTryIbWk6Qrv4Lz89WMM6 # XFxlrqQVphAmhns1+rNrr6yacRCtAgMBAAGjggFnMIIBYzATBgNVHSUEDDAKBggr # BgEFBQcDAzAdBgNVHQ4EFgQUseZoPiUpJDttlBAhnIzqzbcXsK4wUAYDVR0RBEkw # R6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNv # MRYwFAYDVQQFEw0yMzAwNzIrNDU4Mzk0MB8GA1UdIwQYMBaAFN3WR4sjFC/YOGhC # oz5tw/CQ9yzQMFMGA1UdHwRMMEowSKBGoESGQmh0dHA6Ly9jcmwubWljcm9zb2Z0 # LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Rlc1BDQV8yMDEwLTA3LTAxLmNybDBX # BggrBgEFBQcBAQRLMEkwRwYIKwYBBQUHMAKGO2h0dHA6Ly93d3cubWljcm9zb2Z0 # LmNvbS9wa2kvY2VydHMvTWljVGVzUENBXzIwMTAtMDctMDEuY3J0MAwGA1UdEwEB # /wQCMAAwDQYJKoZIhvcNAQELBQADggEBAJYdTCu6GLf0F8qu4JuKidCt6hweTHFz # 012VGqDoVNN8REwov3VMjK71y8oL6wgvx29RYYqD2sKn6a/NcKUlHJjttvbXW/Az # NK4FetsfpyURFCRTS8C5hRcGZTIZfiSsJXn0N/yV/pbf/M6N4c0Q//I5f+e5lMch # 0jf6TGVLEHcXgOOH1PcS4Rd9LjAaggJG7VAOrIQaoSfgtsMn/a0CoYXeigizHb4k # sZW2nEC5JSAZ49b3Y1Pjvtr1H6xfMewXwtGCEvTq2btl8in/TV8du5cimL7VmZAa # aggJr0eFOmLCNUgGhH+Ic+sLH7G7vpkdggW9PRQ0wtQm8ofUIYhIn2swggZ8MIIE # ZKADAgECAgphEYRvAAAAAAADMA0GCSqGSIb3DQEBCwUAMIGQMQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTowOAYDVQQDEzFNaWNyb3NvZnQgVGVz # dGluZyBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDEwMB4XDTEwMDcwMTIx # MjMwMVoXDTI1MDcwMTIxMzMwMVoweTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh # c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD # b3Jwb3JhdGlvbjEjMCEGA1UEAxMaTWljcm9zb2Z0IFRlc3RpbmcgUENBIDIwMTAw # ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBvSHVS2YGAJIwORjKy3NC # WbHvmyeo4OhVvSmw+SQfOtHow1mJ7ZG2wegzY/ZaZBniLnwMkIAFOL8cproNai/v # J5er3vbvUPOD59fDRTciPxi1wpYRto0Sg1mLJ1EGVnW5YGoTDtUmPy2WqgXMoYc/ # vk807wxMb8wE1KHmZ80KJzOf46+bb2h8vLQMczSMWoH5h/tUHMVHbOqfV7RZ/c4Z # qXd8h0KftXmUvMt2ktuWl6FfBCQ5/qGV4Z+G417ZXFbfQ5CfyRTq0fWgW6vzCATd # KK8b4qouE6AK7dKZRCr1mUT7K6RP8bthwh0t9SUnAqh475M59F51ge7S4HYMWyPv # AgMBAAGjggHsMIIB6DAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQU3dZHiyMU # L9g4aEKjPm3D8JD3LNAwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0P # BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUowEEfjCIM+u5MZzK # 64V2Z/xltNEwWQYDVR0fBFIwUDBOoEygSoZIaHR0cDovL2NybC5taWNyb3NvZnQu # Y29tL3BraS9jcmwvcHJvZHVjdHMvTWljVGVzUm9vQ2VyQXV0XzIwMTAtMDYtMTcu # Y3JsMF0GCCsGAQUFBwEBBFEwTzBNBggrBgEFBQcwAoZBaHR0cDovL3d3dy5taWNy # b3NvZnQuY29tL3BraS9jZXJ0cy9NaWNUZXNSb29DZXJBdXRfMjAxMC0wNi0xNy5j # cnQwgaAGA1UdIAEB/wSBlTCBkjCBjwYJKwYBBAGCNy4DMIGBMD0GCCsGAQUFBwIB # FjFodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vUEtJL2RvY3MvQ1BTL2RlZmF1bHQu # aHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAFAAbwBsAGkAYwB5AF8A # UwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBAYQU+N90z # e1LCSGuA92ADFrbJLol+vdlYHGPT9ZLS9loEDQjuh7/rmDQ6ZXxQ5MgbKVB5VXsY # OZG1QSbzF3+YlWd8TN1y5B21BM3DSPb6r+3brl50kW2t1JqACtiLbZnmhmh5hkdJ # i8HYUfLQ7xKcP0g1CIJP9CyKil7UJv/HnMXKigTGiBaHjfVtVwG5k8roymrEirpB # DcOMVB0OZiTXxYIHDbM4v7LItZYIISdPs6+LwxwzwdroMdpj42+3dWQBumpRGQAg # qJ9i5UiBQtUM+9vLpKIRnujnWfQxbaIuIt2HRLFpHUYKGOXRlf148o+71dX3YWap # 88+ocaxkM8rkavgDNkcWSe9Dpoq8a3tS2P9BpxewDV+iSzF0JRo9UOZeciaSQDZv # rkQskxJjtdO725L6E5Fu1Ti+lGl6exRCnhPbooxCqHEGLRdiwXkrmLp+huTGAK8z # mfEt0d1JFrrDdu5kqoG3OVT2dN4JVFNpOFvCU/LNiVDCyCIcG0cSRVtDjyNckMhu # 1PcPtberjr1mcL8RkTzvonoH4pIvQk1k4IOLpdxslOj2oigApZjqCBJA3mIEZHln # wRuglg4Er74nSmL6953C0r1Vwl7T0vXnQO8izb+incAb1r6Y+45N5aVXww+PqHJB # RjvhjyBKG+1aDLVM3ixjV9P6OZkOvp4uozGCFWIwghVeAgEBMIGQMHkxCzAJBgNV # BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w # HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xIzAhBgNVBAMTGk1pY3Jvc29m # dCBUZXN0aW5nIFBDQSAyMDEwAhMzAAAD53EWvSG3L5ZCAAAAAAPnMA0GCWCGSAFl # AwQCAQUAoIGuMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcC # AQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCDtHwlL0bXqUbZ01GPf # OSu7mIlpO2X8wupl/klpMjWqGzBCBgorBgEEAYI3AgEMMTQwMqAUgBIATQBpAGMA # cgBvAHMAbwBmAHShGoAYaHR0cDovL3d3dy5taWNyb3NvZnQuY29tMA0GCSqGSIb3 # DQEBAQUABIIBAEnny2z9cuZO/Y8zFJZJSGj3kiTFY3S79dgcoaemYQhW5NamhS0b # 1F7NTtODcQulTZAD8vCl2/Lh2sBqT0GRjr0HpahN1lXq5b852py3nlH3xsxjEmdZ # fvYuUIRqJVlSfxGUSNoSLGG/CSVe/nMqLYav/H4wmOaXhMiowBbbjpDUgS5nO954 # eII43jRQfozd5hvMrPxxZ1EEsAcM0vJtO/DetE70qkf8BBk9P9HBCJ7cinGPZNQP # 7jc/vK3tldfCqWlygbajZlw9TnB1JWIhjXj2eSdeVj6K5aasWGBm7XPIpm9jQ1Sp # fVnnADsOZff4M2kPpXIZxByunhVdLJlqgxWhghLxMIIS7QYKKwYBBAGCNwMDATGC # Et0wghLZBgkqhkiG9w0BBwKgghLKMIISxgIBAzEPMA0GCWCGSAFlAwQCAQUAMIIB # VQYLKoZIhvcNAQkQAQSgggFEBIIBQDCCATwCAQEGCisGAQQBhFkKAwEwMTANBglg # hkgBZQMEAgEFAAQgFvXafg5jRrfqdoGIgQPws4yIDkkFWXD6elOlERkMBR8CBl+7 # 6kWnXxgTMjAyMDExMjcxMTQ3NTYuODkyWjAEgAIB9KCB1KSB0TCBzjELMAkGA1UE # BhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc # BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEpMCcGA1UECxMgTWljcm9zb2Z0 # IE9wZXJhdGlvbnMgUHVlcnRvIFJpY28xJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNO # OjMyQkQtRTNENS0zQjFEMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT # ZXJ2aWNloIIORDCCBPUwggPdoAMCAQICEzMAAAEuqNIZB5P0a+gAAAAAAS4wDQYJ # KoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcNMTkx # MjE5MDExNTA1WhcNMjEwMzE3MDExNTA1WjCBzjELMAkGA1UEBhMCVVMxEzARBgNV # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv # c29mdCBDb3Jwb3JhdGlvbjEpMCcGA1UECxMgTWljcm9zb2Z0IE9wZXJhdGlvbnMg # UHVlcnRvIFJpY28xJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOjMyQkQtRTNENS0z # QjFEMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNlMIIBIjAN # BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArtNMolFTX3osUiMxD2r9SOk+HPje # blGceAcBWnZgaeLvj6W2xig7WdnnytNsmEJDwZgfLwHh16+Buqpg9A1TeL52ukS0 # Rw0tuwyvgwSrdIz687drpAwV3WUNHLshAs8k0sq9wzr023uS7VjIzk2c80NxEmyd # Rv/xjH/NxblxaOeiPyz19D3cE9/8nviozWqXYJ3NBXvg8GKww/+2mkCdK43Cjwjv # 65avq9+kHKdJYO8l4wOtyxrrZeybsNsHU2dKw8YAa3dHOUFX0pWJyLN7hTd+jhyF # 2gHb5Au7Xs9oSaPTuqrvTQIblcmSkRg6N500WIHICkXthG9Cs5lDTtBiIwIDAQAB # o4IBGzCCARcwHQYDVR0OBBYEFIaaiSZOC4k3u6pJNDVSEvC3VE5sMB8GA1UdIwQY # MBaAFNVjOlyKMZDzQ3t8RhvFM2hahW1VMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6 # Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1RpbVN0YVBD # QV8yMDEwLTA3LTAxLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0 # dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljVGltU3RhUENBXzIw # MTAtMDctMDEuY3J0MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwgw # DQYJKoZIhvcNAQELBQADggEBAI3gBGqnMK6602pjadYkMNePfmJqJ2WC0n9uyliw # Bfxq0mXX0h9QojNO65JVTdxpdnr9i8wxgxxuw1r/gnby6zbcro9ZkCWMiPQbxC3A # MyVAeOsqetyvgUEDPpmq8HpKs3f9ZtvRBIr86XGxTSZ8PvPztHYkziDAom8foQgu # 4AS2PBQZIHU0qbdPCubnV8IPSPG9bHNpRLZ628w+uHwM2uscskFHdQe+D81dLYjN # 1CfbTGOOxbQFQCJN/40JGnFS+7+PzQ1vX76+d6OJt+lAnYiVeIl0iL4dv44vdc6v # wxoMNJg5pEUAh9yirdU+LgGS9ILxAau+GMBlp+QTtHovkUkwggZxMIIEWaADAgEC # AgphCYEqAAAAAAACMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEG # A1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWlj # cm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0 # aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0xMDA3MDEyMTM2NTVaFw0yNTA3MDEy # MTQ2NTVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD # VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAk # BgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMIIBIjANBgkqhkiG # 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqR0NvHcRijog7PwTl/X6f2mUa3RUENWlCgCC # hfvtfGhLLF/Fw+Vhwna3PmYrW/AVUycEMR9BGxqVHc4JE458YTBZsTBED/FgiIRU # QwzXTbg4CLNC3ZOs1nMwVyaCo0UN0Or1R4HNvyRgMlhgRvJYR4YyhB50YWeRX4FU # sc+TTJLBxKZd0WETbijGGvmGgLvfYfxGwScdJGcSchohiq9LZIlQYrFd/XcfPfBX # day9ikJNQFHRD5wGPmd/9WbAA5ZEfu/QS/1u5ZrKsajyeioKMfDaTgaRtogINeh4 # HLDpmc085y9Euqf03GS9pAHBIAmTeM38vMDJRF1eFpwBBU8iTQIDAQABo4IB5jCC # AeIwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFNVjOlyKMZDzQ3t8RhvFM2ha # hW1VMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNV # HRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fOmhjEMFYG # A1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3Js # L3Byb2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggrBgEFBQcB # AQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kv # Y2VydHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MIGgBgNVHSABAf8EgZUw # gZIwgY8GCSsGAQQBgjcuAzCBgTA9BggrBgEFBQcCARYxaHR0cDovL3d3dy5taWNy # b3NvZnQuY29tL1BLSS9kb2NzL0NQUy9kZWZhdWx0Lmh0bTBABggrBgEFBQcCAjA0 # HjIgHQBMAGUAZwBhAGwAXwBQAG8AbABpAGMAeQBfAFMAdABhAHQAZQBtAGUAbgB0 # AC4gHTANBgkqhkiG9w0BAQsFAAOCAgEAB+aIUQ3ixuCYP4FxAz2do6Ehb7Prpsz1 # Mb7PBeKp/vpXbRkws8LFZslq3/Xn8Hi9x6ieJeP5vO1rVFcIK1GCRBL7uVOMzPRg # Eop2zEBAQZvcXBf/XPleFzWYJFZLdO9CEMivv3/Gf/I3fVo/HPKZeUqRUgCvOA8X # 9S95gWXZqbVr5MfO9sp6AG9LMEQkIjzP7QOllo9ZKby2/QThcJ8ySif9Va8v/rbl # jjO7Yl+a21dA6fHOmWaQjP9qYn/dxUoLkSbiOewZSnFjnXshbcOco6I8+n99lmqQ # eKZt0uGc+R38ONiU9MalCpaGpL2eGq4EQoO4tYCbIjggtSXlZOz39L9+Y1klD3ou # OVd2onGqBooPiRa6YacRy5rYDkeagMXQzafQ732D8OE7cQnfXXSYIghh2rBQHm+9 # 8eEA3+cxB6STOvdlR3jo+KhIq/fecn5ha293qYHLpwmsObvsxsvYgrRyzR30uIUB # HoD7G4kqVDmyW9rIDVWZeodzOwjmmC3qjeAzLhIp9cAvVCch98isTtoouLGp25ay # p0Kiyc8ZQU3ghvkqmqMRZjDTu3QyS99je/WZii8bxyGvWbWu3EQ8l1Bx16HSxVXj # ad5XwdHeMMD9zOZN+w2/XU/pnR4ZOC+8z1gFLu8NoFA12u8JJxzVs341Hgi62jbb # 01+P3nSISRKhggLSMIICOwIBATCB/KGB1KSB0TCBzjELMAkGA1UEBhMCVVMxEzAR # BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p # Y3Jvc29mdCBDb3Jwb3JhdGlvbjEpMCcGA1UECxMgTWljcm9zb2Z0IE9wZXJhdGlv # bnMgUHVlcnRvIFJpY28xJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNOOjMyQkQtRTNE # NS0zQjFEMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNloiMK # AQEwBwYFKw4DAhoDFQD7X8I3oEgt5TXIMaj5vpaSkuhCm6CBgzCBgKR+MHwxCzAJ # BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jv # c29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBBQUAAgUA42sF8DAi # GA8yMDIwMTEyNzA4NTgyNFoYDzIwMjAxMTI4MDg1ODI0WjB3MD0GCisGAQQBhFkK # BAExLzAtMAoCBQDjawXwAgEAMAoCAQACAh7yAgH/MAcCAQACAhHoMAoCBQDjbFdw # AgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAIAgEAAgMHoSCh # CjAIAgEAAgMBhqAwDQYJKoZIhvcNAQEFBQADgYEAXBmT+V6uUY0Q8biUAa29Lamm # 3VV/2JxGk0y35fyxdN3rAXUdbDZnW8xbH212dDfQpdMTD4e7ovXHOMoa7FCYLZ2m # G6Fcx6EcXetuBaCP++76S389Ovew9VgeIOoQiCigY8Fm1I36PF+QW27dYmuwxRx6 # F78Z2bVKauEvlzUrscExggMNMIIDCQIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEG # A1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWlj # cm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFt # cCBQQ0EgMjAxMAITMwAAAS6o0hkHk/Rr6AAAAAABLjANBglghkgBZQMEAgEFAKCC # AUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCDl # 6dLwkQDTgkbE4dbCECx872XBwBmgiB5Zp5YVCa/61TCB+gYLKoZIhvcNAQkQAi8x # geowgecwgeQwgb0EINr+zc7xiFaKqlU3SRN4r7HabRECHXsmlHoOIWhMgskpMIGY # MIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV # BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQG # A1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAEuqNIZB5P0 # a+gAAAAAAS4wIgQgsTmaL5CxitPCfsYHjaB6MUzafhKj5W7h/4eb+aXeogkwDQYJ # KoZIhvcNAQELBQAEggEAC3a/7UNmJe2N1v2+sgdFd2rsGpRLXZDp51YPsEoRRwc3 # gnpQYgYxWMQ3WT8foBNnjS8ht5WagOF7HP2i5P4ZHAGqB2luy1QuqfIfuWMpOp59 # on/qkBWRWkZSHscaqhJmYYMz9SFYqVXNxoQSPa2DjTplRYB7P2e6mtxDHpNtFEOX # RLfUI1YBcBthXklNPNmQWMZ3UHnD+ynrULXPg/hIdVt3ddfKAmHLiEhUQ56HI0Os # /1usiQyZgflsG/Nc4qmmrI42ifALiahT8Nnzk0V9E41aqCsmsHLIe0RPe6/vXexK # vEE/OyJZ4LzqmUNx4d11qs77vuhqIX1iocP5nV8WeA== # SIG # End signature block |