Skip to content
Open app

Terraform & OpenTofu

The CuliPulse provider lets you keep your monitors, webhooks and alert routing in code, next to the infrastructure they watch. Review changes in a pull request, apply them with one command, and recreate the same setup in another account whenever you need to.

It works with Terraform 1.5 or newer and OpenTofu 1.6 or newer, and is published on both registries as culipulse/culipulse:

Name What it is
culipulse_http_monitor A website or API check, including headers, secret headers, login details, request body and assertions.
culipulse_heartbeat_monitor A heartbeat (dead man’s switch) for cron jobs and backups. Gives you the ping URL to call.
culipulse_webhook_channel A webhook that receives signed alerts.
culipulse_channel_routing Which monitors send alerts to a channel — works for webhook, Slack and Telegram channels.
culipulse_agents (data source) Looks up checkpoints (CuliPulse’s shared ones or your own agents) by region.
culipulse_channel (data source) Finds a Slack or Telegram channel you connected in the app, by name.

Other monitor types (TCP, UDP, ping, DNS, domain expiry, vendor status) can’t be managed with the provider yet. Keep managing them in the app — the provider leaves them alone.

In the app, go to Settings → API Tokens and generate a token with Read + Write access (see API tokens). A read-only token can plan, but every change will fail.

Give the token to Terraform through an environment variable, so it never ends up in your code:

Terminal window
export CULIPULSE_API_TOKEN="cpk_..."
terraform {
required_providers {
culipulse = {
source = "culipulse/culipulse"
version = "~> 0.1"
}
}
}
# Uses the CULIPULSE_API_TOKEN environment variable.
provider "culipulse" {}

Then run terraform init (or tofu init).

This example checks an API from Singapore, watches a nightly backup, and sends both to your own webhook:

data "culipulse_agents" "singapore" {
kind = "first_party"
region = "sg"
}
resource "culipulse_http_monitor" "api" {
name = "API health"
url = "https://api.example.com/health"
interval_seconds = 300
agent_ids = data.culipulse_agents.singapore.ids
expected_status = "200"
}
resource "culipulse_heartbeat_monitor" "backup" {
name = "Nightly backup"
interval_seconds = 86400
grace_seconds = 1800
}
resource "culipulse_webhook_channel" "incidents" {
name = "Incident webhook"
url = "https://hooks.example.com/culipulse"
}
resource "culipulse_channel_routing" "incidents" {
channel_id = culipulse_webhook_channel.incidents.id
monitor_ids = [
culipulse_http_monitor.api.id,
culipulse_heartbeat_monitor.backup.id,
]
}
# Your backup job calls this URL when it finishes.
output "backup_ping_url" {
value = culipulse_heartbeat_monitor.backup.ping_url
sensitive = true
}

Run terraform plan to see what will change, then terraform apply. Every attribute is described in the provider reference.

Bringing existing monitors under Terraform

Section titled “Bringing existing monitors under Terraform”

Everything the provider manages can be imported. A monitor’s id is in the address bar when you open it in the app (.../app/monitors/mon_...). To find a channel’s id (it starts with nch_), list your channels through the API:

Terminal window
curl -H "Authorization: Bearer $CULIPULSE_API_TOKEN" https://culipulse.dev/v1/channels
Terminal window
terraform import culipulse_http_monitor.api mon_...
terraform import culipulse_heartbeat_monitor.backup mon_...
terraform import culipulse_webhook_channel.incidents nch_...
terraform import culipulse_channel_routing.incidents nch_...

A few things can’t be read back from CuliPulse, so check them before your first apply after an import:

  • Secret headers, bearer tokens, login passwords and secret request bodies on an HTTP monitor. If your configuration doesn’t include them, the next apply removes them from the monitor. Add them to your configuration first.
  • A webhook’s URL and signing secret. Add the url to your configuration; as long as it points at the same host, CuliPulse records it without recreating the webhook. The signing secret can’t be recovered after an import — keep using the one you copied when you created the webhook.
  • Secrets are stored in your Terraform state. Secret headers, passwords, tokens, webhook URLs, signing secrets and heartbeat ping URLs are marked sensitive, so they’re hidden in plan output — but they’re saved in the state file. Keep your state somewhere private and encrypted (for example a remote backend with encryption and restricted access).
  • Changes to secrets made in the app aren’t detected. CuliPulse never shows secrets again once saved, so Terraform can’t notice if someone changes one in the app.
  • Slack and Telegram channels are connected in the app. Both need you to approve the connection by hand, so Terraform can’t create them. Connect them in the app, then use the culipulse_channel data source to route alerts to them.
  • Changing a webhook’s URL replaces the webhook and gives it a new signing secret. Update your receiver with the new secret (available as signing_secret).
  • Removing a culipulse_channel_routing resource sets that channel back to receiving alerts from all monitors, which is the app’s default.
  • Large changes on the Free plan take longer. Each token can make 60 requests a minute on the Free plan (see Rate limits). The provider waits and retries automatically, so a big apply succeeds — it just takes a few minutes.

Found a bug or missing a feature? Open an issue on GitHub or email [email protected].