Skip to main content
Oleh Hrechukh

Automation azure workbook with terraform IAC!

What Are Azure Workbooks? #

Before we dive into the workflow, let's briefly touch on what Azure Workbooks are. Azure Workbooks is a service within Azure that allows you to create rich, interactive reports and dashboards. Think of them as a canvas where you can combine text, queries, metrics, and parameters to create a single, consolidated view of your data.

Key features of Azure Workbooks include:

Common use cases for Azure Workbooks include monitoring application performance, analyzing user behavior, creating operational dashboards, and troubleshooting issues. They are a versatile tool for anyone who needs to make sense of the data generated by their Azure resources.

The Core Workflow: From Code to Content #

The code used for the examples in this article is available in this repository on GitHub: https://github.com/olehrechukh/azure-tf-workbooks-demo

This workflow is designed to be a continuous loop, allowing you to evolve your workbooks over time as your monitoring and reporting needs change.

  1. Create from Scratch with Terraform: The workflow begins by defining the workbook as a code asset in Terraform. This establishes a single source of truth for your workbook and ensures that it is managed in a repeatable and version-controlled way.
  2. Populate with Your Own Content: Once the workbook is created, you can use the Azure portal to design and add your own charts, tables, and other content. This is the creative phase of the workflow, where you can leverage the rich and interactive UI of Azure Workbooks to build your reports.
  3. Capture Your Content as Code: After you have designed your content in the UI, you will export the updated workbook definition as a JSON payload and use it to update your Terraform configuration. This captures the state of your workbook's content as code and allows you to redeploy it consistently across all your environments.
Sample workbook visualizations showing combined charts and metrics used in the demo workbook

A Deep Dive into the Workflow #

Let's now explore each part of this workflow in detail. For the purposes of this article, we will be creating a Log Analytics Workspace as the primary data source for our workbook. This is a common scenario, but the same principles apply to other data sources like Application Insights. The storage account in our example is used for simplicity to store our data, but your workbook can be connected to a wide variety of data sources.

1. Initialization with Empty State #

The first step of our workflow is to initialize the workbook with an empty state. As you can see in the main.tf file, the azurerm_application_insights_workbook resource is configured to use workbook.json, which contains a minimal payload for an empty workbook. This ensures that the workbook is created as a code-managed asset from the very beginning and provides a clean slate for our design work.

# main.tf
terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 4.44"
    }
  }
}

provider "azurerm" {
  features {}
}

data "azurerm_client_config" "current" {}
data "azurerm_subscription" "current" {}

resource "azurerm_resource_group" "rg" {
  name     = "rg-workbook"
  location = "eastus2"
}

resource "azurerm_storage_account" "sa" {
  name                     = "saexampleworkbook"
  resource_group_name      = azurerm_resource_group.rg.name
  location                 = azurerm_resource_group.rg.location
  account_tier             = "Standard"
  account_replication_type = "LRS"
}

resource "azurerm_log_analytics_workspace" "log_analytics" {
  name                = "law-workbook"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  sku                 = "PerGB2018"
}

resource "azurerm_application_insights_workbook" "monitoring" {
  name                = "85b3e8bb-fc93-40be-83f2-98f6bec18ba0"
  resource_group_name = azurerm_resource_group.rg.name
  location            = azurerm_resource_group.rg.location
  display_name        = "Monitoring"
  source_id           = lower(azurerm_log_analytics_workspace.log_analytics.id)

  data_json = jsonencode(templatefile("${path.module}/workbook.json", {
    fallbackResourceId  = "/subscriptions/${data.azurerm_subscription.current.subscription_id}/resourcegroups/${azurerm_resource_group.rg.name}/providers/microsoft.operationalinsights/workspaces/${azurerm_log_analytics_workspace.log_analytics.name}"
  }))

  tags = {
    "hidden-link:" = "Resource"
  }
}
// workbook.json
{
  "version": "Notebook/1.0",
  "items": [],
  "fallbackResourceIds": [
    "${log_analytics_workspace_id}"
  ],
  "$schema": "https://github.com/Microsoft/Application-Insights-Workbooks/blob/master/schema/workbook.json"
}

Running

terraform apply

with this configuration will create a new, empty workbook in your Azure subscription. This is your starting point, a blank canvas that is fully managed by your Terraform code.

We will discuss a using templatefile later.

2. Adding a Chart in the UI #

Now that you have an empty workbook, you can navigate to it in the Azure portal and start designing your content. For example, you can add a new chart that visualizes the number of storage account transactions by API name, helping you monitor which operations, such as retrieving blob properties or deleting containers, are being used most frequently.

Example metric chart: transactions by API name for a storage account

Do not save manually; it is not recommended, as Terraform will override it.

