# Monitor a shop with Terraform - Checkly Docs

> Declare a shop's whole monitoring stack in Terraform with the Checkly provider, from the alert channel and check group to Browser Checks loaded from spec files and an API check with body assertions.

Source: https://www.checklyhq.com/docs/guides/monitoring-ecommerce-apps-using-terraform/

---

- Step 1: Configure the provider
- Step 2: Decide who hears about failures
- Step 3: Put shared settings in one group
- Step 4: Monitor the shop’s flows with Browser Checks
- Step 5: Assert on the product API
- Step 6: Plan, then apply
- Verify it works
- Next
- Reference

Examples

# Monitor a shop with Terraform

Declare a shop’s whole monitoring stack in Terraform with the Checkly provider, from the alert channel and check group to Browser Checks loaded from spec files and an API check with body assertions.

By the end of this guide, your shop’s monitoring lives in five Terraform resources: an email alert channel, a check group that owns locations, retries, and alerting, two Browser Checks loaded from Playwright spec files, and an API check that asserts on the response body.
Terminal

```
# checkly_alert_channel.shop_email will be created
# checkly_check.books_api will be created
# checkly_check.browser["home"] will be created
# checkly_check.browser["search"] will be created
# checkly_check_group_v2.shop will be created

Plan: 5 to add, 0 to change, 0 to destroy.
```

