How to create App Insights Availability Tests with bicep

How to create App Insights Availability Tests with bicep

Introduction

I found it rather difficult to create a classic availability test by bicep-lang, as it uses an embedded WebTest XML string that doesn't seem to be documented anywhere.

The successor of the classic URL ping test produces relief in that respect as it doesn't use the legacy WebTest format anymore. However, even there is a minor obstacle to be aware of.

Reasons for creating a web test might be obvious - you'd like to monitor the response time and availability of your HTTP endpoint from different callers spread around the globe.

However, in my specific case, I was looking for an alternative solution that mimics the Azure App Service's AlwaysOn functionality.

The thing is, the AlwaysOn feature won't let you configure a path to keep your web application warm and keeps calling the root URL. If your application doesn't provide a controller on that path, you will fill up your App Insight logs with errors 😑.

Azure provides for types of availability tests, which are:

  • URL Ping Test (classic)
  • Standard Test (preview)
  • Multi-Step Web Test (classic)
  • Custom TrackAvailability Test

This article will provide examples for the first two tests. So without further ceremony here are the templates.  

Creating a URL Ping Test (Classic)

As mentioned in the introduction the classic test requires a WebTest XML string. The example below will send an HTTP GET request to www.azureblue.io and expect a status code of 200.

<WebTest
    xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010" 
         Name="demo-classic-test" 
         Id="245c86e6-3488-461d-92f9-62be82435d7d" 
         Enabled="True" 
         CssProjectStructure="" 
         CssIteration="" 
         Timeout="120" 
         WorkItemIds="" 
         Description="" 
         CredentialUserName="" 
         CredentialPassword="" 
         PreAuthenticate="True" 
         Proxy="default" 
         StopOnError="False" 
         RecordedResultFile="" 
         ResultsLocale="">
    <Items>
        <Request Method="GET" 
               Guid="aa29472a-53b2-1b60-cd39-26c0efc17bb2" 
               Version="1.1" 
               Url="https://www.azureblue.io" 
               ThinkTime="0" 
               Timeout="120" 
               ParseDependentRequests="False" 
               FollowRedirects="True" 
               RecordResult="True" 
               Cache="False" 
               ResponseTimeGoal="0" 
               Encoding="utf-8" 
               ExpectedHttpStatusCode="200" 
               ExpectedResponseUrl="" 
               ReportingName="" 
               IgnoreHttpStatusCode="False" />
    </Items>
</WebTest>
WebTest.xml

Now let's put that into place. I have carved out the relevant parameters and used string interpolation to build the XML part.

var name = 'demo-url-ping-web-test'
var rg = 'rg-article'
var subscriptionId = '<your_subscription_id>'
var appInsightsName = 'appi-demo'

var id = guid('seed')
var timeout = 120
var frequency = 300
var guidId = guid('seed')
var method = 'GET'
var url = 'https://www.azureblue.io'
var expectedHttpStatusCode = 200
var version = '1.1'
var followRedirects = 'True'
var recordResults = 'True'
var cache = 'False'
var parseDependentRequests = 'False'
var ignoreHttpStatusCode = 'False'

resource urlPingWebTest 'Microsoft.Insights/webtests@2015-05-01' = {
  name: name
  location: 'westeurope'
  tags: {
    'hidden-link:/subscriptions/${subscriptionId}/resourceGroups/${rg}/providers/microsoft.insights/components/${appInsightsName}': 'Resource'
  }
  kind: 'ping'
  properties: {
    Configuration: {
      WebTest: '<WebTest xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010" Name="${name}" Id="${id}" Enabled="True" CssProjectStructure="" CssIteration="" Timeout="${timeout}" WorkItemIds="" Description="" CredentialUserName="" CredentialPassword="" PreAuthenticate="True" Proxy="default" StopOnError="False" RecordedResultFile="" ResultsLocale=""> <Items> <Request Method="${method}" Guid="${guidId}" Version="${version}" Url="${url}" ThinkTime="0" Timeout="${timeout}" ParseDependentRequests="${parseDependentRequests}" FollowRedirects="${followRedirects}" RecordResult="${recordResults}" Cache="${cache}" ResponseTimeGoal="0" Encoding="utf-8" ExpectedHttpStatusCode="${expectedHttpStatusCode}" ExpectedResponseUrl="" ReportingName="" IgnoreHttpStatusCode="${ignoreHttpStatusCode}" /> </Items> </WebTest>'
    }
    Description: 'Runs a classic URL ping test'    
    Enabled: true
    Frequency: frequency
    Kind: 'ping'
    Locations: [
      {
        Id: 'emea-nl-ams-azr'
      }
      {
        Id: 'emea-ru-msa-edge'
      }
      {
        Id: 'apac-hk-hkn-azr'
      }
      {
        Id: 'latam-br-gru-edge'
      }
      {
        Id: 'emea-au-syd-edge'
      }
    ]
    Name: name
    RetryEnabled: true 
    SyntheticMonitorId: '${name}-id'
    Timeout: timeout
  }
}
Create a URL Ping Test (Classic) with bicep-lang

One way to deploy the template is with Azure CLI.

$ az deployment group create --name rollout01 --resource-group rg-article --subscription "your subscription" --template-file urlpingwebtest.bicep

Bicep supports multi-line strings since version v0.3 and making use of it would enhance the readability of the XML string. However, then we can't interpolate variables anymore as multi-line strings are considered verbatim.

The good news is, we don't need to escape quotes with bicep-lang, as opposed to the JSON-based templates.

