Skip to content

Latest commit

 

History

1 Commit

Folders and files

Repository files navigation

☁️ Azure Application Insights Web Test Terraform Module

Provisions a single Azure Application Insights availability WebTest (azurerm_application_insights_web_test) that probes an HTTP(S) endpoint from one or more Azure regions and reports availability into an Application Insights component. Targets hashicorp/azurerm ~> 4.0.

Terraform azurerm Module Type Resources

🧩 Overview

  • 🎯 Creates one Application Insights availability WebTest that repeatedly probes an endpoint and records success/failure and response time.
  • 🌍 Runs the probe from multiple Azure geo-locations for global coverage, so a single-region blip does not read as an outage.
  • ✅ Ships active by default (enabled = true) with retries on (retry_enabled = true) at a 5-minute cadence (frequency = 300).
  • 🧪 Supports both ping (single-request) and multistep test kinds; ping is the default and recommended shape.
  • 🏷️ Carries the universal tags tail and an operational timeouts block.

💡 Why it matters: A synthetic availability test is the earliest signal that a public endpoint has gone dark or slow — often before a single user complains. Provisioning it as code keeps the probe cadence, region coverage, and alert-feeding metric consistent across every service in the estate, and keeps a disabled (silent) test from slipping into production unnoticed.

❤️ Support this project

If this module saves you time, please consider supporting its continued development:

🗺️ Where this fits in the family

graph TD
  rg["terraform-azurerm-resource-group"]
  ai["terraform-azurerm-application-insights"]
  wt["terraform-azurerm-application-insights-web-test"]
  ag["terraform-azurerm-monitor-action-group"]

  rg -->|"resource_group_name"| wt
  ai -->|"application_insights_id"| wt
  wt -->|"availability results"| ai
  wt -->|"failure metric"| ag

  classDef me fill:#0078D4,stroke:#004578,color:#ffffff;
  classDef target fill:#004578,stroke:#002a44,color:#ffffff;
  classDef sib fill:#eef3f8,stroke:#b9c7d6,color:#1b2733;

  class wt me;
  class ai target;
  class rg,ag sib;
Loading

The WebTest depends on an existing resource group and an Application Insights component; its results surface as availability data in that component, and a metric alert can route failures to a monitor action group.

🧬 What this module builds

graph LR
  subgraph inputs["Inputs"]
    v1["name / location"]
    v2["resource_group_name"]
    v3["application_insights_id"]
    v4["kind / geo_locations / frequency"]
    v5["configuration (XML)"]
  end

  res["azurerm_application_insights_web_test.this"]

  subgraph outputs["Outputs"]
    o1["id"]
    o2["name"]
    o3["synthetic_monitor_id"]
  end

  v1 -->|"identity"| res
  v2 -->|"placement"| res
  v3 -->|"parent component"| res
  v4 -->|"test definition"| res
  v5 -->|"probe spec"| res
  res -->|"emits"| o1
  res -->|"emits"| o2
  res -->|"emits"| o3

  classDef me fill:#0078D4,stroke:#004578,color:#ffffff;
  classDef io fill:#eef3f8,stroke:#b9c7d6,color:#1b2733;

  class res me;
  class v1,v2,v3,v4,v5,o1,o2,o3 io;
Loading

Resource inventory

Resource Count Role
azurerm_application_insights_web_test.this 1 The keystone availability WebTest, with an optional timeouts block.

✅ Provider / Versions

Requirement Value
Terraform >= 1.12.0
hashicorp/azurerm ~> 4.0
Provider block None in this module — the caller configures provider "azurerm", its auth, and the mandatory features {} block.

Schema notes that bite (verified against the live provider schema):

  • Force-new fields — changing any of name, resource_group_name, location, application_insights_id, or kind destroys and recreates the WebTest.
  • location must correlate with the parent Application Insights component's region; a mismatch is rejected by the platform.
  • frequency accepts only 300, 600, 900 seconds; kind accepts only ping or multistep.
  • The provider's timeout argument (the test's own response timeout in seconds) is surfaced here as the test_timeout variable to avoid confusion with the operational timeouts CRUD block.
  • This Terraform resource maps to the Azure "Application Insights Classic Web Test"; the Terraform name is kept for backward compatibility.