To follow along without your own app, clone the [sample project](https://github.com/checkly/docs/tree/main/samples/guides/terraform-shop). It monitors the [Danube demo shop](https://danube-web.shop/) and its `/api/books` endpoint.

Let your agent do it

To run this guide from your terminal or your coding agent, run `npx checkly init` in your project first. It installs the Checkly CLI and [Checkly Skills](https://www.checklyhq.com/docs/ai/skills) for your agent. Then paste the prompt below into Claude Code, Cursor, Codex, or any agent that supports skills. It builds the same setup as this guide, proves it with `npx checkly test --record`, and stops for your confirmation before `npx checkly deploy`. Prompt

```
Declare the monitoring for this shop in Terraform with the checkly/checkly provider, version "~> 1.0".

Success criteria:
1. `providers.tf` configures the provider from the variables `checkly_api_key` and `checkly_account_id`, read from TF_VAR_ environment variables. Never write credentials into a file.
2. `alerts.tf` creates an email alert channel for failures and recoveries. Ask me for the address and pass it as a variable.
3. `group.tf` creates a `checkly_check_group_v2` that enforces two locations, parallel runs, a linear retry strategy, and a run-based alert policy subscribed to the email channel. It sets a `SHOP_URL` environment variable from a `shop_url` variable.
4. `scripts/` holds one Playwright spec per shop flow. Each spec reads `process.env.SHOP_URL` with the production URL as the fallback and asserts on visible content, not just that a page loaded. `browser.tf` creates one Browser Check per spec with `for_each` and `script = file(...)`.
5. `api.tf` creates an API check for the product API that asserts the status code, the content type, and that the JSON body is not empty.
6. Run `npx playwright test`, `terraform fmt -check`, `terraform validate`, and `terraform plan`, and show me the results.
7. Stop. Wait for my confirmation before `terraform apply`.

Explain each file you changed and why.
```

For this guide, the agent proves the setup with `npx playwright test` and `terraform plan`, and stops before `terraform apply`. The steps below are what it does, in the open.

## ​ Step 1: Configure the provider

Start in an empty folder. The provider needs a Checkly user API key and the ID of the account to create resources in. Declare both as variables so they never land in a file you commit.
providers.tf

```
terraform {
required_providers {
checkly = {
source = "checkly/checkly"
version = "~> 1.0"
}
}
}

variable "checkly_api_key" {
type = string
sensitive = true
}

variable "checkly_account_id" {
type = string
}

provider "checkly" {
api_key = var.checkly_api_key
account_id = var.checkly_account_id
}
```

Terraform reads any variable from an environment variable prefixed with `TF_VAR_`. The [Terraform provider overview](https://www.checklyhq.com/docs/integrations/iac/terraform/overview) shows where to find both values in the app.
Terminal

```
export TF_VAR_checkly_api_key=" "
export TF_VAR_checkly_account_id=" "
terraform init
```

Terminal

```
Initializing provider plugins...
- Finding checkly/checkly versions matching "~> 1.0"...
- Installing checkly/checkly v1.29.0...
- Installed checkly/checkly v1.29.0 (signed by a HashiCorp partner, key ID 4E5AC4D95E185A57)

Terraform has been successfully initialized!
```

## ​ Step 2: Decide who hears about failures

A check that fails without telling anyone is a dashboard, not monitoring. Create the alert channel first, so everything after it can subscribe.
alerts.tf

```
variable "alert_email" {
type = string
description = "Where failure and recovery emails go."
}

resource "checkly_alert_channel" "shop_email" {
email {
address = var.alert_email
}

send_failure = true
send_recovery = true
send_degraded = false
}
```

Set the address the same way as the credentials, with `export TF_VAR_alert_email="you@yourcompany.com"`. Email is the simplest channel to start with. The same resource takes Slack, PagerDuty, Opsgenie, SMS, and webhook blocks; see [alert channels for Terraform](https://www.checklyhq.com/docs/integrations/iac/terraform/alerting).

## ​ Step 3: Put shared settings in one group

Every check for the shop should run from the same places, retry the same way, and alert the same people. Declare that once on a group instead of repeating it on every check.
group.tf

```
variable "shop_url" {
type = string
default = "https://danube-web.shop"
}

# Every check in this guide joins this group. The enforce blocks make the
# group, not each check, own locations, scheduling, retries, and alerting.
resource "checkly_check_group_v2" "shop" {
name = "Shop (Terraform)"
activated = true
tags = ["terraform-shop"]

enforce_locations {
enabled = true
locations = ["us-east-1", "eu-west-1"]
}

enforce_scheduling_strategy {
enabled = true
run_parallel = true
}

enforce_retry_strategy {
enabled = true
retry_strategy {
type = "LINEAR"
max_retries = 2
base_backoff_seconds = 30
same_region = false
}
}

enforce_alert_settings {
enabled = true

alert_settings {
escalation_type = "RUN_BASED"
run_based_escalation {
failed_run_threshold = 1
}
}

alert_channel_subscription {
channel_id = checkly_alert_channel.shop_email.id
activated = true
}
}

environment_variable {
key = "SHOP_URL"
value = var.shop_url
}
}
```

`checkly_check_group_v2` changes nothing about its checks unless you ask it to. Each `enforce_` block you add takes that setting away from the checks and applies the group’s value instead. Here the group runs every check from N. Virginia and Ireland in parallel, retries a failed run up to twice from a different region, and emails you as soon as one run still fails after its retries.
The `SHOP_URL` variable reaches every Browser Check in the group. Point the whole stack at a staging shop with `terraform plan -var="shop_url= "`, without touching a check.

## ​ Step 4: Monitor the shop’s flows with Browser Checks

Keep each flow in its own Playwright spec file. The file is the check’s script, and you can run it locally before Terraform ever sees it.

```
import { test, expect } from '@playwright/test'

// SHOP_URL comes from the check group in Terraform. Locally, it falls
// back to the production shop so the same file runs in both places.
const shopUrl = process.env.SHOP_URL ?? 'https://danube-web.shop'

test('home page lists the top sellers', async ({ page }) => {
await page.goto(shopUrl)

await expect(page.getByRole('heading', { name: 'Top sellers' })).toBeVisible()
await expect(page.locator('.preview').first()).toBeVisible()
})
```

```
import { test, expect } from '@playwright/test'

const shopUrl = process.env.SHOP_URL ?? 'https://danube-web.shop'

test('search returns the matching books', async ({ page }) => {
await page.goto(shopUrl)

await page.locator('input[name="searchbar"]').fill('for')
await page.getByRole('button', { name: 'Search' }).click()

await expect(page.locator('.preview-title')).toHaveText([
'The Foreigner',
'The Transformation',
'For Whom the Ball Tells',
'Baiting for Robot',
])
})
```

The search test asserts the exact list of results, so a search that returns the wrong books fails as surely as one that returns nothing. Install Playwright and run both specs:
Terminal

```
npm install --save-dev @playwright/test
npx playwright install chromium
npx playwright test
```

Terminal

```
Running 2 tests using 2 workers

✓ 1 scripts/home.spec.ts:7:5 › home page lists the top sellers (626ms)
✓ 2 scripts/search.spec.ts:5:5 › search returns the matching books (722ms)

2 passed (1.3s)
```

Now turn every spec into a check. A `for_each` over a map creates one Browser Check per entry, and `file()` reads the spec as the check’s script:
browser.tf

```
# One Browser Check per spec file in scripts/. To monitor another flow,
# add a spec file and one line here.
locals {
browser_flows = {
home = "Shop home page"
search = "Shop search"
}
}

resource "checkly_check" "browser" {
for_each = local.browser_flows

name = each.value
type = "BROWSER"
activated = true
frequency = 10
group_id = checkly_check_group_v2.shop.id

script = file("${path.module}/scripts/${each.key}.spec.ts")
}
```

The checks set only what is theirs: a name, a script, and how often to run. Everything else comes from the group. For the checkout itself, with test data that stays out of your sales numbers, follow [Monitor a checkout flow](https://www.checklyhq.com/docs/guides/monitoring-ecommerce-apps-using-playwright).

## ​ Step 5: Assert on the product API

The storefront renders whatever `/api/books` returns. If the API answers 200 with an empty list, the pages load and show nothing to buy. An API check catches that every minute, without starting a browser.
api.tf

```
resource "checkly_check" "books_api" {
name = "Shop books API"
type = "API"
activated = true
frequency = 1
group_id = checkly_check_group_v2.shop.id

degraded_response_time = 1000
max_response_time = 3000

request {
url = "${var.shop_url}/api/books"
method = "GET"

assertion {
source = "STATUS_CODE"
comparison = "EQUALS"
target = "200"
}

assertion {
source = "HEADERS"
property = "content-type"
comparison = "CONTAINS"
target = "application/json"
}

assertion {
source = "JSON_BODY"
property = "$.length"
comparison = "GREATER_THAN"
target = "0"
}
}
}
```

Three assertions, three different failures: the API is down, it returns an HTML error page, or it returns an empty catalog. Responses slower than one second are degraded, and slower than three seconds fail.

## ​ Step 6: Plan, then apply

Check the formatting and the configuration, then ask Terraform what it would create:
Terminal

```
terraform fmt -check
terraform validate
terraform plan
```

The plan lists all five resources. This part of the group block shows settings it enforces on its checks:
Terminal

```
# checkly_check_group_v2.shop will be created
+ resource "checkly_check_group_v2" "shop" {
+ activated = true
+ name = "Shop (Terraform)"

+ enforce_locations {
+ enabled = true
+ locations = [
+ "eu-west-1",
+ "us-east-1",
]
}

+ enforce_retry_strategy {
+ enabled = true

+ retry_strategy {
+ base_backoff_seconds = 30
+ max_duration_seconds = 600
+ max_retries = 2
+ same_region = false
+ type = "LINEAR"
}
}

+ environment_variable {
+ key = "SHOP_URL"
+ value = "https://danube-web.shop"
}
}

Plan: 5 to add, 0 to change, 0 to destroy.
```

When the plan matches what you expect, run `terraform apply` and type `yes` to create the checks in your account. From then on, the folder is the source of truth. Change a file, run `terraform plan` to see the difference, and apply.

Manage these resources in Terraform or in the Checkly app, not both. Changing Terraform-managed resources in the app, and vice versa, is likely to cause issues.

## ​ Verify it works

Break a check before it reaches production. In `scripts/search.spec.ts`, change `'Baiting for Robot'` to `'Waiting for Robot'` and run the specs again:
Terminal

```
npx playwright test
```

Terminal

```
✓ 2 scripts/home.spec.ts:7:5 › home page lists the top sellers (519ms)
✘ 1 scripts/search.spec.ts:5:5 › search returns the matching books (5.6s)

1) scripts/search.spec.ts:5:5 › search returns the matching books ────────────────────────────────

Error: expect(locator).toHaveText(expected) failed

Locator: locator('.preview-title')
Timeout: 5000ms
- Expected - 1
+ Received + 1

Array [
"The Foreigner",
"The Transformation",
"For Whom the Ball Tells",
- "Waiting for Robot",
+ "Baiting for Robot",
]

1 failed
scripts/search.spec.ts:5:5 › search returns the matching books ─────────────────────────────────
1 passed (6.1s)
```

The failure names the flow and the exact result that changed, and the home page check still passes. Nothing reached Checkly, because only `terraform apply` changes a check. Once the checks exist, the same edit shows up in `terraform plan` as a change to that check’s `script`, so a reviewer sees the monitoring change next to the code change. Put `'Baiting for Robot'` back.

## ​ Next

[Run checks on every deploy](https://www.checklyhq.com/docs/guides/sdlc-monitoring): `terraform apply` keeps your monitoring in step with your code. Run the checks against each deploy too, so a broken release fails before it reaches the schedule.

## ​ Reference

- [Terraform provider overview](https://www.checklyhq.com/docs/integrations/iac/terraform/overview), [checks and groups](https://www.checklyhq.com/docs/integrations/iac/terraform/checks-groups), and [alert channels](https://www.checklyhq.com/docs/integrations/iac/terraform/alerting)

- [Testing Terraform scripts locally](https://www.checklyhq.com/docs/integrations/iac/terraform/testing-scripts-locally)

- [`checkly_check_group_v2` on the Terraform Registry](https://registry.terraform.io/providers/checkly/checkly/latest/docs/resources/check_group_v2)

- [Browser checks](https://www.checklyhq.com/docs/detect/synthetic-monitoring/browser-checks/overview) and [API checks](https://www.checklyhq.com/docs/detect/synthetic-monitoring/api-checks/overview)

- [Groups](https://www.checklyhq.com/docs/platform/groups)

- [Checkly CLI vs. Terraform and Pulumi](https://www.checklyhq.com/docs/cli/cli-vs-terraform-pulumi)

- [Checkly Skills](https://www.checklyhq.com/docs/ai/skills)

Was this page helpful?

[Suggest edits](https://github.com/checkly/docs/edit/main/guides/monitoring-ecommerce-apps-using-terraform.mdx)[Raise issue](https://github.com/checkly/docs/issues/new?title=Issue%20on%20docs&body=Path:%20/guides/monitoring-ecommerce-apps-using-terraform)

[Monitor a checkout flow Previous](https://www.checklyhq.com/docs/guides/monitoring-ecommerce-apps-using-playwright)[How to monitor the Stripe customer API with Checkly | API Monitoring Examples Next](https://www.checklyhq.com/docs/guides/monitoring-the-stripe-api)

[x](https://x.com/checklyhq)[github](https://github.com/checkly)[linkedin](https://linkedin.com/company/checkly)

[Powered by This documentation is built and hosted on Mintlify, a developer documentation platform](https://www.mintlify.com/?utm_campaign=poweredBy&utm_medium=referral&utm_source=checkly-422f444a)
