Configuring diagnostic settings for Azure services using bicep

Posted by Rik Hepworth on Friday, May 17, 2024

If you’re hosting your application infrastructure in Azure and consolidate the diagnostic and audit information from your services into Log Analytics or elsewhere, it’s pretty easy to do using Bicep. It’s not very well documented, however, which is the reason for this post.

A quick description of our problem

If we’re building an application using Azure services then it’s important to think about telemetry. In simple terms, we have two sources of telmetry: Our application code should be instrumented to emit telemetry, either using Open Telemetry, or the Application Insights SDK (this isn’t an otel article - whichever works for you); The Azure services also generate metrics, audit data and logging.

If we want to be able to effectively fault-find and monitor our application we really want to combine those two data sources. Fortunately, using a single Log Analytics workspace to act as a store for both works really well. However, documentation on how to configure the Diagnostic Settings for each service is somewhat sparse.

The good news, though, is that it’s standard across all services, and the bicep is remarkably straightfoward. In this post we’ll create a template using App Services as an example.

A worked example

Create the Log Analytics Workspace

I normally have separate modules for the different Azure services in my application to make it easier to read, edit and debug. In our example then, I have a template to create the Log Analytics workspace.

I use several parameters that are used to control things like tags and resource names.

Tip: The hidden-title tag is used by the portal and is great for putting human-readable names on your resources without compromising your resource naming policies. The content of the tag is dispaly in parentheses after the resource name in the portal.

Tip: In my opinion, the bicep is source code that is used to generate a compiled artefact - the ARM template. There is a field in the ARM template nameed contentVersion. We stamp that with the build version of our artefact, and the bicep reference deployment().properties.template.contentVersion enables us to put that value into a tag so it’s visible in the portal as to which version of my code was used to deploy the infrastructure.

// ** Variables **
// ***************

var LogAnalyticsWorkspaceName = toLower('log-${projectName}-${environment}-${location}')

// ** Resources **
// ***************

// Deploy Log Analytics Workspace
resource LogAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
    name: LogAnalyticsWorkspaceName
    location: location
    tags: {
        project: projectName
        environment: environment
        owner: owner
        displayName: 'Log Analytics'
        'hidden-title': 'Log Analytics'
        version: deployment().properties.template.contentVersion
    }
    properties: {
        sku: {
            name: logAnalyticsSku
        }
    }
}

Reference Log Analytics and create App Service Plan

Since our Log Analytics workspace is created elsewhere, we need to reference is as an existing resource when it comes to our service module. Doing this means I don’t need to work out the resource Id to be able to reference it.

This snippet from my App Services module creates a new hosting plan. It uses a parameter to control whether we want Linux or Windows, and another for the service tier (sku)

// ** Variables **
// ***************

var AppServicePlanName = toLower('asp-${projectName}-${environment}-${location}')
var LogAnalyticsWorkspaceName = toLower('log-${projectName}-${environment}-${location}')

// ** Resources **
// ***************

// Reference existing Log Analytics Workspace
resource LogAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = {
  name: LogAnalyticsWorkspaceName
}

//Deploy App Service Plan
resource AppServicePlan 'Microsoft.Web/serverfarms@2023-01-01' = {
  name: AppServicePlanName
  location: location
  tags: {
    project: projectName
    environment: environment
    owner: owner
    displayName: '${componentName} App Service Plan'
    'hidden-title': '${componentName} App Service Plan'
    version: deployment().properties.template.contentVersion
  }
  sku: {
    name: appServicePlanSku
  }
  properties: {
    reserved: toLower(appServicePlanKind) == 'linux' ? true : null
  }
  kind: appServicePlanKind
}

Configure the diagnostic settings

The DiagnosticSettings resource type is how we configure our service to emit it’s telemetry into our Log Analytics workspace. The resource is tied to our service using the scope property, rather than the more common parent.

Within the properties section, we configure what metrics and logs we want to send to Log Analytics. Getting the specific settings for each service is simplified by using the Azure Portal - the JSON View option in the blade where you configure the settings will provide what you need; you just need to convert it to bicep.

A hat tip to my friend Mikael Sand, who has a handy blog post showing you how to do this, so I’ll point you to him rather than run through in detail here.

Our example below for App Services is sending all metrics to the Log Analytics workspace

// Configure diagnostics on app service plan
resource DiagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {
  name: 'LogAnalytics'
  scope: AppServicePlan
  properties: {
    metrics: [
      {
        enabled: true
        retentionPolicy: {
          days: 0
          enabled: false
        }
        category: 'AllMetrics'
      }
    ]
    workspaceId: LogAnalyticsWorkspace.id
    logAnalyticsDestinationType: null
  }
}

The logAnalyticsDestinationType is normally null but in some services, other values can be specified. For example, below is the bicep for diagnostics on API Management. That service allows you to choose whether logs appear in dedicated service-specific tables or the common Log Analytics tables for service diagnostics or metrics. Here I am specifying ‘dedicated’ for the service-specific type.

// Configure diagnostics on api management
resource DiagnosticSettings 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {
  name: 'LogAnalytics'
  scope: ApiManagement
  properties: {
    logs: [
      {
        category: null
        categoryGroup: 'audit'
        enabled: true
        retentionPolicy: {
          days: 0
          enabled: false
        }
      }
      {
        category: null
        categoryGroup: 'allLogs'
        enabled: true
        retentionPolicy: {
          days: 0
          enabled: false
        }
      }
    ]
    metrics: [
      {
        enabled: true
        retentionPolicy: {
          days: 0
          enabled: false
        }
        category: 'AllMetrics'
      }
    ]
    workspaceId: LogAnalyticsWorkspace.id
    logAnalyticsDestinationType: 'Dedicated'
  }
}