Use Terraform
Note
Grafana Workflows is currently in public preview. Grafana Labs offers limited support, and breaking changes might occur prior to the feature being made generally available.
You can manage Grafana Workflows with the Grafana Terraform provider.
The provider doesn’t have a dedicated workflow resource.
Instead, you use its general-purpose grafana_apps_generic_resource, which accepts a workflow definition as its manifest.
Terraform creates, updates, and deletes workflows. It doesn’t run them. To run a workflow or read its results, refer to Use gcx or Use the API.
Note
The Grafana Terraform provider marks
grafana_apps_generic_resourceas experimental, and the way it compares changes might change. Pin the provider version, and review each plan before you apply it.
Before you begin
Before you start, make sure you have:
- Terraform installed.
- A Grafana Cloud stack with the Workflows app installed.
- A service account token with the Editor basic role and edit access to the folders Terraform writes to. For details, refer to Service accounts and Manage access.
Configure the provider
Add the Grafana provider to your Terraform files, and set url to your stack’s URL.
The provider finds your stack’s namespace for you.
terraform {
required_providers {
grafana = {
source = "grafana/grafana"
version = ">= 4.46.0"
}
}
}
provider "grafana" {
url = "https://<STACK_SLUG>.grafana.net/"
}Keep the token out of your Terraform files.
Instead, set it in the GRAFANA_AUTH environment variable before you run Terraform:
export GRAFANA_AUTH="<SERVICE_ACCOUNT_TOKEN>"Create a workflow from a YAML file
Keep each workflow in its own YAML file, and load the file into Terraform with the yamldecode function.
To start from a workflow you built in the editor, copy its definition from More > View JSON.
To create a workflow with Terraform, follow these steps:
Save the following definition as
workflows/incident-kickoff.yaml, and replace FOLDER_UID with your folder UID.The workflow runs when someone creates an incident. Its one step builds a summary.
apiVersion: workflows.ext.grafana.app/v1alpha1 kind: Definition metadata: name: incident-kickoff annotations: grafana.app/folder: <FOLDER_UID> spec: name: Incident kickoff enabled: true startWhen: matchingRules: - eventNameRegex: grafana_irm_app\.incident\.updated filter: "inputs.data.eventKind == 'incident_created'" schedules: [] steps: - id: summary name: Build summary type: transform inputs: template: "New incident: ${resources.incident.event.title}"If you copied the file from the editor or the API, remove the fields that Grafana adds. These are
resourceVersion,uid,generation, andmanagedFields.Add a resource that loads the file:
resource "grafana_apps_generic_resource" "incident_kickoff" { manifest = yamldecode(file("${path.module}/workflows/incident-kickoff.yaml")) }Preview the change:
terraform planThe plan shows one resource to add.
Create the workflow:
terraform applyIf the file has a problem, the apply stops and names it, for example,
step "summary" is missing required field "template" for type "transform".Confirm the workflow exists. Open the Workflows app, go to Manage workflows, and find Incident kickoff.
Manage a folder of workflow files
To manage every YAML file in a folder with one resource block, use for_each with the fileset function:
resource "grafana_apps_generic_resource" "workflow" {
for_each = fileset("${path.module}/workflows", "*.yaml")
manifest = yamldecode(file("${path.module}/workflows/${each.value}"))
}When you add a YAML file, the next apply creates a workflow. When you remove a file, the next apply deletes its workflow.
Create the Grafana folder with Terraform
Every workflow needs the grafana.app/folder annotation.
If Terraform also manages the Grafana folder, use the folder’s UID from Terraform instead of typing it.
The following example creates a folder.
It then uses the merge function to add the folder’s UID to your YAML file’s contents.
With this pattern, the YAML file can leave out annotations.
resource "grafana_folder" "automation" {
title = "Incident automation"
}
locals {
kickoff = yamldecode(file("${path.module}/workflows/incident-kickoff.yaml"))
}
resource "grafana_apps_generic_resource" "incident_kickoff" {
manifest = merge(local.kickoff, {
metadata = merge(local.kickoff.metadata, {
annotations = {
"grafana.app/folder" = grafana_folder.automation.uid
}
})
})
}Write workflow expressions in Terraform files
You can also write the definition directly in HashiCorp Configuration Language (HCL), the language of Terraform files.
Terraform and Workflows both use ${...} for expressions.
As a result, Terraform tries to fill in every Workflows expression in an HCL string.
To pass an expression through to Workflows, write it as $${...}.
In the following example, $${inputs.who} reaches Workflows as ${inputs.who}, and var.enabled is a Terraform variable:
resource "grafana_apps_generic_resource" "hello" {
manifest = {
apiVersion = "workflows.ext.grafana.app/v1alpha1"
kind = "Definition"
metadata = {
name = "hello-workflow"
annotations = {
"grafana.app/folder" = grafana_folder.automation.uid
}
}
spec = {
name = "Hello workflow"
enabled = var.enabled
startWhen = {
matchingRules = []
schedules = []
}
steps = [
{
id = "greet"
name = "Build greeting"
type = "transform"
inputs = {
template = "Hello $${inputs.who}"
}
}
]
}
}
}Terraform doesn’t fill in expressions in files that you load with file, so YAML files need no changes.
For workflows with many expressions, use YAML files.
Reference secrets
A workflow definition refers to a secret by its address, such as secret://..., not by its value.
You can safely keep these addresses in your Terraform files and in version control.
Create the secret values in the Workflows app or in Grafana Secrets Management. For details, refer to Manage secrets.
Import a workflow into Terraform
To manage a workflow that you built in the editor, import it into Terraform.
You need the workflow’s ID, which is its metadata.name.
The ID appears at the end of the editor URL.
To import a workflow, follow these steps:
Add a resource block whose manifest matches the workflow.
Import the workflow:
terraform import grafana_apps_generic_resource.incident_kickoff \ workflows.ext.grafana.app/v1alpha1/Definition/incident-kickoffWith Terraform 1.5 or later, you can use an
importblock instead:import { to = grafana_apps_generic_resource.incident_kickoff id = "workflows.ext.grafana.app/v1alpha1/Definition/incident-kickoff" }Preview the result:
terraform planThe first plan can show Terraform removing fields that Grafana adds, such as the
grafana.app/createdByannotation. These changes are safe to apply, and Grafana keeps its own fields. If the plan shows changes tospec, update your manifest until it matches the workflow.Apply the result, then run
terraform planagain.The plan shows no changes.
Handle changes made outside Terraform
Drift is a change that someone makes to a workflow outside Terraform, for example, in the editor.
When a workflow drifts, the next terraform plan shows the difference.
The next terraform apply replaces the change with the version in your Terraform files.
The provider marks each workflow it creates with the grafana.app/managedBy: terraform annotation.
The Workflows app doesn’t read this annotation, so it doesn’t stop edits in the editor.
The provider’s allow_ui_updates argument doesn’t change this.
Terraform overwrites those edits on the next apply.
Other tools do read the annotation.
For example, gcx skips Terraform-managed workflows unless you add --include-managed.
To keep Terraform as the only way to change a workflow, follow these guidelines:
- Put Terraform-managed workflows in their own Grafana folder.
- Give most people view access to that folder. Give edit access only to the Terraform service account and the people who maintain it.
- Run
terraform planon a schedule to find drift early.


