---
title: "Anatomy of an alert rule in Terraform | Grafana Labs"
description: "How a Grafana alert rule maps to a Terraform resource block"
---

> For a curated documentation index, see [llms.txt](/llms.txt). For the complete documentation index, see [llms-full.txt](/llms-full.txt).

## Alerting resources

Grafana alerting resources in Terraform include:

| Terraform resource            | What it manages                                               | Key attributes                                               |
|-------------------------------|---------------------------------------------------------------|--------------------------------------------------------------|
| `grafana_rule_group`          | A group of alert rules with shared evaluation interval        | `name`, `folder_uid`, `interval_seconds`, `rule` blocks      |
| `grafana_contact_point`       | Notification destinations (email, Slack, PagerDuty, webhooks) | `name`, integration-specific blocks (`email`, `slack`, etc.) |
| `grafana_notification_policy` | Routing rules that match alerts to contact points             | `contact_point`, `group_by`, `matcher` blocks                |
| `grafana_mute_timing`         | Time windows when notifications are suppressed                | `name`, `intervals` with days, months, times                 |
| `grafana_message_template`    | Reusable Go templates for notification content                | `name`, `template`                                           |

An alert rule defines what to watch, what “bad” looks like, and how often to check. In other words, it tells Grafana when something is wrong. Contact points and notification policies tell it what to do about it. Together, these three resources form a complete alerting pipeline.

## Alert rule group

In Terraform, alert rules live inside a rule group. A rule group is a named collection of related rules that share the same evaluation interval and folder.

| Concept                 | What it is                                                                                     |
|-------------------------|------------------------------------------------------------------------------------------------|
| **Alert rule**          | The instructions you give Grafana: what to watch, what “bad” looks like, how often to check    |
| **Rule group**          | A named collection of related alert rules that share the same evaluation interval and folder   |
| **Evaluation interval** | How often Grafana checks the rule’s condition                                                  |
| **Condition**           | An expression that reduces query results to a single value and compares it against a threshold |

## Example: a CPU usage alert

This HCL block defines a rule group with one alert rule that fires when CPU usage exceeds 80%.

The data blocks form a pipeline: the first block (A) queries the metric, the second (B) reduces the result to a single value, and the third (C) compares that value against a threshold. Blocks B and C use `datasource_uid = "__expr__"`, which is Grafana’s built-in server-side expression engine for transforming and evaluating query results without a separate data source. The `condition` field on the rule points to the final data block (C), which determines when the alert fires.

Labels on the rule, like `severity = "warning"`, control how notifications are routed. The notification policy you’ll define in the next slide matches on these labels to decide which contact point receives the alert.

hcl ![Copy code to clipboard](/media/images/icons/icon-copy-small-2.svg) Copy

```hcl
resource "grafana_rule_group" "cpu_alerts" {
  name             = "CPU alerts"
  folder_uid       = grafana_folder.alerts.uid
  interval_seconds = 60

  rule {
    name      = "High CPU usage"
    condition = "C"

    data {
      ref_id         = "A"
      datasource_uid = "prometheus"

      relative_time_range {
        from = 600
        to   = 0
      }

      model = jsonencode({
        expr = "100 - (avg(rate(node_cpu_seconds_total{mode=\"idle\"}[5m])) * 100)"
      })
    }

    data {
      ref_id         = "B"
      datasource_uid = "__expr__"

      relative_time_range {
        from = 0
        to   = 0
      }

      model = jsonencode({
        type       = "reduce"
        expression = "A"
        reducer    = "last"
      })
    }

    data {
      ref_id         = "C"
      datasource_uid = "__expr__"

      relative_time_range {
        from = 0
        to   = 0
      }

      model = jsonencode({
        type       = "threshold"
        expression = "B"
        conditions = [{
          evaluator = {
            type   = "gt"
            params = [80]
          }
        }]
      })
    }

    labels = {
      severity = "warning"
    }
  }
}
```
