How to Deploy Grafana on Azure Using Terraform

Grafana is one of the most popular open-source visualisation tools for monitoring systems, applications, and infrastructure. If you're running on Azure and want to automate the deployment of Grafana using Infrastructure as Code (IaC), Terraform is the tool for the job. This guide will walk through deploying Grafana on Azure using Terraform.
What do you need
All you need to follow this post is:
Terraform installed
Azure CLI installed
VS Code (or follow this article using your IDE of choice)
An Azure Subscription to deploy to
As we are deploying Grafana and not utilising any Cloud-hosted option, you do not need to sign up for any other services.
Terraform Configuration
First, create a directory for this project and open it within your chosen IDE. Then, create a file and call it main.tf. This is where we will configure the code we need.
We will start by configuring the provider. For this article, we are going to use AzureRM to do this:
provider "azurerm" {
features {}
}
And now we move onto the resource block for Grafana. Microsoft offers an Azure-hosted Grafana service, which we will utilise as part of the deployment in this post:
resource "azurerm_resource_group" "grafana_rg" {
name = "grafana-rg"
location = "West Europe"
}
resource "azurerm_dashboard_grafana" "grafana" {
name = "grafana-instance-weu"
resource_group_name = azurerm_resource_group.grafana_rg.name
location = "West Europe"
grafana_major_version = 11
api_key_enabled = true
deterministic_outbound_ip_enabled = true
public_network_access_enabled = true
identity {
type = "SystemAssigned"
}
tags = {
key = "value"
}
}
Now we have the resource block defined, let's break down what has been configured:
Name - The name we want to give the Grafana instance
Resource_group_name - The resource group the instance will sit within
Location - The Azure region to deploy into. In this example, we are using West Europe
Grafana_major_version - The major version of Grafana to deploy. At the time of writing, version 11 is the latest supported by the Azure resource
Api_key_enabled - Enables API keys for programmatic access to Grafana. Useful if you plan to integrate with other tooling
Deterministic_outbound_ip_enabled - When enabled, the instance uses a fixed set of outbound IPs. This is helpful when you need to allow-list the Grafana instance against data sources behind a firewall
Public_network_access_enabled - Controls whether the Grafana endpoint is reachable from the public internet. In a production setup, you would likely want this set to
falseand use Private Link insteadIdentity - We are assigning a system-assigned managed identity, which we will use later to grant Grafana access to data sources such as Azure Monitor
Tags - Standard tagging block. Replace the placeholder key/value with your own tagging standard
Granting Access to Azure Monitor
On its own, the Grafana instance will deploy and be accessible, but it will not be able to read any data from Azure. To make this useful, we need to grant the managed identity permission to read metrics and logs from your subscription.
The most common role to assign is Monitoring Reader at the subscription scope. We can do this directly in Terraform using the azurerm_role_assignment resource:
data "azurerm_subscription" "current" {}
resource "azurerm_role_assignment" "grafana_monitoring_reader" {
scope = data.azurerm_subscription.current.id
role_definition_name = "Monitoring Reader"
principal_id = azurerm_dashboard_grafana.grafana.identity[0].principal_id
}
What we are doing here:
Data block - Pulling the current subscription ID so we do not need to hardcode it
Scope - Assigning the role at the subscription level. You can narrow this down to a resource group or resource if you want tighter control
Role_definition_name - The built-in role we want to assign. Monitoring Reader is enough for most dashboarding scenarios
Principal_id - This is the system-assigned identity we created as part of the Grafana resource
If you also want Grafana to read from Log Analytics, you may want to assign the Log Analytics Reader role as well.
Granting Yourself Access
One thing that often catches people out the first time they deploy Azure Managed Grafana is that deployment alone does not give your user account access to sign in. You need to assign yourself the Grafana Admin (or Grafana Editor/Viewer) role on the instance itself.
We can handle this in Terraform too:
data "azurerm_client_config" "current" {}
resource "azurerm_role_assignment" "grafana_admin" {
scope = azurerm_dashboard_grafana.grafana.id
role_definition_name = "Grafana Admin"
principal_id = data.azurerm_client_config.current.object_id
}
This grabs the object ID of whoever is running the Terraform apply and assigns them Grafana Admin rights on the new instance. If you are deploying this via a pipeline using a service principal, you will want to swap the principal_id out for the object ID of the user or group that should actually be managing the instance.
Deploying the Configuration
With everything in place, we can deploy. Open a terminal in your project directory and run the following:
az login
terraform init
terraform plan
terraform apply
The terraform plan step is a good habit to get into before any apply. It shows exactly what Terraform intends to create, modify, or destroy. Once you are happy with the plan output, confirm the apply and Terraform will provision your resource group, Grafana instance, and role assignments.
Deployment typically takes a few minutes. Once complete, head over to the Azure Portal, find your Grafana resource, and click the endpoint URL listed on the overview page. This will take you to the Grafana UI, where you can now start building dashboards.
You can commit the code to a repository and build a pipeline to deploy this also, but for this post, we executed it locally.
Wrapping Up
In a handful of lines of Terraform, we have a fully managed Grafana instance on Azure, wired up with a managed identity and the right permissions to start pulling data from Azure Monitor. No VMs to patch, no containers to maintain, and updates are handled by Microsoft.
In future posts, I will cover locking the instance down behind Private Link, integrating Grafana with Azure Managed Prometheus, and deploying dashboards as code so the whole stack can live alongside your infrastructure in source control.





