Azure Local - LENS - Deploy and auto-update the workbook with Azure DevOps


Intro

This article is the automation companion to Azure Local - LENS. In that post I showed how to manually import the Azure Local LENS workbook into Azure Monitor. Here I want to show how I use my own scripts and an Azure DevOps pipeline to keep that workbook deployed and updated with much less manual work.

The target audience here is the Azure Local administrator who may not live in Azure DevOps every day. So I will keep the pipeline parts practical and explain what the scripts do, why I split the workbook when it gets too large, and how the update flow works when the upstream workbook changes.

I also want to give credit where it is due: the Azure Local LENS workbook itself is excellent work by its creators, and this post is only about how I automate delivery of that work into my own environment.

HINT

LENS is still the same workbook. The difference in this post is that I am packaging the deployment into scripts and a pipeline so I can repeat it consistently across environments.

Why I built it this way

The manual process works, but it gets old quickly when you manage more than one tenant (like MSPs do):

  • I want one repeatable deployment path instead of copying JSON into the portal every time.
  • I want deployment to keep working even if the upstream GitHub repo is temporarily unavailable or returns an error.
  • I want the deployment to handle the Azure Monitor Workbooks payload limit without me having to think about it.

That is what this repo does. It pulls the latest upstream workbook JSON, caches it, optionally opens a PR when the cached version changes, and deploys the workbook to my own Azure subscription.

What is in the repo

The repository has two main parts:

FilePurpose
scripts/DeployAzureLocalLENSWorkbook.ps1Entry point for validation, splitting, and deployment
scripts/FetchCacheAndUpdateAzureLocalLENSWorkbook.ps1Downloads the upstream workbook and manages the cached copy
scripts/Invoke-WorkbookDeploy.ps1Thin wrapper around az rest for workbook deployment
scripts/Split-LENSWorkbook.ps1Splits the workbook when the payload gets too large
pipeline/DeployAzureLocalLENSWorkbook.ymlAzure DevOps pipeline definition

The important design choice is that the workbook is treated as code. The JSON file is cached in the repo, and the pipeline works from that cached copy.

Prerequisites

Before I run this in Azure DevOps, I need a few things in place:

  1. Azure CLI and PowerShell 7 if I want to test the scripts locally.
  2. An Azure DevOps project with a YAML pipeline.
  3. An Azure service connection that can deploy to the target subscription. If you need to set one up, Microsoft has a good guide here: https://learn.microsoft.com/en-us/azure/devops/pipelines/release/configure-workload-identity?view=azure-devops&tabs=managed-identity
  4. Permission to create or update workbook resources in the target resource group.
  5. Allow scripts to access the OAuth token enabled if I want the pipeline to create pull requests for cache updates.

HINT

The service connection name must match the target subscriptionId. That keeps the pipeline simple and avoids extra mapping logic. If your environment uses a different naming pattern, adjust the pipeline to point at the correct service connection name.

How the Azure DevOps pipeline is set up

The pipeline is defined in pipeline/DeployAzureLocalLENSWorkbook.yml, and it runs on a daily schedule to check for upstream changes. I can still run it manually when I want to, but the schedule gives me a regular update check without any extra work.

In Azure DevOps I import the YAML, point it at the repo, and then run it with runtime inputs. For admins who do not use Azure DevOps every day, runtime inputs are just the values you fill in when you click Run pipeline.

The key inputs are:

InputWhat I use it for
subscriptionIdTarget subscription for the workbook deployment
resourceGroupNameResource group where the workbook is created
locationAzure region for the workbook resource
workbookDisplayNameFriendly name shown in the portal
workbookResourceNameDeterministic workbook resource name
capacityWorkbookResourceNameOptional child workbook for capacity views
cachePathRepo path to the cached workbook JSON
githubWorkbookUrlUpstream raw JSON URL
createCacheUpdatePullRequestWhether the pipeline opens a PR when the cache changes

What happens when the pipeline runs

When I start the pipeline, Azure DevOps creates a job that checks out the repo and runs the PowerShell scripts.

The job first fetches the upstream workbook JSON and compares it with the cached version in the repo. If the upstream workbook has changed, the pipeline updates the cache and can open a pull request so I can review the change before it reaches my repository.

After that, the deployment script checks the workbook size. Azure Monitor Workbooks has a hard payload limit of about 600 KB, so the scripts use a conservative split threshold of 500 KB.

If the workbook is too large, the split logic does two things:

  1. Extracts the capacity-tab-group into a standalone capacity child workbook.
  2. Rewrites the Capacity tab in the main workbook so it opens the child workbook through a WorkbookTemplate link.

The capacity child workbook is deployed first, followed by the main workbook. If the workbook stays under the limit, the pipeline deploys it as a single resource.

Deploying the workbook in Azure

Once the job finishes, I can see the deployed workbook resources in Azure.

The main workbook is what I use day to day in Azure Monitor.

That gives me the same LENS experience I described in the manual deployment article, but now I can repeat the deployment, review upstream changes, and keep the workbook current with far less effort.

Running the scripts locally

I still like being able to test the scripts outside Azure DevOps. The repo supports that as well.

Fetch and update the cache

Set-Location scripts

pwsh -File .\FetchCacheAndUpdateAzureLocalLENSWorkbook.ps1 `
  -CachePath "cache/AzureLocal-LENS-Workbook/AzureLocal-LENS-Workbook.json" `
  -GitHubWorkbookUrl "https://raw.githubusercontent.com/Azure/AzureLocal-LENS-Workbook/main/AzureLocal-LENS-Workbook.json" `
  -CreateCacheUpdatePullRequest $false `
  -CacheUpdateBranch "autoupdate/azurelocal-lens-workbook-cache" `
  -CacheUpdateTargetBranch "main"

Deploy the workbook

az login
az account set --subscription "00000000-0000-0000-0000-000000000000"

Set-Location scripts

pwsh -File .\DeployAzureLocalLENSWorkbook.ps1 `
  -SubscriptionId "00000000-0000-0000-0000-000000000000" `
  -ResourceGroupName "rg-monitoring-shared" `
  -Location "westeurope" `
  -WorkbookDisplayName "Azure Local LENS Workbook" `
  -WorkbookResourceName "azure-local-lens-workbook" `
  -WorkbookJsonPath "cache/AzureLocal-LENS-Workbook/AzureLocal-LENS-Workbook.json" `
  -CapacityWorkbookResourceName "azure-local-lens-workbook-capacity"

Why this is useful for Azure Local admins

If you already manage Azure Local, you probably do not want to update things manually. Using Azure DevOps as the control plane for deployment and updates gives me:

  • A workbook that stays current when the upstream project changes,
  • A deployment path I can repeat across subscriptions and tenants
  • Reviewable update flow instead of ad hoc portal edits.

That makes it much easier to standardize LENS deployment across tenants.

If you want to try it out, the scripts and pipeline definition are available at github.com/chkja/azureLocalLENSPipelineDeploy. Feel free to adapt them to fit your own naming conventions and environments — and if you run into issues or have improvements, pull requests are welcome.