diff --git a/src/000-cloud/020-observability/scripts/import-grafana-dashboards.sh b/src/000-cloud/020-observability/scripts/import-grafana-dashboards.sh index 85e4ec13b..926132bcc 100755 --- a/src/000-cloud/020-observability/scripts/import-grafana-dashboards.sh +++ b/src/000-cloud/020-observability/scripts/import-grafana-dashboards.sh @@ -8,6 +8,9 @@ set -euo pipefail # Environment variables (passed from Terraform) GRAFANA_NAME="${GRAFANA_NAME:-}" RESOURCE_GROUP_NAME="${RESOURCE_GROUP_NAME:-}" +# Name of the Azure Monitor Workspace whose auto-provisioned managed Prometheus +# datasource backs the AIO sample dashboard panels. +MONITOR_WORKSPACE_NAME="${MONITOR_WORKSPACE_NAME:-}" if [[ -z "$GRAFANA_NAME" || -z "$RESOURCE_GROUP_NAME" ]]; then echo "Error: GRAFANA_NAME and RESOURCE_GROUP_NAME environment variables must be set" @@ -29,10 +32,10 @@ retry() { return 0 fi if ((attempt >= max_attempts)); then - echo "Failed after ${max_attempts} attempts" + echo "Failed after ${max_attempts} attempts" >&2 return 1 fi - echo "Attempt ${attempt}/${max_attempts} failed, retrying in ${delay}s..." + echo "Attempt ${attempt}/${max_attempts} failed, retrying in ${delay}s..." >&2 sleep "$delay" ((attempt++)) done @@ -53,10 +56,52 @@ done # Import dashboard from GitHub echo "Importing AIO sample dashboard from GitHub..." -retry az grafana dashboard import \ - -g "$RESOURCE_GROUP_NAME" \ - -n "$GRAFANA_NAME" \ - --overwrite \ - --definition "https://raw.githubusercontent.com/Azure/azure-iot-operations/refs/heads/main/samples/grafana-dashboard/aio.sample.json" + +AIO_DASHBOARD_URL="https://raw.githubusercontent.com/Azure/azure-iot-operations/refs/heads/main/samples/grafana-dashboard/aio.sample.json" + +# The AIO sample dashboard is a Grafana export: every panel references its +# datasource through the "${DS_MANAGED_PROMETHEUS_INSTANCE}" __inputs placeholder. +# az grafana dashboard import only auto-binds that placeholder when a matching +# Prometheus datasource is Grafana's default. In Azure Managed Grafana the +# managed Prometheus datasource is not the default, so the placeholder would be +# imported literally and panels fail with +# "Datasource ${DS_MANAGED_PROMETHEUS_INSTANCE} was not found". +# Resolve the managed Prometheus datasource uid and bind it before importing. +resolve_prometheus_datasource_uid() { + az grafana data-source list -g "$RESOURCE_GROUP_NAME" -n "$GRAFANA_NAME" -o json \ + | jq -r --arg name "$MONITOR_WORKSPACE_NAME" ' + (map(select(.name == $name)) | .[0].uid) + // (map(select(.type == "prometheus")) | .[0].uid) + // empty + ' +} + +DS_UID="$(retry resolve_prometheus_datasource_uid)" + +if [[ -z "$DS_UID" ]]; then + echo "Warning: could not resolve a managed Prometheus datasource; importing AIO dashboard as-is" + retry az grafana dashboard import \ + -g "$RESOURCE_GROUP_NAME" \ + -n "$GRAFANA_NAME" \ + --overwrite \ + --definition "$AIO_DASHBOARD_URL" +else + echo "Binding AIO dashboard datasource to Prometheus uid: ${DS_UID}" + TMP_DIR="$(mktemp -d)" + trap 'rm -rf "$TMP_DIR"' EXIT + AIO_DASHBOARD_FILE="${TMP_DIR}/aio.sample.json" + + curl -fsSL "$AIO_DASHBOARD_URL" \ + | jq --arg uid "$DS_UID" ' + walk(if type == "object" and .uid == "${DS_MANAGED_PROMETHEUS_INSTANCE}" then .uid = $uid else . end) + | del(.__inputs) + ' >"$AIO_DASHBOARD_FILE" + + retry az grafana dashboard import \ + -g "$RESOURCE_GROUP_NAME" \ + -n "$GRAFANA_NAME" \ + --overwrite \ + --definition "$AIO_DASHBOARD_FILE" +fi echo "Dashboard import completed successfully" diff --git a/src/000-cloud/020-observability/terraform/main.tf b/src/000-cloud/020-observability/terraform/main.tf index aa111c687..aea6418b9 100644 --- a/src/000-cloud/020-observability/terraform/main.tf +++ b/src/000-cloud/020-observability/terraform/main.tf @@ -130,8 +130,9 @@ resource "terraform_data" "apply_scripts" { command = "bash ${path.module}/../scripts/import-grafana-dashboards.sh" environment = { - GRAFANA_NAME = azurerm_dashboard_grafana.monitor.name - RESOURCE_GROUP_NAME = var.azmon_resource_group.name + GRAFANA_NAME = azurerm_dashboard_grafana.monitor.name + RESOURCE_GROUP_NAME = var.azmon_resource_group.name + MONITOR_WORKSPACE_NAME = azurerm_monitor_workspace.monitor.name } } }