Blog

Bereitstellung von Grafana auf Azure App Service mit Terraform (und Active Directory Integration)

Valerii Matveev

Aktualisiert Oktober 16, 2025
7 Minuten

Intro

Grafana ist eine kostenlose und quelloffene Plattform, die es Ihnen ermöglicht, Ihre Metriken abzufragen, zu visualisieren, zu melden und zu verstehen.
Wenn Ihr System größer wird und mehr bewegliche Teile hat, wird es wichtig, auf einen Blick erkennen zu können, ob es gesund und betriebsbereit ist.

In Azure können Sie Ihr Grafana auf verschiedene Weise zum Laufen bringen:

Verwenden Sie Azure Managed Grafana

Dies ist ein vollständig verwalteter Service, der eine praktikable Option sein kann, wenn Sie keine Ressourcen für die Wartung Ihrer Überwachungslösung entbehren können. Sie können sicher sein, dass Ihre verwaltete Grafana-Instanz mit den neuesten Patches aktualisiert wird.

Der Nachteil dieses Ansatzes ist der Preis - zum Zeitpunkt der Erstellung dieses Artikels kostet es ~5,7 Euro pro Benutzer und Monat, was nicht allzu teuer ist, wenn Sie nicht viele Personen für den Zugriff auf das Dashboard benötigen. Eine weitere Einschränkung ist, dass Sie aufgrund von Sicherheitsbedenken keine benutzerdefinierten Plugins installieren können.

Grafana auf Azure App Service bereitstellen

Diese Option würde mehr Verantwortung auf die Techniker übertragen, da sie dafür verantwortlich wären, dass die Grafana-Instanz gepatcht ist und alle notwendigen Plugins installiert sind. Das Gute daran ist, dass Sie mehr Kontrolle über die Plattform haben und nicht pro Benutzer zahlen müssen.

Voraussetzungen

Um diese Ressourcen bereitstellen zu können, benötigen Sie:

  • Terraform (v1.1.9 zum Zeitpunkt des Schreibens)
  • Azure-Konto mit Eigentümerrechten für Ihr Abonnement
  • Azure CLI
  • sqlite

Provisionierung

Anbieter

Melden wir uns zunächst bei AzureCLI an

az login

Nachdem dies erfolgreich war, können wir Terraform initialisieren. Wir benötigen zwei Anbieter: azurerm und random.

terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~>3.0"
    }
    azuread = {
      source  = "hashicorp/azuread"
      version = "~> 2.15.0"
    }
    random = {
      source  = "hashicorp/random"
      version = "3.1.0"
    }
  }
}

provider "azurerm" {
  features {}
}

provider "random" {
}

provider "azuread" {
  tenant_id = data.azurerm_client_config.current.tenant_id
}

Falls Sie ein Remote-Backend verwenden möchten (was Sie wahrscheinlich tun sollten), vergessen Sie nicht, es im terraform Block zu konfigurieren. Für diesen Beitrag lassen wir ihn weg.

Nachdem Ihre Provider konfiguriert sind, können wir das Arbeitsverzeichnis initialisieren.

terraform init

Bootstrap für die Infrastruktur

In unserem Beispiel werden wir eine PostgreSQL-Datenbank überwachen. Um die Einrichtung zu vereinfachen, werden alle Ressourcen in einer Ressourcengruppe bereitgestellt. Lassen Sie uns die Ressourcengruppe erstellen.

locals {
  location            = "westeurope"
  resource_group_name = "grafana"
}

resource "azurerm_resource_group" "rg" {
  name     = local.resource_group_name
  location = local.location
}

Lassen Sie uns auch den Keyvault erstellen, in dem wir unser Admin-Passwort speichern. Wir könnten auch die Terraform-Ausgabe verwenden, um das Passwort auszulesen. Der Weg über den Keyvault würde es Ihren Kollegen ermöglichen, auf die Anmeldedaten zuzugreifen, ohne mit Terraform arbeiten zu müssen.

locals {
  ...
  kv_name             = "grafanakv"
}

data "azurerm_client_config" "current" {}

resource "azurerm_key_vault" "kv" {
  name                       = local.kv_name
  location                   = azurerm_resource_group.rg.location
  resource_group_name        = azurerm_resource_group.rg.name
  tenant_id                  = data.azurerm_client_config.current.tenant_id
  soft_delete_retention_days = 7
  purge_protection_enabled   = false

  sku_name = "standard"

  access_policy {
    tenant_id = data.azurerm_client_config.current.tenant_id
    object_id = data.azurerm_client_config.current.object_id

    secret_permissions = [
      "Get", "List", "Purge", "Set", "Delete", "Recover"
    ]
  }
}

Wir geben unserem angemeldeten Benutzer auch Zugriff auf die Geheimnisse in diesem Keyvault. Wenn mehr Benutzer/Gruppen ihn benötigen, können Sie jederzeit weitere access_policy Blöcke hinzufügen.

Wenn der Keyvault eingerichtet ist, können wir den Datenbankserver erstellen

locals {
  ...
  postgres_name  = "grafana"
  database_name  = "data"
  sql_admin_name = "sqladmin"
}

