Global Service Monitor (GSM) is a cloud service that provides a simplified way to monitor the availability of web-based applications from multiple locations around the world. GSM tests can be performed from 16 different locations around the world.
GSM supports the following two types of monitoring:
Important: GSM is retiring in favor of Application Insights in November 2018.
Application Insights offer two types of tests, very similar to GSM:
Besides these, there are many other additional features which are offered by Application Insights that can help to diagnose application performance issues in depth https://docs.microsoft.com/en-us/azure/application-insights/app-insights-monitor-web-app-availability
The System Center team has developed a Migration tool which will migrate all your Ping Tests and Web Tests from GSM to Azure Application Insights. This tool will also create corresponding alerts in Application Insights, like what you had configured in GSM for your tests.
Important: The following sections provide information on how to migrate the GSM tests to Application Insights, also few notes and points to consider while migrating.
Understand the following details/do the actions applicable in case you are attempting to migrate the tests under specific scenarios as mentioned below:
Scenario | Notes |
You are trying to migrate more than 800 tests | AI limits these. Learn more about the limits. The resource group can hold only 800 web tests and a component can only hold 100 tests. The migration tool creates a new resource group and migrates the remaining tests to this new resource group. This new resource group will be named as ""<RESOURCE_GROUP_NAME1>" and so on. |
You do not wish to run the script from the SCOM server | Install SCOM Powershell on the computer, from where you want to run the script, and provide the SCOM server name in the PowerShell. |
You wish to do a logical grouping of tests | The customer can run the tool and move everything on https://resources.azure.com . There is an option to moveResources under -resourceGroups à DTSLogCleaner. |
Important: To avoid duplicate tests in Application Insights, ensure you run the script only once.
Logging from the tool:
Important: During migration if there is any issue with Application Insights and all the tests are not yet migrated then please follow these steps:
Once you have successfully migrated all your GSM tests to Application Insights, verify these tests in the Azure Application Insights.
Note: Once all the tests are migrated to Application Insights, the test summary displays the test results in some time.
Click the green/red dot to find the result summary,
In Application insights, URL ping tests are configured to monitor HTTP response. Http Status code 200 indicates success that a normal web page has been returned. All the GSM availability tests report success/failure based on this criterion after they are migrated to Application Insights. The failures can be sent through an email or setup a webhook which would be called when an alert is raised.
Note: The migration tool with create Alerts for your GSM tests in Application Insights. See highlighted is enabled.
Application insights provides the description and status of alerts under
Home-> Resource groups -> <ResourceGroupName>- Activity Log in Azure Portal.
There are few functionalities available in GSM but not available in Application Insights.
You cannot monitor the following scenarios in AI:
<# .SYNOPSIS . .DESCRIPTION The script will migrate all the GSM tests to Application Insights. .PARAMETER Path The path to the . .PARAMETER LiteralPath Specifies a path to one or more locations. Unlike Path, the value of LiteralPath is used exactly as it is typed. No characters are interpreted as wildcards. If the path includes escape characters, enclose it in single quotation marks. Single quotation marks tell Windows PowerShell not to interpret any characters as escape sequences. .EXAMPLE C:\PS> .\MigrateGSMToAI.ps1 -SubscriptionName MyAzureSubscription -AzureResourceGroupName MyResourceGroupName -ResourceLocation "East US" -SCOMMSComputerName "MySCOMComputer" #> Param( ############## The Azure subscription Name. [Parameter(Mandatory=$true)] [string]$SubscriptionName, ############## The name of the resource group in azure portal, if you do not have the resource group created in azure, the script will create it. [Parameter(Mandatory=$true)] [string]$AzureResourceGroupName, ############## The name of the Applications Insight's component, you can provide the parameter, by default it will be take SCOM's Management Group name and all tests will be created under this component. [Parameter(Mandatory=$false)] [string]$AIcomponentName, ############## Resource location for the Azure Resource group. [Parameter(Mandatory=$true)] [string]$ResourceLocation, ############## Computer Name for SCOM Management Server machine. Provide the parameter if you are using a remote machine to connect to the Management Server or leave blank. [Parameter(Mandatory=$false)] [string]$SCOMMSComputerName, ############## Provide the Instance IDs of Ping tests we failed to migrate. [Parameter(Mandatory=$false)] [String[]]$FailedPingTestsInstanceIds, ############## Provide the Instance IDs of MultiStep tests we failed to migrate. [Parameter(Mandatory=$false)] [String[]]$FailedMultiStepTestsInstanceIds ) #end param ############## Log the progress of Migration. function LogMessage([string]$message, [string]$logFileName, [int]$logLevel, [string]$testNameForLog) { $levelText = "" If ( $logLevel -eq $ERROR ) { $levelText = "[ERROR]" $newLogFileName = $logFileName.Replace($testNameForLog, ($testNameForLog + "-Failed")) Move-Item -Path $logFileName -Destination $newLogFileName -Force $logFileName = $newLogFileName } ElseIf ( $logLevel -eq $FATALERROR ) { $levelText = "[FATALERROR]" Write-Host "The script has ended abruptly, Please see the logs @ :- $migrationLogFileName" -ForegroundColor Red } ElseIf ( $logLevel -eq $DUPERROR ) { $levelText = "[DUPLICATE TEST]" } Else { $levelText = "[INFO]" } $messageToBelogged = $($levelText + " : " + $message) $messageToBelogged | Out-File -Append -filepath $logFileName -Force } ############## Check prerequisites function Check-Prerequisites { If ( -not (Get-Module OperationsManager ) ) { Import-Module OperationsManager } If ( -not (Get-Module OperationsManager ) ) { LogMessage "OperationsManager Powershell module not found." $migrationLogFileName $FATALERROR exit } If ( -not ( Get-Module AzureRm.Profile ) ) { Import-Module AzureRm.Profile } If ( -not (Get-Module AzureRm.Profile ) ) { LogMessage "AzureRm.Profile Powershell module not found." $migrationLogFileName $FATALERROR exit } } ############## Interactive login to connect to the Azure account. function Login-ToAzure { $azureConnection = $null Try { $azureConnection = Connect-AzureRmAccount } Catch { LogMessage "Please verify the login credentials. Unable to login to the azure portal." $migrationLogFileName $FATALERROR exit } If ($azureConnection -eq $null) { LogMessage "Please verify the login credentials. Unable to login to the azure portal." $migrationLogFileName $FATALERROR exit } } ############## Function creates the resource group if already not present on azure. function Create-ResourceGroup() { $resourceGroupFromAzure = $null $IsResourceGroupFoundToBeUsed = $false $IsResourceGroupToBeCreated = $false While ( -Not $IsResourceGroupFoundToBeUsed ) { Try { LogMessage "Verifying if the resource group with Name:- $script:resourceGroupName and location:- $ResourceLocation already exists." $migrationLogFileName $INFORMATION $resourceGroupFromAzure = Get-AzureRmResource -ResourceGroupName $script:resourceGroupName -ResourceType "microsoft.insights/components" -ApiVersion 2014-04-01 If ( $resourceGroupFromAzure -ne $null ) { If ( $( $resourceGroupFromAzure | Group-Object ResourceType).Count -ge 8 ) { $resourceGroupSuffix ++ $script:resourceGroupName = $( $AzureResourceGroupName + $resourceGroupSuffix) continue } } Else { $resourceGroupFromAzure = Get-AzureRmResource -ResourceId /subscriptions/$subscriptionID/resourceGroups/$script:resourceGroupName -ApiVersion 2014-04-01 If ( $resourceGroupFromAzure -ne $null ) { LogMessage "Resource group with Name:- $script:resourceGroupName and location:- $resourceLocation already exists." $migrationLogFileName $INFORMATION $IsResourceGroupFoundToBeUsed = $true } Else { LogMessage "The azure resource group:- $script:resourceGroupName does not exist. Will try to create the resource group." $migrationLogFileName $INFORMATION $IsResourceGroupToBeCreated = $true } } } Catch { LogMessage "The azure resource group:- $script:resourceGroupName does not exist. Will try to create the resource group." $migrationLogFileName $INFORMATION $IsResourceGroupToBeCreated = $true } $IsResourceGroupFoundToBeUsed = $true } If ( $IsResourceGroupToBeCreated ) { LogMessage "Creating resource group with Name:- $script:resourceGroupName and location:- $ResourceLocation." $migrationLogFileName $INFORMATION Try { New-AzureRmResourceGroup -Location $ResourceLocation -Name $script:resourceGroupName } Catch { LogMessage "Unable to create the resource group with Name:- $script:resourceGroupName and location:- $ResourceLocation. Please verify if you have the correct privileges to create the resource group." $migrationLogFileName $FATALERROR exit } } Else { LogMessage "Resource group with Name:- $script:resourceGroupName and location:- $ResourceLocation already exists." $migrationLogFileName $INFORMATION return } LogMessage "Created Resource group with Name:- $script:resourceGroupName and location:- $ResourceLocation." $migrationLogFileName $INFORMATION } ############## Function for creating component in Application Insights. function Create-Component( $componentName ) { $kindOfComponent = "web" $componentJSON = "" $componentJSONObject = New-Object -TypeName PSObject Add-Member -InputObject $componentJSONObject -MemberType NoteProperty -Name name -Value $componentName Add-Member -InputObject $componentJSONObject -MemberType NoteProperty -Name location -Value $ResourceLocation Add-Member -InputObject $componentJSONObject -MemberType NoteProperty -Name kind -Value $kindOfComponent $componentPropertiesObject = New-Object -TypeName PSObject Add-Member -InputObject $componentPropertiesObject -MemberType NoteProperty -Name Application_Type -Value "web" Add-Member -InputObject $componentPropertiesObject -MemberType NoteProperty -Name ApplicationId -Value $componentName Add-Member -InputObject $componentPropertiesObject -MemberType NoteProperty -Name Flow_Type -Value "Bluefield" Add-Member -InputObject $componentPropertiesObject -MemberType NoteProperty -Name Request_Source -Value "rest" Add-Member -InputObject $componentJSONObject -MemberType NoteProperty -Name properties -Value $componentPropertiesObject $componentJSON = $componentJSONObject | ConvertTo-Json LogMessage $( "Component JSON:- " + $componentJSON ) $migrationLogFileName $INFORMATION LogMessage "Creating Component with Name:- $componentName." $migrationLogFileName $INFORMATION Try { New-AzureRmResource -ResourceName $componentName -Location $ResourceLocation -PropertyObject $componentJSON -ResourceGroupName $script:resourceGroupName -ResourceType microsoft.insights/components -ApiVersion 2015-05-01 -Force } Catch { LogMessage "Unable to create Component with Name:- $componentName." $migrationLogFileName $FATALERROR exit } LogMessage $( "Created Component Name:- " + $componentName + " in Resource group :- " + $script:resourceGroupName ) $migrationLogFileName $INFORMATION } ############## Function to validate the resource location provided by the user as the parameter ResourceLocation function ValidateResourceLocation() { $azureLocations = $(Get-AzureRmLocation).DisplayName If ( -Not $azureLocations.Contains($ResourceLocation) ) { LogMessage $("Please provide a valid resource location. Here are the valid locations "+$azureLocations) $migrationLogFileName $FATALERROR exit } } ############## Function for formating GeoLocations to REST API consumable format. function Format-GeoLocations ( $testLocations ) { $webTestGeoLocations = @{}, @{} Foreach ( $location in $testLocations ) { Switch ( $location ) { "emea-nl-ams-edge" { $location = "emea-nl-ams-azr" } "us-ca-lax-edge" { $location = "us-ca-sjc-azr" } "apac-sg-sin-edge" { $location = "apac-sg-sin-azr" } "us-tx-sn1-edge" { $location = "us-tx-sn1-azr" } "us-il-ch1-edge" { $location = "us-il-ch1-azr" } "emea-gb-lts-edge" { $location = "emea-gb-db3-azr" } "emea-gb-lts-edge" { $location = "emea-gb-db3-azr" } "us-nj-ewr-edge" { $location = "us-va-ash-azr" } "apac-tw-tpa-edge" { $location = "apac-hk-hkn-azr" } } $webTestGeoLocations += @{Id = $location} } return $webTestGeoLocations | Select-Object -Skip 2 } ############## Common Function for Ping and Multistep test to create JSON. function Build-TestJSON ( $scomTest, $completeWebTestName, $componentName, $webTestGeoLocations, $webTestXML, $webTestKind, $frequency, $timeout, $testInstanceID) { $testID = $completeWebTestName + "-" + $componentName $testLogFileName = $( $testLogFilePath + $completeWebTestName + ".txt" ) "" | Out-File -filepath $testLogFileName -Force LogMessage $( "Building JSON for Test NAME:- " + $completeWebTestName ) $testLogFileName $INFORMATION LogMessage $( "SCOM Instance ID :- " + $testInstanceID ) $testLogFileName $INFORMATION Write-Host "Building JSON for Test NAME:- " $completeWebTestName -ForegroundColor Green ############# Convert SCOM GSM test frequency into Applications Insights UI supported values (5, 10, 15) minutes $newFrequency = [int][Math]::Ceiling($frequency/60) *60 If ( $newFrequency -gt 900 ) { $newFrequency = 900 } If ( $frequency -ne $newFrequency ) { Write-Host "The Test frequency is changed from :-"$frequency "to :-"$newFrequency -ForegroundColor Yellow LogMessage $( "The Test frequency is changed from :-" + $frequency + " to :- "+ $newFrequency) $testLogFileName $INFORMATION } ############# Convert SCOM GSM test timeout into Applications Insights UI supported values (30, 60, 90, 120) seconds If ( $webTestKind -eq "ping" ) { $newTimeout = [int][Math]::Ceiling($timeout/30) *30 If ( $newTimeout -gt 120 ) { $newTimeout = 120 } If ( $timeout -ne $newTimeout ) { Write-Host "The Test Time out is changed from :-"$timeout "to :-"$newTimeout -ForegroundColor Yellow LogMessage $( "The Test Time out is changed from :-" + $timeout + " to :- "+ $newTimeout) $testLogFileName $INFORMATION } } $enabled = $true $retryEnabled = $true $propertiesObject = New-Object -TypeName PSObject $configurationObject = New-Object -TypeName PSObject Add-Member -InputObject $configurationObject -MemberType NoteProperty -Name WebTest -Value $webTestXML Add-Member -InputObject $propertiesObject -MemberType NoteProperty -Name SyntheticMonitorId -Value $testID Add-Member -InputObject $propertiesObject -MemberType NoteProperty -Name Name -Value $completeWebTestName Add-Member -InputObject $propertiesObject -MemberType NoteProperty -Name Description -Value ("$webTestKind web test for " + $completeWebTestName) Add-Member -InputObject $propertiesObject -MemberType NoteProperty -Name Enabled -Value $enabled Add-Member -InputObject $propertiesObject -MemberType NoteProperty -Name Frequency -Value $newFrequency Add-Member -InputObject $propertiesObject -MemberType NoteProperty -Name Timeout -Value $newTimeout Add-Member -InputObject $propertiesObject -MemberType NoteProperty -Name Kind -Value $webTestKind Add-Member -InputObject $propertiesObject -MemberType NoteProperty -Name RetryEnabled -Value $retryEnabled Add-Member -InputObject $propertiesObject -MemberType NoteProperty -Name Locations -Value @( $webTestGeoLocations ) Add-Member -InputObject $propertiesObject -MemberType NoteProperty -Name Configuration -Value $configurationObject return $propertiesObject } ############## Function for Building MultiStepTest JSON. function Build-MultiStepTest( $FailedMultiStepTestsInstanceIds ) { $multiTestJSON = "" $multiStepTestsForRequest = @() $numberOfTestsInComponent = 0 $componentSuffix = 1 $allGeoLocations = Get-SCOMClass -Name "Microsoft.SystemCenter.Omonline.OutsideIn.OutsideInPop" | Get-SCOMClassInstance If ( $FailedMultiStepTestsInstanceIds.count -eq 0) { $multiStepTests = Get-SCOMClass -DisplayName "Visual Studio Web Test Container" | Get-SCOMClassInstance } Else { $multiStepTests = Get-SCOMClassInstance -Id $FailedMultiStepTestsInstanceIds $componentSuffix = $( $( Get-AzureRmResource -ResourceGroupName $script:resourceGroupName -ResourceType "microsoft.insights/components" -ApiVersion 2014-04-01 ) | Group-Object ResourceType).Count $componentSuffix ++ } Foreach ( $multiStepTest in $multiStepTests ) { $multiStepTestID = $multiStepTest.Id If ( $numberOfTestsInComponent -eq 0 ) { ############## Invoke Create Component using New-AzureRmResource. More info -- https://docs.microsoft.com/en-us/powershell/module/azurerm.resources/new-azurermresource?view=azurermps-6.5.0 $componentName = $("MultiStep-" + $AIcomponentName + $componentSuffix) $componentSuffix++ } [xml]$testLocationsXML = $multiStepTest.'[Microsoft.SystemCenter.Omonline.OutsideIn.VSWebTest.VSWebTestContainer].Locations'.Value $testLocationsIDs = $testLocationsXML.Locations.Location.managementActionPointId $testLocations = $allGeoLocations | Select-Object -Property Id, Name | Where-Object {$_.Id -in $testLocationsIDs} $webTestGeoLocations = Format-GeoLocations $testLocations.Name ## Case where test is configured to run on internal agent. If ( $webTestGeoLocations -eq $null ) { continue; } $NumOfLocMultiTest = $webTestGeoLocations.Count If ( $NumOfLocMultiTest -ge 3 ) { $NumOfLocMultiTest = 3; } [xml]$testConfigXML = $multiStepTest.'[Microsoft.SystemCenter.Omonline.OutsideIn.VSWebTest.VSWebTestContainer].VSDeclarativeTestConfigs'.Value $webTestXML = $testConfigXML.VSWebTestConfigs.VSWebTestElement.content $multiTestFrequency = $multiStepTest.'[Microsoft.SystemCenter.Omonline.OutsideIn.VSWebTest.VSWebTestContainer].IntervalInSeconds'.Value ## Case where test name is a duplicate If ( $allTests.Contains($multiStepTest.DisplayName) ) { $testLogFileName = $( $testLogFilePath + "MultiStep-DuplicateTest.txt" ) LogMessage $( "Cannot migrate test, as there is already a multistep test with name :- " + $multiStepTest.DisplayName + ". Please rename the test to migrate." ) $testLogFileName $DUPERROR LogMessage $( "Information to find the SCOM Instance ID :- " + $multiStepTestID + " with name :- " + $multiStepTest.DisplayName ) $testLogFileName $DUPERROR Write-Host "Cannot migrate test, as there is already a multistep test with name :- " $multiStepTest.DisplayName ". Please rename the test to migrate." -ForegroundColor Red continue } Else { $allTests.Add($multiStepTest.DisplayName) $numberOfTestsInComponent++ If ( $numberOfTestsInComponent -ge $componentLimit ) { $numberOfTestsInComponent = 0 } } $multiStepTestProperty = Build-TestJSON $multiStepTest $multiStepTest.DisplayName $componentName $webTestGeoLocations $webTestXML "multistep" $multiTestFrequency 120 $multiStepTestID $multiStepTestForRequest = New-Object -TypeName PSObject Add-Member -InputObject $multiStepTestForRequest -MemberType NoteProperty -Name NAME -Value $multiStepTest.DisplayName Add-Member -InputObject $multiStepTestForRequest -MemberType NoteProperty -Name ID -Value $multiStepTestID Add-Member -InputObject $multiStepTestForRequest -MemberType NoteProperty -Name PROPERTY -Value $multiStepTestProperty Add-Member -InputObject $multiStepTestForRequest -MemberType NoteProperty -Name COMPONENT -Value $componentName Add-Member -InputObject $multiStepTestForRequest -MemberType NoteProperty -Name NUMOFLOC -Value $NumOfLocMultiTest $multiStepTestsForRequest += $multiStepTestForRequest } return $multiStepTestsForRequest } ############## Function for Http Headers for PingTest. function Build-HTTPHeaders( $httpHeaders ) { [xml]$pingHeaders = '' $headersFlag = $true Foreach ( $httpHeader in $httpHeaders.HttpHeader ) { If ( $headersFlag ) { $pingHeaders.Headers.Header.Name = $httpHeader.Name $pingHeaders.Headers.Header.Value = $httpHeader.Value $headersFlag = $false } Else { $pingHeader = $pingHeaders.CreateElement("Header") $pingHeader.SetAttribute("Name", $httpHeader.Name) $pingHeader.SetAttribute("Value", $httpHeader.Value) [void]$pingHeaders.Headers.AppendChild($pingHeader) } } return $pingHeaders.OuterXml } ############## Function to create StringHttpBody for Ping Test, We are assuming that the customer has provided the body in JSON format function Build-StringHttpBody( $pingTestBody, $contentType ) { return ' '+$([Convert]::ToBase64String($([System.Text.Encoding]::Unicode.GetBytes($pingTestBody))))+' ' } ############## Function for Building PingTest JSON. function Build-PingTest( $FailedPingTestsInstanceIds ) { $pingTestsForRequest = @() $numberOfTestsInComponent = 0 $componentSuffix = 1 $allGeoLocations = Get-SCOMClass -Name "Microsoft.SystemCenter.Omonline.OutsideIn.OutsideInPop" | Get-SCOMClassInstance If ( $FailedPingTestsInstanceIds.count -eq 0 ) { $pingTests = Get-SCOMClass -Name "Microsoft.SystemCenter.WebApplicationSolutions.SingleUrlTest" | Get-SCOMClassInstance } Else { $pingTests = Get-SCOMClassInstance -Id $FailedPingTestsInstanceIds $componentSuffix = $( $( Get-AzureRmResource -ResourceGroupName $script:resourceGroupName -ResourceType "microsoft.insights/components" -ApiVersion 2014-04-01 ) | Group-Object ResourceType).Count $componentSuffix ++ } Foreach ( $pingTest in $pingTests ) { If ( $numberOfTestsInComponent -eq 0 ) { ############## Invoke Create Component using New-AzureRmResource. More info -- https://docs.microsoft.com/en-us/powershell/module/azurerm.resources/new-azurermresource?view=azurermps-6.5.0 $componentName = $("Ping-" + $AIcomponentName + $componentSuffix) $componentSuffix++ } $pingJSON = "" $parametersValues = "" $pingTestFrequency = 0 $pingTestConfig = $null $pingTestRequest = $null $pingTestTimeOut = 0 $followRedirects = "" $httpVersion = "" $httpMethod = "" $httpHeaders = "" $pingTestHttpHeaders = "" $pingTestStringHttpBody= "" $pingTestBody = "" $pingTestForRequest = $null $pingTestId = $pingTest.Id [xml]$testLocationsXML = $pingTest.'[Microsoft.SystemCenter.WebApplicationSolutions.SingleUrlTest].Locations'.Value $testLocationsIDs = $testLocationsXML.Locations.Location.managementActionPointId $testLocations = $allGeoLocations | Select-Object -Property Id, Name | Where-Object {$_.Id -in $testLocationsIDs} $webTestGeoLocations = Format-GeoLocations $testLocations.Name ## Case where test is configured to run on internal agent. If ( $webTestGeoLocations -eq $null ) { continue; } $NumOfLocPingTest = $webTestGeoLocations.Count If ( $NumOfLocPingTest -ge 3 ) { $NumOfLocPingTest = 3; } $parametersValues = ([xml]($pingTest.'[Microsoft.SystemCenter.WebApplicationSolutions.SingleUrlTest].Parameters'.Value)).Parameters.Parameter.Value $pingTestFrequency = $pingTest.'[Microsoft.SystemCenter.WebApplicationSolutions.SingleUrlTest].IntervalInSeconds'.Value $pingTestConfig = ([xml]($pingTest.'[Microsoft.SystemCenter.WebApplicationSolutions.SingleUrlTest].TestConfig'.Value)).TestConfig $pingTestRequest = $pingTestConfig.Requests.Request $pingTestTimeOut = $pingTestConfig.TestTimeout If ( $pingTestTimeOut -eq $null ) { $pingTestTimeOut = 30 } $followRedirects = $pingTestConfig.FollowRedirects If ( $followRedirects -eq $null ) { $followRedirects = "True" } $httpVersion = $pingTestRequest.Version If ( $httpVersion -eq $null ) { $httpVersion = "1.1" } $httpMethod = $pingTestRequest.Verb If ( $httpMethod -eq $null ) { $httpMethod = "GET" } $httpHeaders = $pingTestRequest.HttpHeaders If ( $httpHeaders -ne $null ) { $pingTestHttpHeaders = Build-HTTPHeaders $httpHeaders } $pingTestBody = $pingTestRequest.Body If ( $pingTestBody -ne $null ) { [xml]$pingTestHttpHeadersXML = $pingTestHttpHeaders $contentTypeForTest = $($pingTestHttpHeadersXML.Headers.Header | Where-Object {$_.Name -eq "Content-Type"}).Value $pingTestStringHttpBody = Build-StringHttpBody $pingTestBody $contentTypeForTest } Foreach ( $parametersValue in $parametersValues ) { $pingTestURL = $parametersValue.Innertext $webTestXML = '' $pingtestName = $( $pingTest.DisplayName + "-" + $parametersValue.displayName ) ## Case where test name is a duplicate If ( $allTests.Contains( $($pingtestName + "-" + $componentName) ) ) { $testLogFileName = $( $testLogFilePath + "Ping-DuplicateTest.txt" ) LogMessage $( "Cannot migrate Ping test, as there is already a Ping test with name :- " + $pingtestName + " in component :- " + $componentName + ". Please rename the test to migrate." ) $testLogFileName $DUPERROR LogMessage $( "Information to find the SCOM Instance ID :- " + $pingTestId + " with name :- " + $pingtestName ) $testLogFileName $DUPERROR Write-Host "Cannot migrate Ping test, as there is already a Ping test with name :- " $pingtestName " with URL :- " $pingTestURL " in component :- " $componentName " . Please rename the test to migrate" -ForegroundColor Red continue } Else { $allTests.Add( $($pingtestName + "-" + $componentName) ) $numberOfTestsInComponent++ If ( $numberOfTestsInComponent -ge $componentLimit ) { $numberOfTestsInComponent = 0 } } $pingTestProperty = Build-TestJSON $pingTest $pingtestName $componentName $webTestGeoLocations $webTestXML "ping" $pingTestFrequency $pingTestTimeOut $pingTestId $pingTestForRequest = New-Object -TypeName PSObject Add-Member -InputObject $pingTestForRequest -MemberType NoteProperty -Name NAME -Value $pingtestName Add-Member -InputObject $pingTestForRequest -MemberType NoteProperty -Name ID -Value $pingTestId Add-Member -InputObject $pingTestForRequest -MemberType NoteProperty -Name PROPERTY -Value $pingTestProperty Add-Member -InputObject $pingTestForRequest -MemberType NoteProperty -Name COMPONENT -Value $componentName Add-Member -InputObject $pingTestForRequest -MemberType NoteProperty -Name NUMOFLOC -Value $NumOfLocPingTest $pingTestsForRequest += $pingTestForRequest } } return $pingTestsForRequest } ############## Function for creating a web test in Application Insights function Create-WebTest( $webTests ) { $previousComponentName = "" Foreach ( $webTest in $webTests ) { $componentName = $webTest.COMPONENT $propertiesObject = $webtest.PROPERTY If ( $componentName -ne $previousComponentName) { $previousComponentName = $componentName Create-Component $componentName } $testLogFileName = $( $testLogFilePath + $webtest.NAME + ".txt" ) ############## REST Call to create the Web Test $webTestCompleteName = $( $webtest.NAME + "-" + $componentName) $webTestMigrated = $null $webTestAlertCreated = $null LogMessage $( "Migrating Test for NAME:- " + $webtest.NAME ) $testLogFileName $INFORMATION LogMessage $( "Component NAME:- " + $componentName ) $testLogFileName $INFORMATION LogMessage $( "Resource Group NAME:- " + $script:resourceGroupName ) $testLogFileName $INFORMATION LogMessage $( "JSON for Test :- " + $( $propertiesObject | ConvertTo-Json -Depth 5 ) ) $testLogFileName $INFORMATION Write-Host "Migrating Test for NAME:- " $webtest.NAME -ForegroundColor Green Try { $tagsObject = @{} $tagsObject.Add("hidden-link:/subscriptions/$subscriptionID/resourceGroups/$script:resourceGroupName/providers/microsoft.insights/components/$componentName", "Resource") $tagsObject.Add("hidden-link:Microsoft.Internal.IsScomWebTest", "true") New-AzureRmResource -ResourceName $webTestCompleteName -Location $ResourceLocation -PropertyObject $propertiesObject -ResourceGroupName $script:resourceGroupName -ResourceType microsoft.insights/webtests -Tag $tagsObject -ApiVersion 2015-05-01 -Force } Catch { $exceptionString = $_.Exception.Message If ( $exceptionString.contains("ResourceQuotaExceeded") ) { LogMessage $( "Creating a new Resource Group as Resource Group :- " + $script:resourceGroupName + " has exceed the quota of '800' resources." ) $migrationLogFileName $INFORMATION $resourceGroupSuffix ++ $script:resourceGroupName = $( $AzureResourceGroupName + $resourceGroupSuffix) ########### If the quota of 800 resource exceeds, we will create a new Resource group and a new Component in that resource group and re-try the test creation in new Resource group. Create-ResourceGroup Create-Component $componentName Try { $tagsObject = @{} $tagsObject.Add("hidden-link:/subscriptions/$subscriptionID/resourceGroups/$script:resourceGroupName/providers/microsoft.insights/components/$componentName", "Resource") $tagsObject.Add("hidden-link:Microsoft.Internal.IsScomWebTest", "true") New-AzureRmResource -ResourceName $webTestCompleteName -Location $ResourceLocation -PropertyObject $propertiesObject -ResourceGroupName $script:resourceGroupName -ResourceType microsoft.insights/webtests -Tag $tagsObject -ApiVersion 2015-05-01 -Force } Catch { LogMessage $( "Exception:- " + $_.Exception.Message ) $testLogFileName $INFORMATION $webtest.NAME LogMessage $( "Unable to migrate the Test with Name:- " + $webtest.NAME + " SCOM Instance ID :- " + $webtest.ID ) $testLogFileName $ERROR $webtest.NAME Write-Host "Unable to migrate the Test with Name:- " $webtest.NAME ". Please see the logs @ :- $testLogFileName" -ForegroundColor Red continue } } Else { LogMessage $( "Exception:- " + $_.Exception.Message ) $testLogFileName $INFORMATION $webtest.NAME LogMessage $( "Unable to migrate the Test with Name:- " + $webtest.NAME + " SCOM Instance ID :- " + $webtest.ID ) $testLogFileName $ERROR $webtest.NAME Write-Host "Unable to migrate the Test with Name:- " $webtest.NAME ". Please see the logs @ :- $testLogFileName" -ForegroundColor Red continue } } LogMessage $( "Verifying if the test:- " + $webtest.NAME + " got created in Application Insights." ) $testLogFileName $INFORMATION $webTestMigrated = Get-AzureRmResource -ResourceGroupName $script:resourceGroupName -ResourceType microsoft.insights/webtests -ResourceName $webTestCompleteName If ( $webTestMigrated -eq $null ) { LogMessage $( "Exception:- " + $_.Exception.Message ) $testLogFileName $INFORMATION $webtest.NAME LogMessage $( "Migration Not Successful for the Test with Name:- " + $webtest.NAME + " SCOM Instance ID :- " + $webtest.ID ) $testLogFileName $ERROR $webtest.NAME Write-Host "Migration Not Successful for the Test with Name:- " $webtest.NAME ". Please see the logs @ :- $testLogFileName" -ForegroundColor Red continue } Else { LogMessage $( "Migration Successful for the Test with Name:- " + $webtest.NAME ) $testLogFileName $INFORMATION } Try { LogMessage $( "Creating alert for test :- " + $webtest.NAME ) $migrationLogFileName $INFORMATION Create-WebTestAlert $webTestCompleteName $webTest.COMPONENT $webTest.NUMOFLOC } Catch { LogMessage $( "Exception:- " + $_.Exception.Message ) $testLogFileName $INFORMATION $webtest.NAME LogMessage $( "Unable to create alert for Name:- " + $webtest.NAME ) $testLogFileName $ERROR $webtest.NAME Write-Host "Unable to create alert for Name:- " $webtest.NAME ". Please see the logs @ :- $testLogFileName" -ForegroundColor Red continue } LogMessage $( "Verifying if the alert for test:- " + $webtest.NAME + " got created in Application Insights." ) $testLogFileName $INFORMATION $webTestAlertCreated = Get-AzureRmResource -ResourceGroupName $script:resourceGroupName -ResourceType microsoft.insights/alertrules -ResourceName $webTestCompleteName If ( $webTestAlertCreated -eq $null ) { LogMessage $( "Exception:- " + $_.Exception.Message ) $testLogFileName $INFORMATION $webtest.NAME LogMessage $( "Creation of alert Not Successful for the Test with Name:- " + $webtest.NAME ) $testLogFileName $ERROR $webtest.NAME Write-Host "Creation of alert Not Successful for the Test with Name:- " $webtest.NAME ". Please see the logs @ :- $testLogFileName" -ForegroundColor Red continue } Else { LogMessage $( "Creation of alert Successful for the Test with Name:- " + $webtest.NAME ) $testLogFileName $INFORMATION } Write-Host "Migration Successful for the Test with Name:- " $webtest.NAME -ForegroundColor Green $newTestLogFileName = $testLogFileName.Replace($webtest.NAME, ($webtest.NAME + "-Migrated")) Move-Item -Path $testLogFileName -Destination $newTestLogFileName -Force $testLogFileName = $newTestLogFileName } } ############## Function for creating Alert for a web test. function Create-WebTestAlert( $completeWebTestName, $componentName, $numberOfAlertLocations) { $tagsObject = @{} $propertiesObject = New-Object -TypeName PSObject $conditionObject = New-Object -TypeName PSObject $dataSourceObject = New-Object -TypeName PSObject $actionObject = New-Object -TypeName PSObject $tagsObject.Add("hidden-link:/subscriptions/$subscriptionID/resourceGroups/$script:resourceGroupName/providers/microsoft.insights/components/$componentName", "Resource") $tagsObject.Add("hidden-link:/subscriptions/$subscriptionID/resourceGroups/$script:resourceGroupName/providers/microsoft.insights/webtests/$completeWebTestName", "Resource") Add-Member -InputObject $dataSourceObject -MemberType NoteProperty -Name odata.type -Value "Microsoft.Azure.Management.Insights.Models.RuleMetricDataSource" Add-Member -InputObject $dataSourceObject -MemberType NoteProperty -Name MetricName -Value "GSMT_AvRaW" Add-Member -InputObject $dataSourceObject -MemberType NoteProperty -Name ResourceUri -Value "/subscriptions/$subscriptionID/resourceGroups/$script:resourceGroupName/providers/microsoft.insights/webtests/$completeWebTestName" Add-Member -InputObject $conditionObject -MemberType NoteProperty -Name odata.type -Value "Microsoft.Azure.Management.Insights.Models.LocationThresholdRuleCondition" Add-Member -InputObject $conditionObject -MemberType NoteProperty -Name DataSource -Value $dataSourceObject Add-Member -InputObject $conditionObject -MemberType NoteProperty -Name WindowSize -Value "PT5M" Add-Member -InputObject $conditionObject -MemberType NoteProperty -Name FailedLocationCount -Value $numberOfAlertLocations Add-Member -InputObject $actionObject -MemberType NoteProperty -Name odata.type -Value "Microsoft.Azure.Management.Insights.Models.RuleEmailAction" Add-Member -InputObject $actionObject -MemberType NoteProperty -Name SendToServiceOwners -Value $true Add-Member -InputObject $actionObject -MemberType NoteProperty -Name CustomEmails -Value @() Add-Member -InputObject $propertiesObject -MemberType NoteProperty -Name Name -Value $completeWebTestName Add-Member -InputObject $propertiesObject -MemberType NoteProperty -Name Description -Value ("Alert rule for web test " + $completeWebTestName) Add-Member -InputObject $propertiesObject -MemberType NoteProperty -Name IsEnabled -Value $true Add-Member -InputObject $propertiesObject -MemberType NoteProperty -Name Condition -Value $conditionObject Add-Member -InputObject $propertiesObject -MemberType NoteProperty -Name Action -Value $actionObject Add-Member -InputObject $propertiesObject -MemberType NoteProperty -Name actions -Value @() LogMessage $( "JSON Data for the alert :- " + $( $propertiesObject | ConvertTo-Json -Depth 5 ) ) $testLogFileName $INFORMATION New-AzureRmResource -ResourceName $completeWebTestName -Location $ResourceLocation -PropertyObject $propertiesObject -ResourceGroupName $script:resourceGroupName -ResourceType microsoft.insights/alertrules -Tag $tagsObject -ApiVersion 2016-03-01 -Force } ############## START OF SCRIPT ############## clear ############## Logging variables $ERROR = 1 $FATALERROR = 2 $DUPERROR = 3 $INFORMATION = 4 $testLogFilePath = $(Get-Location).Path + "\Logs\" $migrationLogFileName = $testLogFilePath + "MigrationLog.txt" $testLogFileName = "" $allTests = New-Object 'System.Collections.Generic.List[string]' ############## Variables used in the script. $subscriptionID = "" $pingWebTests = $null $multiStepWebTests = $null $subscription = $null $componentLimit = 100 $resourceGroupSuffix = 0 $resourceGroupName = $AzureResourceGroupName $allWebTestsToMigrate = @() ############## Creating Log folder if does not exist, and delete all log files if already exists. If ( -Not (Test-Path -Path $testLogFilePath) ) { New-Item -ItemType directory -Path $testLogFilePath -ErrorAction Stop } Else { Remove-Item $testLogFilePath* -Include *.txt } "[INFO] START OF SCRIPT" | Out-File -filepath $migrationLogFileName -Force ############## Check Prerequisites. Check-Prerequisites ############## Creates a persistent connection to a management group. Try { IF ( [string]::IsNullOrEmpty($SCOMMSComputerName) ) { New-SCOMManagementGroupConnection } Else { $SCOMCredentials = Get-Credential -Message "Enter SCOM Management Server Credentials." New-SCOMManagementGroupConnection -ComputerName $SCOMMSComputerName -Credential $SCOMCredentials } } Catch { LogMessage $( "Exception:- " + $_.Exception.Message ) $migrationLogFileName $FATALERROR LogMessage "Unable to connect to SCOM. Please verify your credentials" $migrationLogFileName $FATALERROR exit } ############## If the Component Name is not provided in the script parameters, defaulting it to SCOM Management Group. If ( [string]::IsNullOrEmpty($AIcomponentName) ) { $AIcomponentName = $(Get-SCOMManagementGroup).Name } ############## Interactive Login to connect to the Azure account. Login-ToAzure ############## Interactive Login to connect to the Azure account. ValidateResourceLocation ############## Getting The Azure subscription ID from the subcription name. Try { Select-AzureRmSubscription -SubscriptionName $SubscriptionName $subscription = (Get-AzureRmContext).Subscription $subscriptionID = $subscription.Id } Catch { LogMessage $( "Exception:- " + $_.Exception.Message ) $migrationLogFileName $FATALERROR LogMessage "Unable to get the Subscription:- $SubscriptionName from the azure account. Please verify the name of the subscription." $migrationLogFileName $FATALERROR exit } ############## Creates Resource group if not present. Create-ResourceGroup ############## Get the data of the ping test from operations manager and then create the test in Application Insights If ( (( $FailedPingTestsInstanceIds.count -eq 0 ) -and ( $FailedMultiStepTestsInstanceIds.count -eq 0 )) -or ($FailedPingTestsInstanceIds.count -ne 0) ) { $pingWebTests = Build-PingTest $FailedPingTestsInstanceIds $allWebTestsToMigrate += $pingWebTests } ############## Get the data of the multi step test from operations manager and then create the test in Application Insights If ( (( $FailedPingTestsInstanceIds.count -eq 0 ) -and ( $FailedMultiStepTestsInstanceIds.count -eq 0 )) -or ($FailedMultiStepTestsInstanceIds.count -ne 0) ) { $multiStepWebTests = Build-MultiStepTest $FailedMultiStepTestsInstanceIds $allWebTestsToMigrate += $multiStepWebTests } ############## Create Ping and MultiStep Tests in Azure Application Insights. Create-WebTest $allWebTestsToMigrate ############## Logout the session. Disconnect-AzureRmAccount ############## Deletes persistent connections to management groups. Get-SCOMManagementGroupConnection |?{$_.IsActive } | Remove-SCOMManagementGroupConnection ############## All the Log files of the migrated tests. $migratedTestsLogFiles = get-childitem -Path $testLogFilePath | where-object {$_.Name -like "*-Migrated*"} If ( $migratedTestsLogFiles ) { LogMessage $( "Logs for all the tests we successfully migrated to Azure App Insights :-" + $migratedTestsLogFiles ) $migrationLogFileName $INFORMATION Write-Host "Logs for all the tests we successfully migrated to Azure App Insights :-" $migratedTestsLogFiles -ForegroundColor Green } ############## All the Log files of the failed tests. $failedTestsLogFiles = get-childitem -Path $testLogFilePath | where-object {$_.Name -like "*-Failed*"} If ( $failedTestsLogFiles ) { LogMessage $( "Logs of all the tests which are failed to migrate to Azure App Insights :-" + $failedTestsLogFiles ) $migrationLogFileName $INFORMATION Write-Host "Logs of all the tests which are failed to migrate to Azure App Insights :-" $failedTestsLogFiles -ForegroundColor Red } ############## All the Log files of the duplicate tests. $duplicateTestsLogFiles = get-childitem -Path $testLogFilePath | where-object {$_.Name -like "*-DuplicateTest*"} If ( $duplicateTestsLogFiles ) { LogMessage $( "Please refer the logs :- " + $duplicateTestsLogFiles + " to get all the tests which are duplicate, please rename them and re-migrate." ) $migrationLogFileName $INFORMATION Write-Host "Please refer the logs :- " $duplicateTestsLogFiles " to get all the tests which are duplicate, please rename them and re-migrate." -ForegroundColor Red } LogMessage "[INFO] END OF SCRIPT" $migrationLogFileName $INFORMATION ############## Please verify the web test should get created on the Application Insights. ############## END OF SCRIPT ############## # SIG # Begin signature block # MIIkmgYJKoZIhvcNAQcCoIIkizCCJIcCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCD+JCbmHCRozGmo # bLxC0111LvTnQmJ9bybdN3/mwuPfOqCCDYEwggX/MIID56ADAgECAhMzAAABA14l # HJkfox64AAAAAAEDMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMTgwNzEyMjAwODQ4WhcNMTkwNzI2MjAwODQ4WjB0MQsw # CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u # ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB # AQDRlHY25oarNv5p+UZ8i4hQy5Bwf7BVqSQdfjnnBZ8PrHuXss5zCvvUmyRcFrU5 # 3Rt+M2wR/Dsm85iqXVNrqsPsE7jS789Xf8xly69NLjKxVitONAeJ/mkhvT5E+94S # nYW/fHaGfXKxdpth5opkTEbOttU6jHeTd2chnLZaBl5HhvU80QnKDT3NsumhUHjR # hIjiATwi/K+WCMxdmcDt66VamJL1yEBOanOv3uN0etNfRpe84mcod5mswQ4xFo8A # DwH+S15UD8rEZT8K46NG2/YsAzoZvmgFFpzmfzS/p4eNZTkmyWPU78XdvSX+/Sj0 # NIZ5rCrVXzCRO+QUauuxygQjAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE # AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUR77Ay+GmP/1l1jjyA123r3f3QP8w # UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1 # ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDM3OTY1MB8GA1UdIwQYMBaAFEhu # ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu # bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w # Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3 # Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx # MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAn/XJ # Uw0/DSbsokTYDdGfY5YGSz8eXMUzo6TDbK8fwAG662XsnjMQD6esW9S9kGEX5zHn # wya0rPUn00iThoj+EjWRZCLRay07qCwVlCnSN5bmNf8MzsgGFhaeJLHiOfluDnjY # DBu2KWAndjQkm925l3XLATutghIWIoCJFYS7mFAgsBcmhkmvzn1FFUM0ls+BXBgs # 1JPyZ6vic8g9o838Mh5gHOmwGzD7LLsHLpaEk0UoVFzNlv2g24HYtjDKQ7HzSMCy # RhxdXnYqWJ/U7vL0+khMtWGLsIxB6aq4nZD0/2pCD7k+6Q7slPyNgLt44yOneFuy # bR/5WcF9ttE5yXnggxxgCto9sNHtNr9FB+kbNm7lPTsFA6fUpyUSj+Z2oxOzRVpD # MYLa2ISuubAfdfX2HX1RETcn6LU1hHH3V6qu+olxyZjSnlpkdr6Mw30VapHxFPTy # 2TUxuNty+rR1yIibar+YRcdmstf/zpKQdeTr5obSyBvbJ8BblW9Jb1hdaSreU0v4 # 6Mp79mwV+QMZDxGFqk+av6pX3WDG9XEg9FGomsrp0es0Rz11+iLsVT9qGTlrEOla # P470I3gwsvKmOMs1jaqYWSRAuDpnpAdfoP7YO0kT+wzh7Qttg1DO8H8+4NkI6Iwh # SkHC3uuOW+4Dwx1ubuZUNWZncnwa6lL2IsRyP64wggd6MIIFYqADAgECAgphDpDS # AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK # V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 # IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0 # ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla # MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS # ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT # H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB # AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG # OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S # 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz # y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7 # 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u # M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33 # X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl # XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP # 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB # l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF # RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM # CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ # BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud # DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO # 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0 # LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p # Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y # Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB # FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw # cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA # XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY # 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj # 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd # d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ # Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf # wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ # aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j # NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B # xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96 # eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7 # r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I # RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIWbzCCFmsCAQEwgZUwfjELMAkG # A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx # HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z # b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAQNeJRyZH6MeuAAAAAABAzAN # BglghkgBZQMEAgEFAKCB2DAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor # BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgcFbsP8Rq # TSsOVfu+s8r3OJ8ZVdTwo/Cd7Hg54yWTjugwbAYKKwYBBAGCNwIBDDFeMFygMIAu # AE0AaQBjAHIAbwBzAG8AZgB0ACAAUwB5AHMAdABlAG0AIABDAGUAbgB0AGUAcqEo # gCZodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vU3lzdGVtQ2VudGVyIDANBgkqhkiG # 9w0BAQEFAASCAQADnpFjiTloqk5V9kfRV+UsXvR7AW/NuPscz0F+o3bye8v3K7Sh # 0LH+fUE/TWL7IoP62ebbebp0lPCISiauOmkrKUM7CLNsnGE0W1d+yu4Dez467lMh # Y4xD5HiwPJxbhU5RYpoTvvqBtFIxRZQfQJVK8hTehtO9lYqTHZN+HAbxjPs3FwF5 # gLW7a0IohuP9ebPDgZV/kYzDWHptjI8iIGN1xfZK5vaKXM5CG/QHpKtNXHyjaFRi # 0xdtmtdIEPZDWpgdfF9w2lEorM2lkSSjxL8ABUsj5rR7FGF9qu4MHDqU3UNuMFHs # MRj4B1g+CpGVqHp+YVb8j/Tl2GSZvwqL/iZtoYITzzCCE8sGCisGAQQBgjcDAwEx # ghO7MIITtwYJKoZIhvcNAQcCoIITqDCCE6QCAQMxDzANBglghkgBZQMEAgEFADCC # AVgGCyqGSIb3DQEJEAEEoIIBRwSCAUMwggE/AgEBBgorBgEEAYRZCgMBMDEwDQYJ # YIZIAWUDBAIBBQAEIOp0tHy2xDS2PWFabVsBgwP6Xi0db7rmpAprihmc079ZAgZb # iBHO5a8YEzIwMTgwOTEwMTAwNDIwLjc3MVowBwIBAYACAfSggdSkgdEwgc4xCzAJ # BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jv # c29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNT # IEVTTjo5OEZELUM2MUUtRTY0MTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3Rh # bXAgU2VydmljZaCCDx8wggT1MIID3aADAgECAhMzAAAAy194yyMOlJfDAAAAAADL # MA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5n # dG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9y # YXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMB4X # DTE4MDgyMzIwMjYyNFoXDTE5MTEyMzIwMjYyNFowgc4xCzAJBgNVBAYTAlVTMRMw # EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN # aWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRp # b25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo5OEZELUM2 # MUUtRTY0MTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZTCC # ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMV4yB3v8B1BcBxtNEo/VALK # GnizA1WCEIU22DCpyy838O0VlW7D3KUomZPIU3nsx3MxaQXpai0OiVs+DPuHqdoK # tsuYCaMxeDHhodCgPWdPT9NN0hngnC07R2nDB2NhvtRBpr4V36791Pqi3CssKDdL # jBrOQUhqEn8S0VP5xldDQPfMIpqRFQdP6Ut4dvaI/Mva5e86HbawJxdGKrTdHp7L # Oae3YHX25khbhuNatqp3dDu3Do6xDE1BIa2GuUGZa4oHVNwWIWk3SZ4xZlarT3eA # i712yWyeTrjGv56Ryje8yDiBtd+1UCn67t0TwQpTa+a2ZPP2v8HyQxQegc+9ThUC # AwEAAaOCARswggEXMB0GA1UdDgQWBBQo5PLm9snRTa5uyNsqlr8xw/vZdjAfBgNV # HSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBHhkVo # dHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNUaW1T # dGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAC # hj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0YVBD # QV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUF # BwMIMA0GCSqGSIb3DQEBCwUAA4IBAQCL9GGFgwVibMsUlJfD6SUDbHKxL9pN6ZYM # g+aOTE8AyCh9oD6HcuinUjkj6afQU63TvgVRWExYJLzrQBysAh2GgbGkKIPtdV6y # QQMlJxclXpR48t1jS1VvBX0KksR5Bq/4/0e58+jXvUaU2JcUQVw3lHn9I/YtQJeu # AvnNfLENxJKE3A7FOjOAw+fEH49OGK1IBR9yhXS+r6HslFuFLfjK7DU89+Cu1zAg # 9JTCCrqlWSydWApAYh/ACInONLHHp9OZdilC42zGjB8Ro/07YqMAjPhK7Ze12lWT # hiZIFqc5fZTxCi3L2T8pQI91/Nxu4CnpIzLXUwSXUxkIpfSNsK7OMIIGcTCCBFmg # AwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMx # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3Qg # Q2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcNMjUw # NzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQ # MA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u # MSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIwDQYJ # KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0VBDV # pQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEwRA/x # YIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQedGFn # kV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKxXf13 # Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4GkbaI # CDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEAAaOC # AeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7fEYb # xTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYw # DwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvXzpoY # xDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtp # L2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYIKwYB # BQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20v # cGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0gAQH/ # BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93d3cu # bWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYBBQUH # AgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUAbQBl # AG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOhIW+z # 66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS+7lT # jMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlKkVIA # rzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon/VWv # L/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOiPPp/ # fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/fmNZ # JQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCIIYdqw # UB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0cs0d # 9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7aKLix # qduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQcdeh # 0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+NR4I # uto229Nfj950iEkSoYIDrTCCApUCAQEwgf6hgdSkgdEwgc4xCzAJBgNVBAYTAlVT # MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVy # YXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo5OEZE # LUM2MUUtRTY0MTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vydmlj # ZaIlCgEBMAkGBSsOAwIaBQADFQC5o5PSQHbRtx8VowRRl644K9uaIaCB3jCB26SB # 2DCB1TELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT # B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEpMCcGA1UE # CxMgTWljcm9zb2Z0IE9wZXJhdGlvbnMgUHVlcnRvIFJpY28xJzAlBgNVBAsTHm5D # aXBoZXIgTlRTIEVTTjo1N0Y2LUMxRTAtNTU0QzErMCkGA1UEAxMiTWljcm9zb2Z0 # IFRpbWUgU291cmNlIE1hc3RlciBDbG9jazANBgkqhkiG9w0BAQUFAAIFAN9Ad/Aw # IhgPMjAxODA5MTAxMjU2MTZaGA8yMDE4MDkxMTEyNTYxNlowdDA6BgorBgEEAYRZ # CgQBMSwwKjAKAgUA30B38AIBADAHAgEAAgIU6zAHAgEAAgIWpDAKAgUA30HJcAIB # ADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMBoAowCAIBAAIDFuNgoQow # CAIBAAIDB6EgMA0GCSqGSIb3DQEBBQUAA4IBAQCQNbEJlYUpSLy39rCmIhHOxn6L # 7dxJ+UtC0IokKI4JYm7wgV9d8tBe0iGbA1IZdR1lfnt7iPGXQDwyFvc7MA2Ndu/V # DrruUiZtLbQ87vAaxwCQ/RdJHkIoJjjOIDv9UKwkyH1MOtuuf49VX+ZYvF7tVz3S # HChsPffGG3WpN/3lL/PyGdeaCWvc4qzDTfc8PnXXHSpI8fc56h32HfsUEzHXuidT # d6iakoZkkfmqVsx9VrIDvKiPSiigU90w0W1GUheY/9VKOrrsH7Sg9JbR1bpcwABq # Y140G1cULPhK7SGF6KE333Jm5Wixzu2ePTDTfORyM3Fij/3OIV8ZPXHKtdACMYID # DTCCAwkCAQEwgZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x # EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv # bjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAADL # X3jLIw6Ul8MAAAAAAMswDQYJYIZIAWUDBAIBBQCgggFKMBoGCSqGSIb3DQEJAzEN # BgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQxIgQgmJ7xUUlWd+g3nNKvMH81Vw+I # l6RYuBy6iZYk6Ivvo3IwgfoGCyqGSIb3DQEJEAIvMYHqMIHnMIHkMIG9BCA2JyGq # qWCnXutz0KS9S3wuF/afS9Mu7hRHXqpg3cEdZDCBmDCBgKR+MHwxCzAJBgNVBAYT # AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBU # aW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAAy194yyMOlJfDAAAAAADLMCIEIF94UIgv # XMNDmgF6TuOsuWjS0VkxLAmBopLCk2o1lLkYMA0GCSqGSIb3DQEBCwUABIIBAI47 # W5ne9e0dEip8s/wpGkUfLQ8LaSOCQkyHkySnrLrUFoJnBjUQKkV1CrgDHi/1F4HT # ylZsyj51qNXEaPLr/CofgP4SAMqcS7N4JirL3F6PRuCkKafOk0phbvd/LiBjF67V # 4wzKm9T19Y4X2q31MXiXBWnrNK1r1UeLpfHL+s4maMc85emWOzXJhXlcxQrtcrsI # 5Gnsuxyg2IswI+s2X2a9h3gbz2VrsRhMABc8AY9pEDqOtuz5woUAXKO8RDEqT0C6 # tY78isSqyXYLZVZ25rydOugCCbkbDN02RpEnzQ1gnPN9LPSIXhE7kK9tMCZedByr # rpxuUdgaKIm63sBo56U= # SIG # End signature block ' + $pingTestHttpHeaders + $pingTestStringHttpBody + '