Skip to content

Terraform provider

The monitorable-io/monitorable provider manages servers, the alert template each server follows, and maintenance mutes from Terraform 1.5 or later and OpenTofu 1.6 or later. It talks to the same public API as everything else, with an organization API key.

terraform {
required_providers {
monitorable = {
source = "monitorable-io/monitorable"
version = "~> 0.1"
}
}
}
provider "monitorable" {}

The same required_providers block works with OpenTofu 1.6 or later; run tofu init.

Create a key in the dashboard under Team → API keys and give it these four scopes: servers:read, servers:write, alerts:read and alerts:write. The Automation preset covers the server scopes and alerts:write but not alerts:read, which the monitorable_alert_template, monitorable_alert_templates and monitorable_notification_channels data sources need, so choose the four scopes explicitly. API keys and scopes lists what each scope allows.

Pass the key with an environment variable so no credential appears in the configuration:

Terminal window
export MONITORABLE_API_KEY=<your-api-key>
Setting Environment variable Argument Default
API key MONITORABLE_API_KEY api_key none (required)
API origin MONITORABLE_ENDPOINT endpoint https://api.monitorable.io

A provider argument wins over the matching environment variable. Set api_key only from a secret store or a sensitive variable, never as a literal.

monitorable_server creates the server record. The API returns the agent’s key and the install command once, when the server is created; the provider keeps them as the sensitive attributes agent_api_key and install_command. Run the command on the host with a remote-exec provisioner:

resource "monitorable_server" "web" {
name = "web-01"
}
# Install the agent on the host with the command the API returned when the
# server was created. install_command embeds the agent's API key; it is
# sensitive, so Terraform hides the provisioner's output.
resource "null_resource" "web_agent" {
depends_on = [monitorable_server.web]
triggers = {
server_id = monitorable_server.web.id
}
connection {
type = "ssh"
host = "203.0.113.10"
user = "ubuntu"
}
# install_command is null for an imported server (and after a create whose
# response was lost and recovered on retry), so this provisioner applies only
# to servers this configuration created. For an imported server, rotate the
# agent key in the dashboard and install the agent with the new key.
provisioner "remote-exec" {
inline = [monitorable_server.web.install_command]
}
}

Changing name renames the server in place. Destroying the resource deletes the server and its metrics history.

A server with no assignment follows the organization’s default template. Look a template up by name and assign it:

# Look the template up by name instead of hard-coding its id.
data "monitorable_alert_template" "strict" {
name = "Strict"
}
# Servers with no assignment follow the organization's default template.
# Destroying this resource returns web-01 to that default; the template stays.
resource "monitorable_server_alert_template" "web" {
server_id = monitorable_server.web.id
template_id = data.monitorable_alert_template.strict.id
}

The name is exact and case-sensitive. Destroying monitorable_server_alert_template returns the server to the default template; it never deletes the template. If someone resets the assignment in the dashboard, the next plan assigns it again.

monitorable_server_mute mutes a server’s alerts. Set until for a window, or omit it to mute until the resource is destroyed.

# A maintenance window: alerts are muted until the instant passes. After that
# the resource stays in state as completed (active = false) and is not applied
# again; change `until` to open a new window.
resource "monitorable_server_mute" "window" {
server_id = monitorable_server.web.id
until = "2026-12-01T06:00:00Z"
reason = "kernel upgrade"
}
# Mute for the duration of one apply, for example from a deploy pipeline.
# timestamp() changes on every run, so ignore_changes keeps the mute from being
# replaced by the next plan: the window is opened once, when the resource is
# created, and `terraform destroy` lifts it early. A pipeline that wants one
# window per deploy should drop `ignore_changes`: each apply's timestamp() then
# differs from the state's, which replaces the mute and opens a fresh window.
resource "monitorable_server_mute" "deploy" {
server_id = monitorable_server.web.id
until = timeadd(timestamp(), "30m")
reason = "deploy"
lifecycle {
ignore_changes = [until]
}
}
# With no `until` the mute lasts until the resource is destroyed.
resource "monitorable_server_mute" "until_destroyed" {
server_id = monitorable_server.web.id
reason = "decommissioning"
}

A mute whose until has passed stays in state as completed and is not applied again. Changing until to another instant replaces the resource and opens a new window. Destroying the resource unmutes the server. A server has at most one mute.

Adopt a server that already exists, whether you added it in the dashboard or an apply left it behind (see Interrupted applies):

Terminal window
terraform import monitorable_server.web 42

The id is the server’s id, which data "monitorable_servers" returns. The alert template assignment and the mute import the same way, by server id:

Terminal window
terraform import monitorable_server_alert_template.web 42
terraform import monitorable_server_mute.window 42

A server that follows the default template has no assignment to import, and a server that is not muted has no mute to import. An imported server has no agent_api_key or install_command, because the API returns them only when the server is created. To install the agent on it, rotate its key in the dashboard.

Data source Returns
monitorable_server One server, by id or by exact, case-sensitive name.
monitorable_servers Servers, optionally filtered with name_contains and status.
monitorable_alert_template One alert template, by name.
monitorable_alert_templates All alert templates, with which one is the default.
monitorable_notification_channels The organization’s notification channels; their settings are never exposed.

monitorable_server suits a known, named set of machines. When servers appear without Terraform creating each one, such as autoscaling groups and machine images, use an enrollment key instead: each host registers itself when it boots. See Add servers without the dashboard.

If an apply is killed or interrupted after the API created a server, or its retries ran out, the server can exist without Terraform tracking it. Find it with data "monitorable_servers" or in the dashboard and adopt it with terraform import monitorable_server.<name> <id>. Running the apply again without importing would try to create a second server.

agent_api_key and install_command are marked sensitive, which hides them from plan and apply output, but Terraform still stores them in state in plain text. Treat the state as a secret: use an encrypted remote backend with restricted access and never commit it. If a state file is exposed, rotate the server’s agent key in the dashboard.

  • Plan limit. When the plan’s server limit is reached, creating a server fails with a 402 plan_limit_reached error that says where to raise the limit. Remove a server or upgrade; see pricing for the plans.
  • Rate limits. Writes are limited to 60 per minute per API key. The provider waits and retries when the API answers 429, so Terraform’s default -parallelism=10 is fine. See Rate limits and retries and Errors.