resource "random_password" "password" {
  length  = 16
  special = true
}

resource "azurerm_key_vault_secret" "password" {
  name         = "db-password"
  value        = random_password.password.result
  key_vault_id = azurerm_key_vault.kv.id
}

resource "azurerm_postgresql_server" "source" {
  name                          = local.postgres_name
  location                      = azurerm_resource_group.rg.location
  resource_group_name           = azurerm_resource_group.rg.name
  public_network_access_enabled = true

  administrator_login              = local.sql_admin_name
  administrator_login_password     = azurerm_key_vault_secret.password.value
  backup_retention_days            = 7
  sku_name                         = "B_Gen5_1"
  ssl_enforcement_enabled          = false
  ssl_minimal_tls_version_enforced = "TLSEnforcementDisabled"
  storage_mb                       = 5120
  version                          = "11"
}

Und die Datenbank selbst

resource "azurerm_postgresql_database" "db" {
  name                = local.database_name
  resource_group_name = azurerm_resource_group.rg.name
  charset             = "UTF8"
  collation           = "English_United States.1252"
  server_name         = azurerm_postgresql_server.source.name
}

Bereitstellung von Grafana

Wenn die Ressourcen vorhanden sind, können wir die Überwachungsinfrastruktur einrichten. Als erstes müssen Sie den Log Analytics Workspace erstellen.

locals {
  ...
  log_analytics_workspace_name = "log-analytics"
}

resource "azurerm_log_analytics_workspace" "logs" {
  name                = local.log_analytics_workspace_name
  location            = local.location
  resource_group_name = local.resource_group_name
  sku                 = "PerGB2018"
  retention_in_days   = 30
}

Als Nächstes erstellen wir den Dienstprinzipal, um eine Verbindung zu Azure AD herzustellen, generieren ein Passwort dafür und speichern es in unserem Keyvault

locals {
  ...
  ad_app_display_name = "grafana"

  microsoft_graph = {
    app_id = "00000003-0000-0000-c000-000000000000" # Microsoft Graph 
    scopes = [
      "37f7f235-527c-4136-accd-4a02d197296e", # openid
      "64a6cdd6-aab1-4aaf-94b8-3cc8405e90d0", # email
      "14dad69e-099b-42c9-810b-d002981feec1", # profile
    ]
  }
}

resource "azuread_application" "grafana" {
  display_name = local.ad_app_display_name
  web {
    homepage_url  = "https://${local.app_service_name}.azurewebsites.net"
    redirect_uris = ["https://${local.app_service_name}.azurewebsites.net/login/generic_oauth"]
  }
  identifier_uris = ["api://${local.ad_app_display_name}"]
  owners          = [data.azuread_user.owner.id]

  required_resource_access {
    resource_app_id = local.microsoft_graph.app_id
    dynamic "resource_access" {
      for_each = local.microsoft_graph.scopes
      content {
        id = resource_access.value
        type = "Scope"
      }
    }
  }
}

resource "azuread_application_password" "grafana" {
  application_object_id = azuread_application.grafana.object_id
}

resource "azurerm_key_vault_secret" "password" {
  name         = "grafana-app-password"
  value        = azuread_application_password.grafana.value
  key_vault_id = data.azurerm_key_vault.kv.id
}

resource "azuread_service_principal" "grafana" {
  application_id = azuread_application.grafana.application_id
  owners         = [data.azuread_user.owner.id]
}

Dieser Dienstleiter muss die Rolle Überwachungsleser in unserer Ressourcengruppe haben:

resource "azurerm_role_assignment" "grafana_monitoring_reader" {
  scope                = data.azurerm_resource_group.rg.id
  role_definition_name = "Monitoring Reader"
  principal_id         = azuread_service_principal.grafana.id
}

web und required_resource_access werden benötigt, um sich mit Ihrem AzureAD-Benutzer bei Grafana anmelden zu können. Denken Sie daran, dass der Teil owner sehr wichtig ist, da Sie sonst nichts mit dem Service Principal anfangen können und im Grunde ausgesperrt sind.

Eine weitere Voraussetzung ist die Bereitstellung eines Speicherkontos mit einer Freigabe, in der Grafana seine Daten speichern kann.

locals {
  ...
  storage_account_name = "grafanastorage"
  grafana_share_name   = "grafana-share"
}

resource "azurerm_storage_account" "storage" {
  name                     = local.storage_account_name
  resource_group_name      = azurerm_resource_group.rg.name
  location                 = azurerm_resource_group.rg.location
  account_tier             = "Standard"
  account_replication_type = "LRS"
  account_kind             = "StorageV2"
  is_hns_enabled           = true
}

resource "azurerm_storage_share" "grafana" {
  name                 = local.grafana_share_name
  storage_account_name = data.azurerm_storage_account.storage.name
  quota                = 50
}

Es gibt ein Problem mit Grafana, dass es möglicherweise nicht in der Lage ist, die Datenbank zu erstellen und zu initialisieren, und Sie erhalten die Fehlermeldung database is locked. Um dies zu vermeiden, können Sie eine leere Sqlite-Datenbank erstellen:

