1
votes

I have the following use-case: I'm using a combination of the Azure DevOps pipelines and Terraform to synchronize our TAP for Grafana (v7.4). Intention is that we can tweak and tune our dashboards on Test, and push the changes to Acceptance (and Production) via the pipelines.

I've got one pipeline that pulls in the state of the Test environment and writes it to a set of json files (for the dashboards) and a single json array (for the folders). The second pipeline should use these resources to synchronize the Acceptance environment.

This works flawlessly for the dashboards, but I'm hitting a snag putting the dashboards in the right folder dynamically. Here's my latest working code:

resource "grafana_folder" "folders" {
 for_each = toset(var.grafana_folders)
 title = each.key
}

resource "grafana_dashboard" "dashboards" {
  for_each    = fileset(path.module, "../dashboards/*.json")
  config_json = file("${path.module}/${each.key}")
}

The folder resources pushes the folders based on a variable list of names that I pass via variables. This generates the folders correctly.

The dashboard resource pushes the dashboards correctly, based on all dashboard files in the specified folder.

But now I'd like to make sure the dashboards end up in the right folder. The provider specifies that I need to do this based on the folder UID, which is generated when the folder is created. So I'd like to take the output from the grafana_folder resource and use it in the grafana_dashboard resource. I'm trying the following:

resource "grafana_folder" "folders" {
 for_each = toset(var.grafana_folders)
 title = each.key
}

resource "grafana_dashboard" "dashboards" {
  for_each    = fileset(path.module, "../dashboards/*.json")
  config_json = file("${path.module}/${each.key}")
  folder = lookup(transpose(grafana_folder.folders), "Station_Details", "Station_Details")
  depends_on  = [grafana_folder.folders]
}

If I read the Grafana Provider github correctly, the grafana_folder resource should output a map of [uid, title]. So I figured if I transpose that map, and (by way of test) lookup a folder title that I know exists, I can test the concept.

This gives the following error:

on main.tf line 38, in resource "grafana_dashboard" "dashboards":
38: folder = lookup(transpose(grafana_folder.folders), "Station_Details", "Station_Details")

Invalid value for "default" parameter: the default value must have the same type as the map elements.

Both Uid and Title should be strings, so I'm obviously overlooking something.

Does anyone have an inkling where I'm going wrong and/or have suggestions on how I can do this (better)?

1

1 Answers

1
votes

I think the problem this error is trying to report is that grafana_folder.folders is a map of objects, and so passing it to transpose doesn't really make sense but seems to be succeeding because Terraform has found some clever way to do automatic type conversions to produce some result, but then that result (due to the signature of transpose) is a map of lists rather than a map of strings, and so "Station_Details" (a string, rather than a list) isn't a valid fallback value for that lookup.

My limited familiarity with folders in Grafana leaves me unsure as to what to suggest instead, but I expect the final expression will look something like the following:

  folder = grafana_folder.folders[SOMETHING].id

SOMETHING here will be an expression that allows you to know for a given dashboard which folder key it ought to belong to. I'm not seeing an answer to that from what you shared in your question, but just as a placeholder to make this a complete answer I'll suggest that one option would be to make a local map from dashboard filename to folder name:

locals {
  # a local value probably isn't actually the right answer
  # here, but I'm just showing it as a placeholder for one
  # possible way to map from dashboard filename to folder
  # name. These names should all be elements of
  # var.grafana_folders in order for this to work.
  dashboard_folders = {
    "example1.json" = "example-folder"
    "example2.json" = "example-folder"
    "example3.json" = "another-folder"
  }
}

resource "grafana_dashboard" "dashboards" {
  for_each = fileset("${path.module}/dashboards", "*.json")

  config_json = file("${path.module}/dashboards/${each.key}")
  folder      = grafana_folder.folders[local.dashboard_folders[each.key]].id
}