3. Exporting the Updated JSON #

Once you have ended your chart, you need to export the updated JSON definition. To do this, click on the "</>" button (Advanced Editor) in the workbook's toolbar. This will open the JSON editor with the new, updated payload. Copy the entire JSON content to your clipboard.

Workbook Advanced Editor view for exporting or editing the workbook JSON

4. Updating the Terraform Configuration #

Edit the workbook.json file and paste the exported JSON into the items section. Parameterize the resourceIds field so the value can be supplied by the calling module.

{
    "version": "Notebook/1.0",
    "items": [
        {
            "type": 10,
            "content": {
                "chartId": "workbook85d2d5d7-0e4c-4c24-8d42-47bf8300bdc0",
                "version": "MetricsItem/2.0",
                "size": 0,
                "chartType": 2,
                "resourceType": "microsoft.storage/storageaccounts",
                "metricScope": 0,
                "resourceIds": [
                    "${storageAccountId}"
                ],
                "timeContext": {
                    "durationMs": 3600000
                },
                "metrics": [
                    {
                        "namespace": "microsoft.storage/storageaccounts",
                        "metric": "microsoft.storage/storageaccounts-Transaction-Transactions",
                        "aggregation": 1,
                        "splitBy": [
                            "ApiName"
                        ],
                        "columnName": ""
                    }
                ],
                "title": "Transactions by API name",
                "showOpenInMe": true,
                "gridSettings": {
                    "rowLimit": 10000
                }
            },
            "customWidth": "50",
            "showPin": true,
            "name": "Transaction by api name"
        }
    ],
    "fallbackResourceIds": [
        "${fallbackResourceId}"
    ],
    "$schema": "https://github.com/Microsoft/Application-Insights-Workbooks/blob/master/schema/workbook.json"
}

Next, update your main.tf to reference the new JSON template:

  data_json = jsonencode(templatefile("${path.module}/workbook.json", {
    storageAccountId = "/subscriptions/${data.azurerm_subscription.current.subscription_id}/resourcegroups/${azurerm_resource_group.rg.name}/providers/Microsoft.Storage/storageAccounts/${azurerm_storage_account.sa.name}",
    fallbackResourceId  = "/subscriptions/${data.azurerm_subscription.current.subscription_id}/resourcegroups/${azurerm_resource_group.rg.name}/providers/microsoft.operationalinsights/workspaces/${azurerm_log_analytics_workspace.log_analytics.name}"
  }))

Understanding the templatefile Function #

The templatefile function is a powerful feature of Terraform that plays a crucial role in this workflow. It allows you to read a file from your local filesystem and render it as a template, substituting variables that you provide. The syntax is as follows:

templatefile(path, vars)

In the context of our workbook automation workflow, the templatefile function provides several key benefits:

By using the templatefile function, you can create a clean, modular, and reusable solution for managing your Azure Workbooks as code.

Running

terraform apply

again will update the workbook in place, adding the new chart to it.

Bingo, it works.

Final deployed workbook UI after applying the exported JSON via Terraform

5. The Iterative Loop #

This process of designing in the UI, exporting the JSON, and updating your Terraform configuration can be repeated as many times as needed. This allows you to evolve your workbooks over time, adding new charts, tables, and text as your monitoring requirements change. Each iteration is captured in your version control system, providing a complete history of your workbook's evolution.

Scaling the Workflow #

This iterative workflow can be easily scaled to manage a large number of workbooks across multiple environments. By using Terraform workspaces and creating a reusable module for your workbooks, you can ensure consistency and reduce code duplication.

Beyond the Basics: Advanced Workbooks and Public Templates #

The examples in this article are intentionally simple to illustrate the core workflow. However, the real power of Azure Workbooks lies in their ability to create rich, complex, and highly customized reports. You are encouraged to build your own advanced workbooks, but you don't have to start from scratch. There is a rich ecosystem of public workbook templates available that you can use as a starting point. A great place to find inspiration is the official Microsoft repository for Application Insights Workbooks on GitHub. This repository contains a large collection of community-contributed workbook templates for a wide variety of scenarios. To use a public template, you can simply download the JSON file, parameterize it with your own variables, and integrate it into your Terraform workflow as described in this article. This can save you a significant amount of time and effort and allow you to leverage the collective knowledge and experience of the Azure community.

Best Practices for the Iterative Workflow #

Conclusion #

Defining Azure Workbooks as code and iterating via the portal provides the best of both worlds: the flexibility of the Azure UI for rapid design, and the repeatability and auditability of Terraform for deployment and configuration management. Start with an empty, code-managed workbook, design interactively in the portal, export the JSON, and commit the template back into your Terraform configuration. Repeat this loop as requirements evolve.