mkdir assets
sqlite3 assets/grafana.db 'PRAGMA journal_mode=wal;'

und laden Sie sie in die Freigabe hoch:

resource "azurerm_storage_share_file" "grafana_db" {
  name             = "grafana.db"
  storage_share_id = azurerm_storage_share.grafana.id
  source           = "assets/grafana.db"
}

Wenn alle Voraussetzungen erfüllt sind, können wir unseren App Service, der Grafana hostet, bereitstellen.

locals {
  ...
  app_service_plan_name = "grafana-asp"
  app_service_name      = "grafana"
}

resource "azurerm_service_plan" "grafana" {
  name                = local.app_service_plan_name
  location            = local.location
  resource_group_name = local.resource_group_name
  os_type             = "Linux"
  sku_name            = "B1"
}

resource "azurerm_linux_web_app" "grafana" {
  name                = local.app_service_name
  location            = local.location
  resource_group_name = local.resource_group_name
  service_plan_id     = azurerm_service_plan.grafana.id

  site_config {
    application_stack {
      docker_image     = "grafana/grafana"
      docker_image_tag = "latest"
    }
  }

  app_settings = {
    "GF_SERVER_ROOT_URL"                          = "https://${local.app_service_name}.azurewebsites.net"
    "GF_SECURITY_ADMIN_USER"                      = data.azuread_user.owner.user_principal_name
    "GF_INSTALL_PLUGINS"                          = "grafana-clock-panel,grafana-simple-json-datasource"
    "GF_AUTH_GENERIC_OAUTH_NAME"                  = "Azure AD"
    "GF_AUTH_GENERIC_OAUTH_ENABLED"               = "true"
    "GF_AUTH_GENERIC_OAUTH_CLIENT_ID"             = azuread_application.grafana.application_id
    "GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET"         = azuread_application_password.grafana.value
    "GF_AUTH_GENERIC_OAUTH_SCOPES"                = "openid email name"
    "GF_AUTH_GENERIC_OAUTH_AUTH_URL"              = "https://login.microsoftonline.com/${data.azurerm_client_config.current.tenant_id}/oauth2/authorize"
    "GF_AUTH_GENERIC_OAUTH_TOKEN_URL"             = "https://login.microsoftonline.com/${data.azurerm_client_config.current.tenant_id}/oauth2/token"
    "GF_AUTH_GENERIC_OAUTH_API_URL"               = ""
    "GF_AUTH_GENERIC_OAUTH_TEAM_IDS"              = ""
    "GF_AUTH_GENERIC_OAUTH_ALLOWED_ORGANIZATIONS" = ""
  }

  storage_account {
    access_key   = data.azurerm_storage_account.datalake.primary_access_key
    account_name = data.azurerm_storage_account.datalake.name
    name         = local.app_service_name
    share_name   = azurerm_storage_share.grafana.name
    type         = "AzureFiles"
    mount_path   = "/var/lib/grafana/"
  }

  logs {
    http_logs {
      file_system {
        retention_in_mb   = 35
        retention_in_days = 5
      }
    }
  }

  depends_on = [
    azurerm_storage_share_file.grafana_db
  ]
}

Bitte beachten Sie, dass unser aktuell angemeldeter Benutzer eine Admin-Rolle zugewiesen bekommt (siehe GF_SECURITY_ADMIN_USER Umgebungsvariable).

Verbinden von Grafana mit Azure Monitor

Nachdem die Konfiguration angewendet wurde, können Sie sich bei Ihrem App-Dienst anmelden und wenn alles korrekt ist, sehen Sie den Grafana-Willkommensbildschirm:

wo Sie auf "Mit Azure AD anmelden" klicken können.

Falls die Seite nicht geladen wird, können Sie die Protokolle im Log Stream überprüfen:

Nachdem Sie sich angemeldet haben, müssen Sie eine neue Datenquelle vom Typ Azure Monitor hinzufügen. Datenquellen können über die Seite Konfiguration hinzugefügt werden:

Suchen Sie nach Azure Monitor und fügen Sie es hinzu

Füllen Sie den Berechtigungsnachweis aus

Und Sie sollten sehen, dass Grafana eine Verbindung zum Azure Log Analytics Service herstellen konnte

Anzeigen von PostgreSQL-Metriken

Lassen Sie uns sehen, ob wir ein Dashboard mit unserer PostgreSQL-CPU-Auslastungsmetrik erstellen können.

Gehen Sie zu Neues Dashboard hinzufügen:

Und geben Sie alle Details zu Ihrer PostgreSQL-Datenbank an:

Wie Sie sehen, können wir mit Grafana Metriken zur CPU-Auslastung der Datenbank auslesen.

Fazit

Wir haben gezeigt, wie Sie Grafana mit Azure Monitor verwenden können, um Ihre Überwachungsmöglichkeiten zu verbessern. Wenn Sie Ihren Anwendungsdienst mit Azure AD verbinden, können Sie den Zugriff auf Grafana zentral und automatisch verwalten.

Verfasst von

Valerii Matveev

Contact

Let’s discuss how we can support your journey.