☝🏼 Did you know? You can transform a multi-line XML document into a single line with VS Code's Join Lines feature.

One important thing to note is that the ominous hidden-link tag clues the web test to your Application Insights resource.

If you wonder what the location IDs a referring to have a look at the following table, which I took out of the official documentation.

Display Name Population Name
Australia East emea-au-syd-edge
Brazil South latam-br-gru-edge
Central US us-fl-mia-edge
East Asia apac-hk-hkn-azr
East US us-va-ash-azr
France South (Formerly France Central) emea-ch-zrh-edge
France Central emea-fr-pra-edge
Japan East apac-jp-kaw-edge
North Europe emea-gb-db3-azr
North Central US us-il-ch1-azr
South Central US us-tx-sn1-azr
Southeast Asia apac-sg-sin-azr
UK West emea-se-sto-edge
West Europe emea-nl-ams-azr
West US us-ca-sjc-azr
UK South emea-ru-msa-edge

Standard Test

If you are with me and don't like to fiddle around with embedded XML documents you are going to like the successor to the URL Ping Test.

Here is an example, that does the same as the classic URL Ping Test from above. Every 5 minutes it sends an HTTP GET request to www.azureblue.io and expects a status code 200.

resource standardWebTest 'Microsoft.Insights/webtests@2018-05-01-preview' = {
  name: 'demo-webtest'
  location: 'westeurope'
  tags: {
    'hidden-link:/subscriptions/ade29918-737f-497e-8808-4bffda5cc46d/resourceGroups/rg-article/providers/microsoft.insights/components/appi-demo': 'Resource'
  }
  kind: 'standard'
  properties: {
    SyntheticMonitorId: 'demo-webtest-id'
    Name: 'demo-webtest'
    Description: null
    Enabled: true
    Frequency: 300
    Timeout: 120 
    Kind: 'standard'
    RetryEnabled: true
    Locations: [
      {
        Id: 'emea-nl-ams-azr'
      }
      {
        Id: 'emea-ru-msa-edge'
      }
      {
        Id: 'apac-hk-hkn-azr'
      }
      {
        Id: 'latam-br-gru-edge'
      }
      {
        Id: 'emea-au-syd-edge'
      }
    ]
    Configuration: null
    Request: {
      RequestUrl: 'https://www.azureblue.io'
      Headers: null
      HttpVerb: 'GET'
      RequestBody: null
      ParseDependentRequests: false
      FollowRedirects: null
    }
    ValidationRules: {
      ExpectedHttpStatusCode: 200
      IgnoreHttpsStatusCode: false
      ContentValidation: null
      SSLCheck: true
      SSLCertRemainingLifetimeCheck: 7
    }
  }
}

As you can see, the XML is gone. Also, we get some additional features like TLS certificate validation checks.

Please also note, that I am using the undocumented 2018-05-01-preview API version. This contradicts the official documentation, which suggests using 2020-10-05-preview. Following that advice results in an NoRegisteredProviderFound error.

For once it seems the implementation is lacking behind the documentation and not the other way round 😄

{
    "code": "DeploymentFailed",
    "message": "At least one resource deployment operation failed. Please list deployment operations for details. Please see https://aka.ms/DeployOperations for usage details.",
    "details": [
        {
            "code": "NoRegisteredProviderFound",
            "message": "No registered resource provider found for location 'westeurope' and API version '2020-10-05-preview' for type 'webtests'. The supported api-versions are '2014-04-01, 2014-08-01, 2014-12-01-preview, 2015-05-01, 2018-05-01-preview'. The supported locations are ', eastus, southcentralus, northeurope, westeurope, southeastasia, westus2, uksouth, centralindia, canadacentral, japaneast, australiaeast, koreacentral, francecentral, centralus, eastus2, eastasia, westus, southafricanorth, northcentralus, brazilsouth, switzerlandnorth, norwayeast, norwaywest, australiasoutheast, australiacentral2, germanywestcentral, switzerlandwest, uaecentral, ukwest, brazilsoutheast, japanwest, uaenorth, australiacentral, francesouth, southindia, westus3, koreasouth, canadaeast, jioindiawest'."
        }
    ]
}

Feature comparison

Here is a list that compares the classic URL Ping Test to the newer Standard Test

Feature URL Ping Test (classic) Standard Test (preview)
Check if endpoint is responding x x
Measure time in which endpoint is responding x x
Create alerts based on results x x
SSL Certificate Validiation Checks x
Proactive Lifetime check x
Choose HTTP Request Verb x
Custom Headers x
Custom Data associated with HTTP Request x

Summary & Conclusion

  • The classic URL Ping Test requires a WebTest XML string, which is undocumented and sucks 🤪
  • The successor standard test is still in preview but makes the embedded XML obsolete, which rocks 🤘🏻
  • When creating a standard test, you currently need to use the undocumented API version 2018-05-01-preview instead of 2020-10-05-preview.

I hope this post saved you some headache and as always I'd love to receive feedback! Thanks for reading 😊

Further reading

Application Insights availability tests - Azure Monitor
Set up recurring web tests to monitor availability and responsiveness of your app or website.
Microsoft.Insights/webtests 2015-05-01 - Bicep & ARM template reference
Azure Microsoft.Insights/webtests syntax and properties to use in Azure Resource Manager templates for deploying the resource. API version 2015-05-01
GitHub - matthiasguentert/bicep-webtest-article
Contribute to matthiasguentert/bicep-webtest-article development by creating an account on GitHub.