🔑 Required Azure RBAC Roles / Permissions

  • Monitoring Contributor at the target resource group (or a narrower scope granting Microsoft.Insights/webtests/*). This is the least-privilege built-in role that can create, read, update, and delete WebTests. Contributor also works but is broader than necessary.

Azure Prerequisites

  • An existing resource group in a supported US Azure region.
  • An existing Application Insights component — its resource ID is a required input; the WebTest's location must correlate with the component's region.
  • The Microsoft.Insights resource provider registered on the target subscription (API version 2022-06-15).
  • The caller configures the provider "azurerm" { features {} } block, auth, and subscription; this module declares none of these.

📁 Module Structure

terraform-azurerm-application-insights-web-test/
├── providers.tf      # required_version >= 1.12.0; azurerm ~> 4.0; no provider block
├── variables.tf      # deeply-typed inputs; secure defaults; universal tags + timeouts tail
├── main.tf           # the keystone azurerm_application_insights_web_test.this renderer
├── outputs.tf        # id first, then name, synthetic_monitor_id, application_insights_id
├── README.md         # this document
├── SCOPE.md          # the cross-module contract
├── LICENSE           # MIT
└── .gitignore        # canonical library ignore set

⚙️ Quick Start

provider "azurerm" {
  features {}
}

module "web_test" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-application-insights-web-test.git?ref=v1.0.0"

  name                    = "wt-storefront-home"
  resource_group_name     = "rg-observability"
  location                = "eastus"
  application_insights_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-observability/providers/Microsoft.Insights/components/appi-storefront"

  geo_locations = ["us-tx-sn1-azr", "us-il-ch1-azr", "us-va-ash-azr"]

  configuration = <<-XML
    <WebTest Name="storefront-home" Enabled="True" Timeout="30" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
      <Items>
        <Request Method="GET" Url="https://www.contoso.com/" ExpectedHttpStatusCode="200" ParseDependentRequests="False" FollowRedirects="True" />
      </Items>
    </WebTest>
  XML
}

ℹ️ The caller owns provider configuration, authentication, and the mandatory features {} block. Pin the module with ?ref=v1.0.0 — never a branch.

🔌 Cross-Module Contract

Consumes

Input Type Typical source
resource_group_name string terraform-azurerm-resource-group (name)
location string caller / parent component region
application_insights_id string terraform-azurerm-application-insights (id)

Emits

Output Description
id WebTest Resource ID (emitted first).
name WebTest name.
synthetic_monitor_id Synthetic monitor ID correlating availability results.
application_insights_id Parent Application Insights component ID.

📚 Example Library

Values these examples reference but do not create are declared inputs:

variable "workspace_id" {
  description = "workspace id of an existing resource these examples reference."
  type        = string
}
1 · Minimal active ping test

The smallest real call. Everything else takes a secure default: enabled = true, retry_enabled = true, kind = "ping", frequency = 300, test_timeout = 30.

module "web_test" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-application-insights-web-test.git?ref=v1.0.0"

  name                    = "wt-home"
  resource_group_name     = "rg-observability"
  location                = "eastus"
  application_insights_id = var.appinsights_id
  geo_locations           = ["us-tx-sn1-azr", "us-il-ch1-azr", "us-va-ash-azr"]

  configuration = <<-XML
    <WebTest Name="home" Enabled="True" Timeout="30" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
      <Items>
        <Request Method="GET" Url="https://www.contoso.com/" ExpectedHttpStatusCode="200" />
      </Items>
    </WebTest>
  XML
}

🔒 The probe targets an https:// URL so the test exercises the encrypted path.

2 · HTTPS endpoint asserting a 200 response

A ping test that explicitly asserts the healthy status code and follows redirects.

module "web_test" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-application-insights-web-test.git?ref=v1.0.0"

  name                    = "wt-api-health"
  resource_group_name     = "rg-observability"
  location                = "eastus2"
  application_insights_id = var.appinsights_id
  geo_locations           = ["us-ca-sjc-azr", "us-va-ash-azr"]

  configuration = <<-XML
    <WebTest Name="api-health" Enabled="True" Timeout="30" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
      <Items>
        <Request Method="GET" Url="https://api.contoso.com/healthz" ExpectedHttpStatusCode="200" FollowRedirects="True" ParseDependentRequests="False" />
      </Items>
    </WebTest>
  XML
}

💡 ParseDependentRequests="False" keeps the probe focused on the endpoint itself rather than its downstream assets.

3 · Broad multi-region coverage

Five geo-locations reduce false alerts from a single-region network event.

module "web_test" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-application-insights-web-test.git?ref=v1.0.0"

  name                    = "wt-home-global"
  resource_group_name     = "rg-observability"
  location                = "centralus"
  application_insights_id = var.appinsights_id

  geo_locations = [
    "us-tx-sn1-azr",
    "us-il-ch1-azr",
    "us-va-ash-azr",
    "us-ca-sjc-azr",
    "us-fl-mia-edge",
  ]

  configuration = <<-XML
    <WebTest Name="home-global" Enabled="True" Timeout="30" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
      <Items>
        <Request Method="GET" Url="https://www.contoso.com/" ExpectedHttpStatusCode="200" />
      </Items>
    </WebTest>
  XML
}

💡 Availability alert rules commonly fire when X of N locations fail; more locations gives a sturdier signal.

4 · Slower cadence for a low-churn endpoint

frequency accepts only 300, 600, or 900 seconds.

module "web_test" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-application-insights-web-test.git?ref=v1.0.0"

  name                    = "wt-marketing"
  resource_group_name     = "rg-observability"
  location                = "eastus"
  application_insights_id = var.appinsights_id
  geo_locations           = ["us-tx-sn1-azr", "us-va-ash-azr"]
  frequency               = 900

  configuration = <<-XML
    <WebTest Name="marketing" Enabled="True" Timeout="30" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
      <Items>
        <Request Method="GET" Url="https://marketing.contoso.com/" ExpectedHttpStatusCode="200" />
      </Items>
    </WebTest>
  XML
}

⚠️ Passing any value other than 300, 600, or 900 fails the input validation before any API call.

5 · Custom per-run response timeout

test_timeout maps to the provider's timeout argument — the seconds the probe waits for a response.

module "web_test" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-application-insights-web-test.git?ref=v1.0.0"

  name                    = "wt-slow-report"
  resource_group_name     = "rg-observability"
  location                = "eastus"
  application_insights_id = var.appinsights_id
  geo_locations           = ["us-tx-sn1-azr", "us-va-ash-azr"]
  test_timeout            = 60

  configuration = <<-XML
    <WebTest Name="slow-report" Enabled="True" Timeout="60" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
      <Items>
        <Request Method="GET" Url="https://reports.contoso.com/daily" ExpectedHttpStatusCode="200" />
      </Items>
    </WebTest>
  XML
}

ℹ️ Keep the XML Timeout attribute aligned with test_timeout so the definition and the resource agree.

6 · Staged test (disabled until cutover)

Provision the test but hold it inactive until the endpoint is live.

module "web_test" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-application-insights-web-test.git?ref=v1.0.0"

  name                    = "wt-preprod"
  resource_group_name     = "rg-observability"
  location                = "eastus"
  application_insights_id = var.appinsights_id
  geo_locations           = ["us-tx-sn1-azr"]
  enabled                 = false

  configuration = <<-XML
    <WebTest Name="preprod" Enabled="True" Timeout="30" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
      <Items>
        <Request Method="GET" Url="https://preprod.contoso.com/" ExpectedHttpStatusCode="200" />
      </Items>
    </WebTest>
  XML
}

⚠️ A disabled test is a silent monitoring gap. enabled defaults to true; setting it false is a deliberate opt-out you should track.

7 · First-failure alerting (retries off)

Turn retries off when you want the very first failure to raise the metric immediately.

module "web_test" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-application-insights-web-test.git?ref=v1.0.0"

  name                    = "wt-payments-critical"
  resource_group_name     = "rg-observability"
  location                = "eastus"
  application_insights_id = var.appinsights_id
  geo_locations           = ["us-tx-sn1-azr", "us-il-ch1-azr", "us-va-ash-azr"]
  retry_enabled           = false

  configuration = <<-XML
    <WebTest Name="payments-critical" Enabled="True" Timeout="30" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
      <Items>
        <Request Method="GET" Url="https://payments.contoso.com/status" ExpectedHttpStatusCode="200" />
      </Items>
    </WebTest>
  XML
}

💡 retry_enabled defaults to true to suppress transient blips; disable it only for endpoints where any failure is worth an immediate page.

8 · Description and tags for governance
module "web_test" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-application-insights-web-test.git?ref=v1.0.0"

  name                    = "wt-home-tagged"
  resource_group_name     = "rg-observability"
  location                = "eastus"
  application_insights_id = var.appinsights_id
  geo_locations           = ["us-tx-sn1-azr", "us-va-ash-azr"]
  description             = "Storefront home page availability, owned by the platform team."

  tags = {
    environment = "prod"
    owner       = "platform"
    cost_center = "1234"
  }

  configuration = <<-XML
    <WebTest Name="home-tagged" Enabled="True" Timeout="30" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
      <Items>
        <Request Method="GET" Url="https://www.contoso.com/" ExpectedHttpStatusCode="200" />
      </Items>
    </WebTest>
  XML
}
9 · Asserting a non-200 status code

An auth-gated endpoint that should return 401 when reached anonymously is still "up" if it answers with 401.

module "web_test" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-application-insights-web-test.git?ref=v1.0.0"

  name                    = "wt-admin-gate"
  resource_group_name     = "rg-observability"
  location                = "eastus"
  application_insights_id = var.appinsights_id
  geo_locations           = ["us-tx-sn1-azr", "us-va-ash-azr"]

  configuration = <<-XML
    <WebTest Name="admin-gate" Enabled="True" Timeout="30" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
      <Items>
        <Request Method="GET" Url="https://admin.contoso.com/" ExpectedHttpStatusCode="401" />
      </Items>
    </WebTest>
  XML
}

🔒 Never embed credentials in the WebTest XML — assert the challenge response instead of authenticating.

10 · Content match on the response body

A ping test can validate a marker string in the response using a validation rule.

module "web_test" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-application-insights-web-test.git?ref=v1.0.0"

  name                    = "wt-content-match"
  resource_group_name     = "rg-observability"
  location                = "eastus"
  application_insights_id = var.appinsights_id
  geo_locations           = ["us-tx-sn1-azr", "us-va-ash-azr"]

  configuration = <<-XML
    <WebTest Name="content-match" Enabled="True" Timeout="30" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
      <Items>
        <Request Method="GET" Url="https://www.contoso.com/status" ExpectedHttpStatusCode="200">
          <ValidationRules>
            <ValidationRule Classname="Microsoft.VisualStudio.TestTools.WebTesting.Rules.ValidationRuleFindText, Microsoft.VisualStudio.QualityTools.WebTestFramework" DisplayName="Find Text" Description="Verifies the presence of the specified text in the response." Level="High" ExecutionOrder="BeforeDependents">
              <RuleParameters>
                <RuleParameter Name="FindText" Value="OK" />
                <RuleParameter Name="IgnoreCase" Value="False" />
                <RuleParameter Name="UseRegularExpression" Value="False" />
                <RuleParameter Name="PassIfTextFound" Value="True" />
              </RuleParameters>
            </ValidationRule>
          </ValidationRules>
        </Request>
      </Items>
    </WebTest>
  XML
}

💡 A content match catches a "200 but wrong page" failure that a bare status-code assertion misses.

11 · Custom CRUD timeouts

The universal timeouts block tunes how long Terraform waits on each operation — unrelated to test_timeout.

module "web_test" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-application-insights-web-test.git?ref=v1.0.0"

  name                    = "wt-home-timeouts"
  resource_group_name     = "rg-observability"
  location                = "eastus"
  application_insights_id = var.appinsights_id
  geo_locations           = ["us-tx-sn1-azr"]

  configuration = <<-XML
    <WebTest Name="home-timeouts" Enabled="True" Timeout="30" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
      <Items>
        <Request Method="GET" Url="https://www.contoso.com/" ExpectedHttpStatusCode="200" />
      </Items>
    </WebTest>

  timeouts = {
    create = "30m"
    read   = "5m"
    update = "30m"
    delete = "30m"
  }
}
12 · Multistep test kind

Set kind = "multistep" for a legacy multi-request test. kind is force-new.

module "web_test" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-application-insights-web-test.git?ref=v1.0.0"

  name                    = "wt-checkout-flow"
  resource_group_name     = "rg-observability"
  location                = "eastus"
  application_insights_id = var.appinsights_id
  geo_locations           = ["us-tx-sn1-azr", "us-va-ash-azr"]
  kind                    = "multistep"

  configuration = var.multistep_webtest_xml
}

ℹ️ ping is the recommended shape for new tests; multistep is retained for backward compatibility. Changing kind after creation replaces the resource.

13 · A fleet of tests with for_each

Drive many endpoints from one keyed map. Stable keys keep additions from re-indexing the rest.

variable "endpoints" {
  type = map(object({
    url      = string
    location = string
  }))
}

module "web_test" {
  source   = "git::https://github.com/microsoftexpert/terraform-azurerm-application-insights-web-test.git?ref=v1.0.0"
  for_each = var.endpoints

  name                    = "wt-${each.key}"
  resource_group_name     = "rg-observability"
  location                = each.value.location
  application_insights_id = var.appinsights_id
  geo_locations           = ["us-tx-sn1-azr", "us-va-ash-azr"]

  configuration = <<-XML
    <WebTest Name="${each.key}" Enabled="True" Timeout="30" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
      <Items>
        <Request Method="GET" Url="${each.value.url}" ExpectedHttpStatusCode="200" />
      </Items>
    </WebTest>
  XML

  tags = { managed_by = "platform" }
}

💡 Keying on a stable name (not a list index) means removing one endpoint never disturbs the others' state.

14 · 🏗️ End-to-end composition

Wire the sibling resource-group and Application Insights modules into this one, feeding the component's id output straight into application_insights_id.

provider "azurerm" {
  features {}
}

module "resource_group" {
  source   = "git::https://github.com/microsoftexpert/terraform-azurerm-resource-group.git?ref=v1.0.0"
  name     = "rg-observability"
  location = "eastus"
}

module "application_insights" {
  source              = "git::https://github.com/microsoftexpert/terraform-azurerm-application-insights.git?ref=v1.0.0"

  workspace_id                 = var.workspace_id
  name                = "appi-storefront"
  resource_group_name = module.resource_group.name
  location            = module.resource_group.location
  application_type    = "web"
}

module "web_test" {
  source = "git::https://github.com/microsoftexpert/terraform-azurerm-application-insights-web-test.git?ref=v1.0.0"

  name                    = "wt-storefront-home"
  resource_group_name     = module.resource_group.name
  location                = module.resource_group.location
  application_insights_id = module.application_insights.id

  geo_locations = ["us-tx-sn1-azr", "us-il-ch1-azr", "us-va-ash-azr"]

  configuration = <<-XML
    <WebTest Name="storefront-home" Enabled="True" Timeout="30" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
      <Items>
        <Request Method="GET" Url="https://www.contoso.com/" ExpectedHttpStatusCode="200" FollowRedirects="True" />
      </Items>
    </WebTest>
  XML

  tags = {
    environment = "prod"
    owner       = "platform"
  }
}

💡 location is taken from the resource group so it correlates with the Application Insights component's region — a mismatch would be rejected by the platform.

📥 Inputs

Identity (all force-new) — name, resource_group_name, location, application_insights_id. Test definition — geo_locations (required list), configuration (required XML), kind (default ping). Behaviour — frequency (default 300), test_timeout (default 30), enabled (default true), retry_enabled (default true), description. Universal tail — tags, timeouts.

Full input schemas
Name Type Default Required Notes
name string — yes Force-new.
resource_group_name string — yes Existing RG; force-new.
location string — yes Force-new; must match parent component region.
application_insights_id string — yes Parent component ID; force-new.
geo_locations list(string) — yes At least one population tag (validated).
configuration string — yes XML WebTest definition.
kind string "ping" no One of ping, multistep (validated); force-new.
frequency number 300 no One of 300, 600, 900 (validated).
test_timeout number 30 no A whole number of seconds greater than zero. Maps to the provider's timeout. The provider applies no ValidateFunc to this field and publishes no upper bound, so only the shape is checked here — an earlier version of this module refused anything above 120, which rejected values the provider accepts.
enabled bool true no Active by default.
retry_enabled bool true no Retries on by default.
description string null no Free-text description.
tags map(string) {} no Resource tags.
timeouts object({ create, read, update, delete }) null no Operational CRUD timeouts, each optional(string).
variable "timeouts" {
  type = object({
    create = optional(string)
    read   = optional(string)
    update = optional(string)
    delete = optional(string)
  })
  default = null
}

🧾 Outputs

Output Description Kind
id Resource ID of the Application Insights WebTest Passthrough
name Name of the Application Insights WebTest Passthrough
location Azure region the resource is deployed in, in the canonical form Azure uses Passthrough
synthetic_monitor_id The synthetic monitor ID of the WebTest, used to correlate availability results in Application Insights Passthrough
application_insights_id Resource ID of the Application Insights component this WebTest reports into Passthrough
kind The kind of test: "ping" or "multistep", lower-case Passthrough
geo_locations The population tags the test runs from, as stored Passthrough
geo_location_count How many locations the test runs from Passthrough
frequency Interval in seconds between runs: 300, 600 or 900 Passthrough
test_timeout Seconds until a run is marked failed Passthrough
enabled Whether the test is actively running Passthrough
timeout_exceeds_run_interval True when test_timeout is greater than or equal to frequency — a configuration in which a slow run can still be in flight when the next one is due, so runs overlap Passthrough
runs_from_fewer_than_five_locations True when the test probes from fewer than five geo-locations Passthrough
collects_nothing_while_disabled Always true, and worth stating because the failure is silent Constant
location_must_match_the_parent_component Always true, and unverifiable from here Constant
configuration_is_unvalidated_xml Always true Constant
every_identifying_argument_is_force_new Always true Constant

No secret is emitted; the resource carries no secret-bearing attribute.

🧠 Architecture Notes

  • Force-new identity. name, resource_group_name, location, application_insights_id, and kind are immutable — Terraform replaces the WebTest if any changes. Treat them as the resource's fixed identity and plan renames as create/destroy.
  • Region correlation. The WebTest location must correlate with the parent Application Insights component's region. Sourcing location from the same value that built the component (as in the end-to-end example) avoids a platform rejection.
  • Two different "timeouts". The provider exposes a scalar timeout (the test's own response timeout) and a timeouts CRUD block. This module names the former test_timeout and keeps the latter as the universal timeouts tail, rendered through a dynamic block with try(..., null) on each field so an unset block adds nothing to the plan.
  • configuration is the probe. The XML defines the request, expected status code, and any validation rules. It is not a secret and must never carry credentials; assert challenge responses (e.g. 401) instead of authenticating.
  • Classic Web Test. This maps to the Azure "Application Insights Classic Web Test". Newer standard availability tests are a separate resource type; if you need that shape, do not expect this resource to model it.
  • for_each key stability. When fanning the module out over a map of endpoints, key on a stable name so removing one entry never re-indexes the others.

🧱 Design Principles

Concern Secure/sensible default (empty call) Opt-out (caller must type it)
Test activation enabled = true — the test is actively monitored set enabled = false
Failure resilience retry_enabled = true — a failed run is retried before alerting set retry_enabled = false
Probe transport examples target https:// endpoints point configuration at an http:// URL
Credentials in the probe none — assert challenge responses (not supported; do not embed secrets)
Test shape kind = "ping" — the recommended availability probe set kind = "multistep"
Cadence frequency = 300 (5 minutes) set 600 or 900

Rules of thumb: a boolean that gates monitoring defaults to the active/resilient value; an enum defaults to the recommended member and is enforced with a validation {} block naming the legal values.

🚀 Runbook

terraform init -backend=false
terraform validate
terraform fmt -check
  • Pin the module with ?ref=v1.0.0 in source — never a branch.
  • This library is plan-only: a human applies from CI after review. Do not run terraform apply as part of authoring.

🧪 Testing

The offline proof gate is what this module guarantees without touching a cloud:

Check Command What it proves
Parse + type terraform validate The typed inputs, validation {} blocks, and resource references are internally consistent — a malformed call fails here, before any API call.
Formatting terraform fmt -check Canonical HCL formatting.
Provider resolution terraform init -backend=false The azurerm ~> 4.0 pin resolves and the schema loads.

What only terraform plan/apply against a real subscription can exercise: region correlation with the parent component, the Microsoft.Insights provider registration, and RBAC on the target scope.

💬 Example Output

$ terraform output
id                      = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-observability/providers/Microsoft.Insights/webtests/wt-storefront-home"
name                    = "wt-storefront-home"
synthetic_monitor_id    = "wt-storefront-home"
application_insights_id = "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-observability/providers/Microsoft.Insights/components/appi-storefront"

🔍 Troubleshooting

Symptom Cause Fix
Plan shows the WebTest being destroyed and recreated after a small edit A force-new field (name, resource_group_name, location, application_insights_id, kind) changed Confirm the change is intended; if only renaming, expect replacement, or use terraform state mv for a pure rename that keeps the same Azure resource.
frequency/kind rejected at plan time Value outside the allowed set Use frequency ∈ {300, 600, 900} and kind ∈ {ping, multistep}.
Apply fails on a location mismatch WebTest location differs from the parent component's region Set location to the same region as the Application Insights component.
Apply fails with an authorization error Caller identity lacks WebTest write permission Grant Monitoring Contributor at the target resource group scope.
provider "azurerm" won't initialize Missing caller-side features {} block Add provider "azurerm" { features {} } to the root module.
Test reports failures for a healthy endpoint Too few geo-locations, or retries off Add more geo_locations, keep retry_enabled = true, and align the XML Timeout with test_timeout.

🔗 Related Docs

💙 "Infrastructure as Code should be standardized, consistent, and secure."