Grafana Grafana

Grafana mit Terraform verwalten

Erstelle Ordner, Datenquellen, Dashboards, Alerts, Teams und Service Accounts aus HCL.

15 min Fortgeschritten

Datei-Provisioning deckt Datenquellen, Dashboards und Alerting ab, aber keine Benutzer, Teams, Service Accounts oder Berechtigungen. Terraform deckt all das ab, funktioniert über seine API gegen jedes laufende Grafana (selbst gehostet oder Grafana Cloud) und führt eine State-Datei, um Drift zu erkennen und zu korrigieren. Diese Anleitung richtet den Provider ein und verwaltet die gängigen Ressourcen.

Voraussetzungen und Provider

Du brauchst Terraform oder OpenTofu, ein erreichbares Grafana und ein Service-Account-Token mit einer Rolle, die verwalten darf, was du deklarierst (siehe die Authentifizierungsanleitung zum Erstellen). Halte das Token aus dem Code heraus:

terraform {
  required_providers {
    grafana = {
      source  = "grafana/grafana"
      version = ">= 2.9.0"   # pinne auf die Version, die du getestet hast
    }
  }
}

variable "grafana_url" {
  type    = string
  default = "http://localhost:3000"
}

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

provider "grafana" {
  url  = var.grafana_url
  auth = var.grafana_token
}

Reiche das Token über die Umgebung weiter, damit es nie in einer Datei landet:

export TF_VAR_grafana_token='glsa_...'
terraform init

Für Grafana Cloud kannst du den Stack und seinen Service Account in derselben Konfiguration erstellen. Die Grafana-Dokumentation zeigt ein Zwei-Provider-Muster: einen cloud-Provider, der sich mit einem Cloud-Access-Policy-Token authentifiziert, um den Stack (grafana_cloud_stack) und einen Service Account (grafana_cloud_stack_service_account mit seinem Token) zu erstellen, und einen zweiten Provider mit Alias für den Stack, der dieses Token nutzt.

Ordner und Datenquellen

resource "grafana_folder" "apps" {
  title = "Applications"
}

resource "grafana_data_source" "prometheus" {
  type       = "prometheus"
  name       = "Prometheus"
  uid        = "prometheus"
  url        = "http://prometheus:9090"
  is_default = true

  json_data_encoded = jsonencode({
    timeInterval = "15s"
  })
}

resource "grafana_data_source" "postgres" {
  type = "postgres"
  name = "Postgres"
  uid  = "postgres-main"
  url  = "postgres.internal:5432"
  username = "grafana"

  json_data_encoded = jsonencode({
    database = "app"
    sslmode  = "require"
  })

  secure_json_data_encoded = jsonencode({
    password = var.postgres_password
  })
}

Das explizite Setzen von uid hält Dashboards und Alert-Regeln über Umgebungen hinweg portabel. Über secure_json_data_encoded übergebene Secrets werden von Grafana verschlüsselt gespeichert, aber auch in den Terraform-State geschrieben, speichere den State also in einem verschlüsselten, zugriffsgeschützten Backend.

Dashboards

resource "grafana_dashboard" "webapp" {
  folder      = grafana_folder.apps.uid
  config_json = file("${path.module}/dashboards/webapp.json")
  overwrite   = true
}

Nutze fileset, um ein ganzes Verzeichnis zu laden:

resource "grafana_dashboard" "all" {
  for_each    = fileset("${path.module}/dashboards", "*.json")
  folder      = grafana_folder.apps.uid
  config_json = file("${path.module}/dashboards/${each.value}")
}

Das Dashboard-JSON ist dieselbe Datei, die du für Datei-Provisioning verwenden würdest, du kannst also zwischen beiden Ansätzen wechseln. Wenn du ein Dashboard an einen Ordner hängst, referenzierten ältere Provider-Versionen die id des Ordners, und neuere bevorzugen seine uid; prüfe die Provider-Dokumentation für die Version, die du pinnst.

Alerting

Terraform hat Ressourcen für jeden Teil des Alertings:

resource "grafana_contact_point" "slack" {
  name = "team-slack"

  slack {
    url       = var.slack_webhook_url
    recipient = "#alerts"
  }
}

resource "grafana_notification_policy" "default" {
  group_by      = ["alertname", "namespace"]
  contact_point = grafana_contact_point.slack.name

  group_wait      = "30s"
  group_interval  = "5m"
  repeat_interval = "4h"
}

Regelgruppen nutzen grafana_rule_group mit rule-Blöcken, deren data-Einträge die Provisioning-YAML spiegeln (Query, Reduce, Threshold). Mute Timings und Templates haben grafana_mute_timing und grafana_message_template. Schlage die exakten Attributnamen in der Provider-Dokumentation nach, da Alerting-Ressourcen über Provider-Versionen hinweg überarbeitet wurden.

grafana_notification_policy verwaltet wie beim Datei-Provisioning den gesamten Policy-Baum, stelle also sicher, dass nur ein Werkzeug ihn besitzt.

Teams, Service Accounts und Berechtigungen

Das sind die Ressourcen, die Dateien nicht ausdrücken können:

resource "grafana_team" "platform" {
  name  = "platform"
  email = "platform@example.com"
}

resource "grafana_service_account" "ci" {
  name = "ci-dashboards"
  role = "Editor"
}

resource "grafana_service_account_token" "ci" {
  name               = "ci-token"
  service_account_id = grafana_service_account.ci.id
}

resource "grafana_folder_permission" "apps" {
  folder_uid = grafana_folder.apps.uid

  permissions {
    team_id    = grafana_team.platform.id
    permission = "Edit"
  }
}

Behandle das erzeugte Token als geheime Ausgabe und schiebe es in deinen Secret Manager, statt es auszugeben.

Workflow

terraform fmt -check
terraform validate
terraform plan -out tfplan
terraform apply tfplan

Führe in CI plan bei Pull Requests und apply beim Merge aus. Der Plan zeigt genau, welche Dashboards, Datenquellen und Alerts sich ändern werden.

Bestehendes übernehmen

Wenn jemand Dinge in der UI gebaut hat, importiere sie in den State, statt sie neu zu erstellen:

terraform import grafana_folder.apps <folder-uid>
terraform import grafana_dashboard.webapp <dashboard-uid>
terraform import grafana_data_source.prometheus <data-source-uid>

Neuere Terraform-Versionen können auch import-Blöcke nutzen. Import-ID-Formate können sich zwischen Provider-Versionen unterscheiden, bestätige sie also in der Ressourcen-Dokumentation.

Terraform neben Datei-Provisioning

Verwalte dieselbe Ressource nicht mit beiden. Wähle eine Grenze und halte dich daran. Eine gängige Aufteilung ist Datei-Provisioning für Datenquellen und Dashboards (beim Start geladen, kein API-Token nötig) und Terraform für Teams, Service Accounts und Berechtigungen. Eine andere ist Terraform für alles, mit auf Servereinstellungen beschränkten Provisioning-Dateien.

Beliebte Anleitungen