Framework/Helpers/AIOrgTelemetryHelper.ps1
|
Set-StrictMode -Version Latest class AIOrgTelemetryHelper { static hidden [string[]] $ParamsToMask = @("OMSSharedKey"); static hidden [Microsoft.ApplicationInsights.TelemetryClient] $OrgTelemetryClient; static hidden [Microsoft.ApplicationInsights.TelemetryClient] $UsageTelemetryClient; static [PSObject] $CommonProperties; static AIOrgTelemetryHelper() { [AIOrgTelemetryHelper]::OrgTelemetryClient = [Microsoft.ApplicationInsights.TelemetryClient]::new() } static [void] TrackEvent([string] $Name) { [AIOrgTelemetryHelper]::TrackEvent($Name, $null, $null); } static [void] TrackEventWithOnlyProperties([string] $Name, [hashtable] $Properties) { [AIOrgTelemetryHelper]::TrackEvent($Name, $Properties, $null); } static [void] TrackEventWithOnlyMetrics([string] $Name, [hashtable] $Metrics) { [AIOrgTelemetryHelper]::TrackEvent($Name, $null, $Metrics); } static [void] TrackEvent([string] $Name, [hashtable] $Properties, [hashtable] $Metrics) { if (![RemoteReportHelper]::IsAIOrgTelemetryEnabled()) { return; }; [AIOrgTelemetryHelper]::TrackEventInternal($Name, $Properties, $Metrics); [AIOrgTelemetryHelper]::OrgTelemetryClient.Flush(); } static [void] TrackEvents([System.Collections.ArrayList] $events) { #if (![RemoteReportHelper]::IsAIOrgTelemetryEnabled()) { return; }; #foreach ($item in $events) { # [AIOrgTelemetryHelper]::TrackEventInternal($item.Name, $item.Properties, $item.Metrics); #} [AIOrgTelemetryHelper]::PublishEvent($events,"AIOrg"); #[AIOrgTelemetryHelper]::OrgTelemetryClient.Flush(); } static [void] TrackCommandExecution([string] $Name, [hashtable] $Properties, [hashtable] $Metrics, [System.Management.Automation.InvocationInfo] $invocationContext) { if (![RemoteReportHelper]::IsAIOrgTelemetryEnabled()) { return; }; $Properties = [AIOrgTelemetryHelper]::AttachInvocationInfo($Properties, $invocationContext); [AIOrgTelemetryHelper]::TrackEventInternal($Name, $Properties, $Metrics); [AIOrgTelemetryHelper]::OrgTelemetryClient.Flush(); } static [void] TrackException([System.Management.Automation.ErrorRecord] $ErrorRecord, [System.Management.Automation.InvocationInfo] $InvocationContext) { [AIOrgTelemetryHelper]::TrackException($ErrorRecord, $null, $null, $InvocationContext); } static [void] TrackExceptionWithOnlyProperties([System.Management.Automation.ErrorRecord] $ErrorRecord, [hashtable] $Properties, [System.Management.Automation.InvocationInfo] $InvocationContext) { [AIOrgTelemetryHelper]::TrackException($ErrorRecord, $Properties, $null, $InvocationContext); } static [void] TrackExceptionWithOnlyMetrics([System.Management.Automation.ErrorRecord] $ErrorRecord, [hashtable] $Metrics, [System.Management.Automation.InvocationInfo] $InvocationContext) { [AIOrgTelemetryHelper]::TrackException($ErrorRecord, $null, $Metrics, $InvocationContext); } static [void] TrackException([System.Management.Automation.ErrorRecord] $ErrorRecord, [hashtable] $Properties, [hashtable] $Metrics, [System.Management.Automation.InvocationInfo] $InvocationContext) { try { if (![RemoteReportHelper]::IsAIOrgTelemetryEnabled()) { return; }; $Properties = [AIOrgTelemetryHelper]::AttachInvocationInfo($Properties, $InvocationContext); $Properties = [AIOrgTelemetryHelper]::AttachCommonProperties($Properties); $Metrics = [AIOrgTelemetryHelper]::AttachCommonMetrics($Metrics); $ex = [Microsoft.ApplicationInsights.DataContracts.ExceptionTelemetry]::new() $ex.Exception = $ErrorRecord.Exception try{ $ex.Properties.Add("ScriptStackTrace", $ErrorRecord.ScriptStackTrace) } catch{ # Eat the current exception which typically happens when the property already exist in the object and try to add the same property again # No need to break execution } $Properties.Keys | ForEach-Object { try{ $ex.Properties.Add($_, $Properties[$_].ToString()); } catch{ # Eat the current exception which typically happens when the property already exist in the object and try to add the same property again # No need to break execution } } $Metrics.Keys | ForEach-Object { try{ $ex.Metrics.Add($_, $Metrics[$_]); } catch{ # Eat the current exception which typically happens when the property already exist in the object and try to add the same property again # No need to break execution } } [AIOrgTelemetryHelper]::OrgTelemetryClient.InstrumentationKey = [RemoteReportHelper]::GetAIOrgTelemetryKey(); [AIOrgTelemetryHelper]::OrgTelemetryClient.TrackException($ex); [AIOrgTelemetryHelper]::OrgTelemetryClient.Flush(); } catch{ # Eat the current exception which typically happens when network or other API issue while sending telemetry events # No need to break execution } } hidden static [void] TrackEventInternal([string] $Name, [hashtable] $Properties, [hashtable] $Metrics) { $Properties = [AIOrgTelemetryHelper]::AttachCommonProperties($Properties); $Metrics = [AIOrgTelemetryHelper]::AttachCommonMetrics($Metrics); try { $event = [Microsoft.ApplicationInsights.DataContracts.EventTelemetry]::new() $event.Name = $Name $Properties.Keys | ForEach-Object { try { $event.Properties[$_] = $Properties[$_].ToString(); } catch { $_ # Eat the current exception which typically happens when the property already exist in the object and try to add the same property again # No need to break execution } } $Metrics.Keys | ForEach-Object { try { $event.Metrics[$_] = $Metrics[$_].ToString(); } catch { # Eat the current exception which typically happens when the property already exist in the object and try to add the same property again # No need to break execution } } [AIOrgTelemetryHelper]::OrgTelemetryClient.InstrumentationKey = [RemoteReportHelper]::GetAIOrgTelemetryKey(); [AIOrgTelemetryHelper]::OrgTelemetryClient.TrackEvent($event); } catch{ # Eat the current exception which typically happens when network or other API issue while sending telemetry events # No need to break execution } } hidden static [hashtable] AttachCommonProperties([hashtable] $Properties) { if ($null -eq $Properties) { $Properties = @{} } else { $Properties = $Properties.Clone() } try { $NA = "NA"; try { $Properties.Add("ScanSource", [RemoteReportHelper]::GetScanSource()); } catch { # Eat the current exception which typically happens when the property already exist in the object and try to add the same property again # No need to break execution } try { $Properties.Add("PowerShellVersion", (Get-Variable -Name PSVersionTable).Value.PSVersion.Tostring()); } catch { # Eat the current exception which typically happens when the property already exist in the object and try to add the same property again # No need to break execution } try { $module = Get-Module 'AzSK*' | Select-Object -First 1 $Properties.Add("ScannerModuleName", $module.Name); $Properties.Add("ScannerVersion", $module.Version.ToString()); } catch { # Eat the current exception which typically happens when the property already exist in the object and try to add the same property again # No need to break execution } try { $azureContext = [ContextHelper]::GetCurrentContext() try { $Properties.Add([TelemetryKeys]::SubscriptionId, $azureContext.Subscription.Id) } catch { # Eat the current exception which typically happens when the property already exist in the object and try to add the same property again # No need to break execution } try { $Properties.Add([TelemetryKeys]::SubscriptionName, $azureContext.Subscription.Name) } catch { # Eat the current exception which typically happens when the property already exist in the object and try to add the same property again # No need to break execution } try { $Properties.Add("AzureEnv", $azureContext.Environment.Name) } catch { # Eat the current exception which typically happens when the property already exist in the object and try to add the same property again # No need to break execution } try { $Properties.Add("TenantId", $azureContext.Tenant.Id) } catch { # Eat the current exception which typically happens when the property already exist in the object and try to add the same property again # No need to break execution } try { $Properties.Add("AccountId", $azureContext.Account.Id) } catch { # Eat the current exception which typically happens when the property already exist in the object and try to add the same property again # No need to break execution } try { if ($Properties.ContainsKey("RunIdentifier")) { $actualRunId = $Properties["RunIdentifier"] $Properties["UniqueRunIdentifier"] = [RemoteReportHelper]::Mask($azureContext.Account.Id + '##' + $actualRunId.ToString()) } } catch { # Eat the current exception which typically happens when the property already exist in the object and try to add the same property again # No need to break execution } try { $Properties.Add("AccountType", $azureContext.Account.Type); } catch { # Eat the current exception which typically happens when the property already exist in the object and try to add the same property again # No need to break execution } } catch { # Eat the current exception which typically happens when the property already exist in the object and try to add the same property again # No need to break execution } } catch { # Eat the current exception which typically happens when the property already exist in the object and try to add the same property again # No need to break execution } return $Properties; } hidden static [hashtable] AttachCommonMetrics([hashtable] $Metrics) { if ($null -eq $Metrics) { $Metrics = @{} } else { $Metrics = $Metrics.Clone() } return $Metrics; } hidden static [hashtable] AttachInvocationInfo([hashtable] $Properties, [System.Management.Automation.InvocationInfo] $invocationContext) { if ($null -eq $Properties) { $Properties = @{} } else { $Properties = $Properties.Clone() } if ($null -eq $invocationContext) { return $Properties}; $Properties.Add("Command", $invocationContext.MyCommand.Name) $params = @{} $invocationContext.BoundParameters.Keys | ForEach-Object { $value = "MASKED" if (![AIOrgTelemetryHelper]::ParamsToMask.Contains($_)) { $value = $invocationContext.BoundParameters[$_].ToString() } $Properties.Add("Param" + $_, $value) $params.Add("$_", $value) } $Properties.Add("Params", [JsonHelper]::ConvertToJsonCustomCompressed($params)) $loadedModules = Get-Module | ForEach-Object { $_.Name + "=" + $_.Version.ToString()} $Properties.Add("LoadedModules" , ($loadedModules -join ';')) return $Properties; } static [void] PublishEvent([string] $EventName, [hashtable] $Properties, [hashtable] $Metrics) { try { #return if telemetry key is empty $telemetryKey= [RemoteReportHelper]::GetAIOrgTelemetryKey() if ([string]::IsNullOrWhiteSpace($telemetryKey)) { return; }; $eventObj = [AIOrgTelemetryHelper]::GetEventBaseObject($EventName) $eventObj=[AIOrgTelemetryHelper]::SetCommonProperties($eventObj) if ($null -ne $Properties) { $Properties.Keys | ForEach-Object { try { if (!$eventObj.data.baseData.properties.ContainsKey($_)) { $eventObj.data.baseData.properties.Add($_ , $Properties[$_].ToString()) } } catch { # Eat the current exception which typically happens when the property already exist in the object and try to add the same property again # No need to break execution } } } if ($null -ne $Metrics) { $Metrics.Keys | ForEach-Object { try { $metric = $Metrics[$_] -as [double] if (!$eventObj.data.baseData.measurements.ContainsKey($_) -and $null -ne $metric) { $eventObj.data.baseData.measurements.Add($_ , $Metrics[$_]) } } catch { # Eat the current exception which typically happens when the property already exist in the object and try to add the same property again # No need to break execution } } } $eventJson = ConvertTo-Json $eventObj -Depth 100 -Compress $uri = [WebRequestHelper]::GetApplicationInsightsEndPoint() Invoke-WebRequest -Uri $uri ` -Method Post ` -ContentType "application/x-json-stream" ` -Body $eventJson ` -UseBasicParsing | Out-Null } catch { # Eat the current exception which typically happens when network or other API issue while sending telemetry events # No need to break execution } } static [void] PublishARMCheckerEvent([string] $EventName, [hashtable] $Properties, [hashtable] $Metrics) { try { $armcheckerscantelemetryEvents = [System.Collections.ArrayList]::new() $telemetryEvent = "" | Select-Object Name, Properties, Metrics $telemetryEvent.Name = $EventName $telemetryEvent.Properties = $Properties $telemetryEvent.Metrics = $Metrics $armcheckerscantelemetryEvents.Add($telemetryEvent) [AIOrgTelemetryHelper]::PublishARMCheckerEvent($armcheckerscantelemetryEvents); } catch { # Left blank intentionally # Error while sending events to telemetry. No need to break the execution. } } static [void] PublishARMCheckerEvent([System.Collections.ArrayList] $armcheckerscantelemetryEvents) { try { #Attach Common Properties to each EventObject $armcheckerscantelemetryEvents | ForEach-Object -Begin{ $module = Get-Module 'AzSK*' | Select-Object -First 1 } -Process { $_.Properties.Add("ScannerModuleName", $module.Name); $_.Properties.Add("ScannerVersion", $module.Version.ToString()); $_.Properties.Add("Command","Get-AzSKARMChecker") } -End {} [AIOrgTelemetryHelper]::PublishEvent($armcheckerscantelemetryEvents,"Usage"); } catch{ # Left blank intentionally # Error while sending events to telemetry. No need to break the execution. } } static [PSObject] GetEventBaseObject([string] $EventName) { $telemetryKey= [RemoteReportHelper]::GetAIOrgTelemetryKey() $eventObj = "" | Select-Object data, iKey, name, tags, time $eventObj.iKey = $telemetryKey $eventObj.name = "Microsoft.ApplicationInsights." + $telemetryKey.Replace("-", "") + ".Event" $eventObj.time = [datetime]::UtcNow.ToString("o") $eventObj.tags = "" | Select-Object ai.internal.sdkVersion $eventObj.tags.'ai.internal.sdkVersion' = "dotnet: 2.1.0.26048" $eventObj.data = "" | Select-Object baseData, baseType $eventObj.data.baseType = "EventData" $eventObj.data.baseData = "" | Select-Object ver, name, measurements, properties $eventObj.data.baseData.ver = 2 $eventObj.data.baseData.name = $EventName $eventObj.data.baseData.measurements = New-Object 'system.collections.generic.dictionary[string,double]' $eventObj.data.baseData.properties = New-Object 'system.collections.generic.dictionary[string,string]' return $eventObj; } #Telemetry functions -- start here static [PSObject] SetCommonProperties([psobject] $EventObj) { $notAvailable = "NA" if([AIOrgTelemetryHelper]::CommonProperties) { try{ $EventObj.data.baseData.properties.Add("SubscriptionId",[AIOrgTelemetryHelper]::CommonProperties.SubscriptionId) $EventObj.data.baseData.properties.Add("SubscriptionName",[AIOrgTelemetryHelper]::CommonProperties.SubscriptionName) $azureContext = [ContextHelper]::GetCurrentContext() $EventObj.data.baseData.properties.Add("TenantId", $azureContext.Tenant.Id) $EventObj.data.baseData.properties.Add("AccountId", $azureContext.Account.Id) } catch{ # Eat the current exception which typically happens to avoid any break in event push # No need to break execution } } return $EventObj } static [PSObject] GetUsageEventBaseObject([string] $EventName,[string] $type) { $eventObj = "" | Select-Object data, iKey, name, tags, time if($type -eq "Usage") { $eventObj.iKey = [Constants]::UsageTelemetryKey } else { $eventObj.iKey = [RemoteReportHelper]::GetAIOrgTelemetryKey() } $eventObj.name = $EventName $eventObj.time = [datetime]::UtcNow.ToString("o") $eventObj.tags = "" | Select-Object ai.internal.sdkVersion $eventObj.tags.'ai.internal.sdkVersion' = "dotnet: 2.1.0.26048" $eventObj.data = "" | Select-Object baseData, baseType $eventObj.data.baseType = "EventData" $eventObj.data.baseData = "" | Select-Object ver, name, measurements, properties $eventObj.data.baseData.ver = 2 $eventObj.data.baseData.name = $EventName $eventObj.data.baseData.measurements = New-Object 'system.collections.generic.dictionary[string,double]' $eventObj.data.baseData.properties = New-Object 'system.collections.generic.dictionary[string,string]' return $eventObj; } static [void] PublishEvent([System.Collections.ArrayList] $servicescantelemetryEvents,[string] $type) { try { $eventlist = [System.Collections.ArrayList]::new() $servicescantelemetryEvents | ForEach-Object { $eventObj = [AIOrgTelemetryHelper]::GetUsageEventBaseObject($_.Name,$type) #SetCommonProperties -EventObj $eventObj $currenteventobj = $_ if ($null -ne $currenteventobj.Properties) { $currenteventobj.Properties.Keys | ForEach-Object { try { if (!$eventObj.data.baseData.properties.ContainsKey($_)) { $eventObj.data.baseData.properties.Add($_ , $currenteventobj.Properties[$_].ToString()) } } catch { # Left blank intentionally # Error while sending CA events to telemetry. No need to break the execution. } } } if ($null -ne $currenteventobj.Metrics) { $currenteventobj.Metrics.Keys | ForEach-Object { try { $metric = $currenteventobj.Metrics[$_] -as [double] if (!$eventObj.data.baseData.measurements.ContainsKey($_) -and $null -ne $metric) { $eventObj.data.baseData.measurements.Add($_ , $currenteventobj.Metrics[$_]) } } catch { # Left blank intentionally # Error while sending CA events to telemetry. No need to break the execution. } } } $eventlist.Add($eventObj) } $eventJson = ConvertTo-Json $eventlist -Depth 100 -Compress if($type -eq "Usage") { Invoke-WebRequest -Uri "https://dc.services.visualstudio.com/v2/track" ` -Method Post ` -ContentType "application/x-json-stream" ` -Body $eventJson ` -UseBasicParsing | Out-Null } else { $uri = [WebRequestHelper]::GetApplicationInsightsEndPoint() Invoke-WebRequest -Uri $uri ` -Method Post ` -ContentType "application/x-json-stream" ` -Body $eventJson ` -UseBasicParsing | Out-Null } } catch { # Left blank intentionally # Error while sending CA events to telemetry. No need to break the execution. } } } # SIG # Begin signature block # MIIjiQYJKoZIhvcNAQcCoIIjejCCI3YCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBpldoOXHqaSohu # 58wy6LiG0oPs4NP0X/rhM9MD2mlwt6CCDYUwggYDMIID66ADAgECAhMzAAABiK9S # 1rmSbej5AAAAAAGIMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ4WhcNMjEwMzAzMTgzOTQ4WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQCSCNryE+Cewy2m4t/a74wZ7C9YTwv1PyC4BvM/kSWPNs8n0RTe+FvYfU+E9uf0 # t7nYlAzHjK+plif2BhD+NgdhIUQ8sVwWO39tjvQRHjP2//vSvIfmmkRoML1Ihnjs # 9kQiZQzYRDYYRp9xSQYmRwQjk5hl8/U7RgOiQDitVHaU7BT1MI92lfZRuIIDDYBd # vXtbclYJMVOwqZtv0O9zQCret6R+fRSGaDNfEEpcILL+D7RV3M4uaJE4Ta6KAOdv # V+MVaJp1YXFTZPKtpjHO6d9pHQPZiG7NdC6QbnRGmsa48uNQrb6AfmLKDI1Lp31W # MogTaX5tZf+CZT9PSuvjOCLNAgMBAAGjggGCMIIBfjAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUj9RJL9zNrPcL10RZdMQIXZN7MG8w # VAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9wZXJh # dGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMwMDEyKzQ1ODM4NjAfBgNVHSMEGDAW # gBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8v # d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIw # MTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDov # L3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDEx # XzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIB # ACnXo8hjp7FeT+H6iQlV3CcGnkSbFvIpKYafgzYCFo3UHY1VHYJVb5jHEO8oG26Q # qBELmak6MTI+ra3WKMTGhE1sEIlowTcp4IAs8a5wpCh6Vf4Z/bAtIppP3p3gXk2X # 8UXTc+WxjQYsDkFiSzo/OBa5hkdW1g4EpO43l9mjToBdqEPtIXsZ7Hi1/6y4gK0P # mMiwG8LMpSn0n/oSHGjrUNBgHJPxgs63Slf58QGBznuXiRaXmfTUDdrvhRocdxIM # i8nXQwWACMiQzJSRzBP5S2wUq7nMAqjaTbeXhJqD2SFVHdUYlKruvtPSwbnqSRWT # GI8s4FEXt+TL3w5JnwVZmZkUFoioQDMMjFyaKurdJ6pnzbr1h6QW0R97fWc8xEIz # LIOiU2rjwWAtlQqFO8KNiykjYGyEf5LyAJKAO+rJd9fsYR+VBauIEQoYmjnUbTXM # SY2Lf5KMluWlDOGVh8q6XjmBccpaT+8tCfxpaVYPi1ncnwTwaPQvVq8RjWDRB7Pa # 8ruHgj2HJFi69+hcq7mWx5nTUtzzFa7RSZfE5a1a5AuBmGNRr7f8cNfa01+tiWjV # Kk1a+gJUBSP0sIxecFbVSXTZ7bqeal45XSDIisZBkWb+83TbXdTGMDSUFKTAdtC+ # r35GfsN8QVy59Hb5ZYzAXczhgRmk7NyE6jD0Ym5TKiW5MIIHejCCBWKgAwIBAgIK # YQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNV # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv # c29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlm # aWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEw # OTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYD # VQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG # 9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+la # UKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc # 6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4D # dato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+ # lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nk # kDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6 # A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmd # X4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL # 5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zd # sGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3 # T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS # 4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRI # bmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAL # BgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBD # uRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jv # c29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf # MDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFf # MDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEF # BQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1h # cnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkA # YwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn # 8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7 # v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0b # pdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/ # KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvy # CInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBp # mLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJi # hsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYb # BL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbS # oqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sL # gOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtX # cVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCFVowghVWAgEBMIGVMH4x # CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt # b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01p # Y3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAAGIr1LWuZJt6PkAAAAA # AYgwDQYJYIZIAWUDBAIBBQCggbAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw # HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIK/r # NZUDb6zK2zMLro47sRu3D1hIEDbhXYXoYcGdo1gMMEQGCisGAQQBgjcCAQwxNjA0 # oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEcgBpodHRwczovL3d3dy5taWNyb3NvZnQu # Y29tIDANBgkqhkiG9w0BAQEFAASCAQBAQ8pz3BaiGLGsn8pinjl/U1RT0Y55VBUa # mJRhcTAIFqBInikvwCiwtTCXel3I9+Bd+2p1Q+x9rLRsqk9ijiAarc6jgw53/vrA # taIHCY9fbv2Q2C3b2i5yl1O8wvOpygoQ+zqxbg3ZaSVthJEPBBL2ykkzHFt/lWLP # ECNHX/cRQEzqrgfoBLUvXbRWl6J0kLajCbhj+go+vWiyYXLPhEk2LLGwSE3RCGux # 2sTSJP4RinGmOGam+E4Put3vo3g9DbtlGPCQRQYY/qcRc6HwMZNHPENdE8/GLPO8 # HPHrVO0ij2lSyoTTm+lmoBANFtOMbrOyDFB1agmO6byYMLASaY3UoYIS4jCCEt4G # CisGAQQBgjcDAwExghLOMIISygYJKoZIhvcNAQcCoIISuzCCErcCAQMxDzANBglg # hkgBZQMEAgEFADCCAVEGCyqGSIb3DQEJEAEEoIIBQASCATwwggE4AgEBBgorBgEE # AYRZCgMBMDEwDQYJYIZIAWUDBAIBBQAEIP/HplGnqdkQPTawa90IjP8UHkM6+2CH # JCnqQj4l1JNdAgZf2Me2vxsYEzIwMjEwMTE0MDgzMjQ3LjUwNlowBIACAfSggdCk # gc0wgcoxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH # EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNV # BAsTHE1pY3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMxJjAkBgNVBAsTHVRoYWxl # cyBUU1MgRVNOOkQ2QkQtRTNFNy0xNjg1MSUwIwYDVQQDExxNaWNyb3NvZnQgVGlt # ZS1TdGFtcCBTZXJ2aWNloIIOOTCCBPEwggPZoAMCAQICEzMAAAFQWKLUp5sLMOsA # AAAAAVAwDQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldh # c2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBD # b3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIw # MTAwHhcNMjAxMTEyMTgyNjAzWhcNMjIwMjExMTgyNjAzWjCByjELMAkGA1UEBhMC # VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV # BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFt # ZXJpY2EgT3BlcmF0aW9uczEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046RDZCRC1F # M0U3LTE2ODUxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2Uw # ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDnen+UeypZwycbVpoN8zNS # AqnZl40+RjRTx17gsPvVYNxvPe6PzruS/J5X2mON6BRt+XaJATJJvkCgHvViJqrU # 7Q39T0qTf02fOTTzkBR1zhB2ihL3XSaEpRE/L2wSa7vgL8jhPFi0dZ8nnqcj96bV # LaRvPs7ANXeDF3xpZNgUSKL2EegBcmRUse+92uWk/NYsj8Y3ECv2qPnSCNESqdQ9 # 7JS4K3R5PzHSCG2xYvRRLp+b90FVI2JCQr1IAj92UNke2wKHbQs5VdyJE+/vgg6t # yZdaxW7AVojIq5KcfM3+QahNKpsdOHm37IwYmD1LfTsb0tVhXLjbh7o4T6cCKiWb # AgMBAAGjggEbMIIBFzAdBgNVHQ4EFgQUglUZHxlF261kL0PBAEM7t+ufRX4wHwYD # VR0jBBgwFoAU1WM6XIoxkPNDe3xGG8UzaFqFbVUwVgYDVR0fBE8wTTBLoEmgR4ZF # aHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljVGlt # U3RhUENBXzIwMTAtMDctMDEuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggrBgEFBQcw # AoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNUaW1TdGFQ # Q0FfMjAxMC0wNy0wMS5jcnQwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEF # BQcDCDANBgkqhkiG9w0BAQsFAAOCAQEAUT9odHKO/uPj08AeL5P2HixMOqHK3oPk # 9JAdmlgf2Xt8xF7Y9BHiFQNWYMKd/HI2ryYOu3SAAs3txZaRpalvY0R16WWIQzC9 # G9oqSD7QNN0RMxsiiCMM65/nq9xSPIrmYh6aTXFgIMuh4GLNk7gMQFybUbg2ZlLZ # sn9r5RzxX/x8aK17ggEWKmiij1lgb/6AE+bAPUuEyy50ua6U9Zs0+bi8/HvnZs6P # iMwGhtXz/sRrZaAYjbLvaCXOk+DbRvHBoYHQQm35QrPUIfiNcw30giIMRy7xYHji # ml/IxakMFUJ56mLE3SvnbSGxaKwppPlkIsw5HhemdSGHs5SlrQTbXjCCBnEwggRZ # oAMCAQICCmEJgSoAAAAAAAIwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVT # MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290 # IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDEwMB4XDTEwMDcwMTIxMzY1NVoXDTI1 # MDcwMTIxNDY1NVowfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwggEiMA0G # CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCpHQ28dxGKOiDs/BOX9fp/aZRrdFQQ # 1aUKAIKF++18aEssX8XD5WHCdrc+Zitb8BVTJwQxH0EbGpUdzgkTjnxhMFmxMEQP # 8WCIhFRDDNdNuDgIs0Ldk6zWczBXJoKjRQ3Q6vVHgc2/JGAyWGBG8lhHhjKEHnRh # Z5FfgVSxz5NMksHEpl3RYRNuKMYa+YaAu99h/EbBJx0kZxJyGiGKr0tkiVBisV39 # dx898Fd1rL2KQk1AUdEPnAY+Z3/1ZsADlkR+79BL/W7lmsqxqPJ6Kgox8NpOBpG2 # iAg16HgcsOmZzTznL0S6p/TcZL2kAcEgCZN4zfy8wMlEXV4WnAEFTyJNAgMBAAGj # ggHmMIIB4jAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQU1WM6XIoxkPNDe3xG # G8UzaFqFbVUwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGG # MA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9lJBb186a # GMQwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3Br # aS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoGCCsG # AQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29t # L3BraS9jZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcnQwgaAGA1UdIAEB # /wSBlTCBkjCBjwYJKwYBBAGCNy4DMIGBMD0GCCsGAQUFBwIBFjFodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vUEtJL2RvY3MvQ1BTL2RlZmF1bHQuaHRtMEAGCCsGAQUF # BwICMDQeMiAdAEwAZQBnAGEAbABfAFAAbwBsAGkAYwB5AF8AUwB0AGEAdABlAG0A # ZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQAH5ohRDeLG4Jg/gXEDPZ2joSFv # s+umzPUxvs8F4qn++ldtGTCzwsVmyWrf9efweL3HqJ4l4/m87WtUVwgrUYJEEvu5 # U4zM9GASinbMQEBBm9xcF/9c+V4XNZgkVkt070IQyK+/f8Z/8jd9Wj8c8pl5SpFS # AK84Dxf1L3mBZdmptWvkx872ynoAb0swRCQiPM/tA6WWj1kpvLb9BOFwnzJKJ/1V # ry/+tuWOM7tiX5rbV0Dp8c6ZZpCM/2pif93FSguRJuI57BlKcWOdeyFtw5yjojz6 # f32WapB4pm3S4Zz5Hfw42JT0xqUKloakvZ4argRCg7i1gJsiOCC1JeVk7Pf0v35j # WSUPei45V3aicaoGig+JFrphpxHLmtgOR5qAxdDNp9DvfYPw4TtxCd9ddJgiCGHa # sFAeb73x4QDf5zEHpJM692VHeOj4qEir995yfmFrb3epgcunCaw5u+zGy9iCtHLN # HfS4hQEegPsbiSpUObJb2sgNVZl6h3M7COaYLeqN4DMuEin1wC9UJyH3yKxO2ii4 # sanblrKnQqLJzxlBTeCG+SqaoxFmMNO7dDJL32N79ZmKLxvHIa9Zta7cRDyXUHHX # odLFVeNp3lfB0d4wwP3M5k37Db9dT+mdHhk4L7zPWAUu7w2gUDXa7wknHNWzfjUe # CLraNtvTX4/edIhJEqGCAsswggI0AgEBMIH4oYHQpIHNMIHKMQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1l # cmljYSBPcGVyYXRpb25zMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpENkJELUUz # RTctMTY4NTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaIj # CgEBMAcGBSsOAwIaAxUAIw17n3LxNWtGEZtallmkMZYeoBKggYMwgYCkfjB8MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNy # b3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIFAOOqKgQw # IhgPMjAyMTAxMTQxMDI1MDhaGA8yMDIxMDExNTEwMjUwOFowdDA6BgorBgEEAYRZ # CgQBMSwwKjAKAgUA46oqBAIBADAHAgEAAgIZODAHAgEAAgIRnDAKAgUA46t7hAIB # ADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAIDB6EgoQow # CAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAHVLg9Are9VlhdKAbknHBZYTeDNK # ufrHM3ONPx+yRoMdFumZ9BArI0E1hnEZVqYWRijLxmWZ2/nscKmguLmS2qVZj3Lx # o/XWds7YZZyyHimGYoiqDV976RWPUD6RiA/RCTX66sqpbfAlqU3oqDnKAmKd5wEX # ynbYQR88icCGfW/pMYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMCVVMxEzARBgNV # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv # c29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAg # UENBIDIwMTACEzMAAAFQWKLUp5sLMOsAAAAAAVAwDQYJYIZIAWUDBAIBBQCgggFK # MBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQxIgQgogHE # 8/TxdcTNFaGJnm609LkJHtAjw+AA/wRsQwyP1m4wgfoGCyqGSIb3DQEJEAIvMYHq # MIHnMIHkMIG9BCBs9D6fL5rCThgXJmGIhdXS6IY1Zg6op47dkKJ8L/Kj9jCBmDCB # gKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH # EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNV # BAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAABUFii1KebCzDr # AAAAAAFQMCIEICGvMFiPT3yATufx72XMBaZHjxXfus0Td7hMYFEWwo0zMA0GCSqG # SIb3DQEBCwUABIIBAMlomuAW05qpQ3cKOnBYoJiiK+/KWAi2CMcFbDMHGN0v10qy # QoMzzhcDZqw60SEK+ICnDTJP8vVeVO6LzzaMqU0jPMPp7cLb5IjVYUfcSwQ8tanU # QOUnBQcMm/1KrEVkSGjuLaAw8XyyqimGWZ+Qbl9C6KFecvyk1sJ32R83mt3NivAE # rRl5F4QvFY3e9opGVGEEP5bWCH3ICUpVir3IGENvDWRV48HCc4rnmBqxbpWsOsEY # jjaXd08GqrX8xlDjS3Xd75mXZWrovtasjwpIZ2aKmOavubzObRO452rlhExGVVWm # umO7PHnxoYybzd5zqPKIfUm4PqMAfpRpGDTyyFU= # SIG # End signature block |