Grafana Grafana

Gérer Grafana avec Terraform

Créez dossiers, sources de données, tableaux de bord, alertes, équipes et comptes de service depuis du HCL.

15 min Intermédiaire

Le provisionnement par fichiers couvre les sources de données, les tableaux de bord et l’alerting, mais pas les utilisateurs, les équipes, les comptes de service ni les permissions. Terraform couvre tout cela, fonctionne avec n’importe quel Grafana en cours d’exécution (auto-hébergé ou Grafana Cloud) via son API, et conserve un fichier d’état pour détecter et corriger la dérive. Ce guide configure le provider et gère les ressources courantes.

Prérequis et provider

Il vous faut Terraform ou OpenTofu, un Grafana accessible, et un jeton de compte de service avec un rôle capable de gérer ce que vous déclarez (voir le guide sur l’authentification pour le créer). Gardez le jeton hors du code :

terraform {
  required_providers {
    grafana = {
      source  = "grafana/grafana"
      version = ">= 2.9.0"   # épinglez la version que vous avez testée
    }
  }
}

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
}

Passez le jeton via l’environnement pour qu’il n’atterrisse jamais dans un fichier :

export TF_VAR_grafana_token='glsa_...'
terraform init

Pour Grafana Cloud, vous pouvez créer le stack et son compte de service dans la même configuration. La documentation de Grafana présente un modèle à deux providers : un provider cloud qui s’authentifie avec un jeton de politique d’accès Cloud pour créer le stack (grafana_cloud_stack) et un compte de service (grafana_cloud_stack_service_account avec son jeton), puis un second provider, aliasé sur le stack, qui utilise ce jeton.

Dossiers et sources de données

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
  })
}

Définir uid explicitement garde les tableaux de bord et les règles d’alerte portables d’un environnement à l’autre. Les secrets passés via secure_json_data_encoded sont stockés chiffrés par Grafana, mais ils sont aussi écrits dans l’état Terraform ; stockez donc l’état dans un backend chiffré et à accès contrôlé.

Tableaux de bord

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

Utilisez fileset pour charger tout un répertoire :

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

Le JSON du tableau de bord est le même fichier que celui utilisé pour le provisionnement par fichiers ; vous pouvez donc passer d’une approche à l’autre. Pour rattacher un tableau de bord à un dossier, les anciennes versions du provider référençaient l’id du dossier et les plus récentes préfèrent son uid ; consultez la documentation du provider pour la version que vous épinglez.

Alerting

Terraform dispose de ressources pour chaque partie de l’alerting :

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"
}

Les groupes de règles utilisent grafana_rule_group avec des blocs rule dont les entrées data reflètent le YAML de provisionnement (requête, réduction, seuil). Les mute timings et les modèles ont grafana_mute_timing et grafana_message_template. Recherchez les noms d’attributs exacts dans la documentation du provider, car les ressources d’alerting ont été révisées au fil des versions du provider.

grafana_notification_policy gère tout l’arbre des politiques, comme dans le provisionnement par fichiers ; assurez-vous donc qu’un seul outil en soit propriétaire.

Équipes, comptes de service et permissions

Ce sont les ressources que les fichiers ne peuvent pas exprimer :

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"
  }
}

Traitez le jeton généré comme une sortie secrète, et envoyez-le dans votre gestionnaire de secrets plutôt que de l’afficher.

Workflow

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

En CI, exécutez plan sur les pull requests et apply à la fusion. Le plan montre exactement quels tableaux de bord, sources de données et alertes vont changer.

Adopter ce qui existe déjà

Si quelqu’un a construit des choses dans l’interface, importez-les dans l’état plutôt que de les recréer :

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

Les versions plus récentes de Terraform peuvent aussi utiliser des blocs import. Les formats d’ID d’import peuvent différer entre les versions du provider ; confirmez-les dans la documentation des ressources.

Terraform aux côtés du provisionnement par fichiers

Ne gérez pas la même ressource avec les deux. Choisissez une frontière et tenez-vous-y. Une répartition courante : provisionnement par fichiers pour les sources de données et les tableaux de bord (chargés au démarrage, sans jeton d’API) et Terraform pour les équipes, les comptes de service et les permissions. Une autre : Terraform pour tout, avec des fichiers de provisionnement limités aux paramètres du serveur.

Guides populaires