Grafana Terraform Provider Dashboards Prompt
Manage Grafana dashboards, folders, and alerts as code using the Terraform grafana provider with stable UIDs and state.
- Target user
- Platform engineers running Grafana in a Terraform workflow
- Difficulty
- Advanced
- Tools
- Claude, ChatGPT
The prompt
You are a senior platform engineer who manages Grafana with the Terraform `grafana/grafana` provider — dashboards, folders, data sources, alert rules, and contact points all in HCL.
I will provide:
- The resources to manage (folders, dashboards, alerts)
- The Grafana URL/auth and whether it is Cloud or self-hosted
- Existing dashboards to import vs greenfield
Your job:
1. **Provider setup**: configure `provider "grafana"` with `url` and `auth` (service account token), pin the provider version, and store state remotely (locked).
2. **Folders first**: create `grafana_folder` resources and reference `.id`/`.uid` from dashboards so ordering is correct.
3. **Dashboards**: use `grafana_dashboard` with `config_json`; keep the JSON in external files via `file()` or `jsonencode()`, and let the provider manage the `uid`.
4. **Avoid drift**: strip server-managed fields (`id`, `version`) from imported JSON; the provider re-adds them. Use `overwrite = true` cautiously.
5. **Alerts**: model `grafana_rule_group`, `grafana_contact_point`, `grafana_notification_policy`, and `grafana_mute_timing` rather than clicking them.
6. **Data sources**: define `grafana_data_source` with `secure_json_data_encoded` for secrets pulled from Vault/SSM, never hardcoded.
7. **Import existing**: use `terraform import grafana_dashboard.x {folderUID}:{uid}` and reconcile the JSON before the first apply.
8. **CI plan gate**: run `terraform plan` in PRs so dashboard diffs are reviewed; apply only on merge.
Mark DESTRUCTIVE: `terraform destroy` or removing a resource block (deletes the dashboard/folder), changing a `uid`, or `overwrite = true` clobbering UI edits.
---
Resources to manage: [DESCRIBE]
Grafana URL/auth: [DESCRIBE]
Import vs greenfield: [DESCRIBE]
Why this prompt works
The Terraform grafana provider spans dozens of resource types, and the common failures — perpetual drift from server-managed JSON, accidental deletes when a block is removed, secrets in HCL — are predictable. This prompt front-loads folder ordering, uid stability, secret sourcing, and a plan-review gate so dashboards-as-code stays reviewable and non-destructive.
How to use it
- Decide import vs greenfield so the JSON reconciliation step is planned.
- Provide auth details to configure the provider and secret backend.
- Run
planin CI and review the dashboard diff before merge. - Never edit managed dashboards in the UI unless you accept clobbering.
Useful commands
# Import an existing dashboard into state
terraform import grafana_dashboard.slo "general:slo-overview"
# Plan / apply with the Grafana provider
export GRAFANA_URL=https://grafana.example.com
export GRAFANA_AUTH=$GRAFANA_SA_TOKEN
terraform plan -out plan.tfplan
terraform apply plan.tfplan
# Verify the resulting dashboard via API
curl -s -H "Authorization: Bearer $GRAFANA_AUTH" \
"$GRAFANA_URL/api/dashboards/uid/slo-overview" | jq '.dashboard.title'
Example config
terraform {
required_providers {
grafana = { source = "grafana/grafana", version = "~> 3.0" }
}
}
provider "grafana" {
url = var.grafana_url
auth = var.grafana_sa_token # from Vault/SSM, not literal
}
resource "grafana_folder" "slo" {
title = "SLO Dashboards"
uid = "slo"
}
resource "grafana_dashboard" "slo_overview" {
folder = grafana_folder.slo.uid
config_json = jsonencode({
uid = "slo-overview"
title = "SLO Overview"
panels = [{
title = "Availability"
type = "timeseries"
datasource = { type = "prometheus", uid = "prometheus-prod" }
targets = [{ expr = "1 - (sum(rate(errors[5m])) / sum(rate(total[5m])))" }]
}]
schemaVersion = 39
time = { from = "now-6h", to = "now" }
})
}
resource "grafana_contact_point" "slack" {
name = "slack-critical"
slack {
url = var.slack_webhook # secret
recipient = "#alerts-critical"
}
}
Common findings this catches
- Perpetual diffs →
id/versionnot stripped from JSON. - Accidental deletes → removed block deletes the real object.
- Broken references →
uidchanged after alerts/links wired. - Secrets in state → tokens hardcoded instead of variables.
- Unreviewed dashboard changes → no
plangate in CI. - Clobbered UI edits →
overwrite = trueon a hand-edited dashboard.
When to escalate
- Migrating hundreds of hand-built dashboards — staged import plan.
- Shared state across many teams — workspace and RBAC design.
- Provider major-version upgrades with breaking schema — platform owner.
Related prompts
-
Grafana Data Source Provisioning YAML Prompt
Provision Grafana data sources as code with provisioning YAML in /etc/grafana/provisioning/datasources for reproducible, secret-safe config.
-
Grafana Folder Governance At Scale Prompt
Design Grafana folder structure, RBAC, and provisioning governance so dashboards stay organized and permissions scale.
-
Grafana Grizzly Dashboards as Code Prompt
Manage Grafana dashboards, folders, and alert rules as code with Grizzly (grr) using a kubectl-style apply/diff workflow.