Azure DiagnosticSettings and Bicep

This is not the explain everything article, but something I wrote as a guide to how I usually solve the issue of DiagnosticSettings.

The basics

I will assume you know what Diagnostics are within Azure and that you know how to create and deploy Bicep. This post aims at showing you how you can solve how to connect Azure Diagnostigs to your resources, using Bicep.

The Scenario

In this scenario I will use something very specific but it will work just as well for your scenario. I am using autoscaling for an Azure Function environment (or Service Plan if you prefer). If the function gets a lot of traffic, the autoscaler will add an additional instance and then remove instances if the traffic goes down.

The autoscaler can alert users whenever it fires. Either by sending emails or using a webhook. However you also need a record of when the autoscaler triggered. That is very useful when you want to analyze traffic and response times.

Bicep and DiagnosticSettings

A diagnostic setting is different compared to, lets say an Azure Storage. Normally, a resource can be deployed by itself but diagnosticsettings need to be connected to an existing resource. This is called Extension Resource.

When deploying an extension resource you simply need to tell it which other resource it is connected to. You do this using the scope property.

Here is an example from my Bicep file:

resource LogAnalyticsConnection 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {
  name: 'scaling'
  scope: FunctionplanElastic_Autoscale
  properties: {...}

The FunctionplanElastic_Autoscale is the autoscale I have created for my function.

The problem

When deploying a diagnostic setting you might not always know which metrics are available to you, and in some cases the metrics differ between the portal and the APIs used by Azure for deployment. So using the portal is not the best way, because you get strange errors complaining about different metrics not being available.

Another problem is that diagnostic settings are not exported as a part of the resource, so finding the settings can be really tricky.

A solution

This is in two parts: Finding what resource to scope, and finding what metrics and logs are available to you.

Finding the scope

This is the easy part. When you navigate the Azure Portal you connect diagnostic settings to a resource. That is the scope you are looking for. If you need it for an Azure Storage, that storage is the scope. In my case, I needed it for an autoscaling resource which in turn is connected to an Azure function. In this case, the diagnostic settings should be connected to the autoscaler.

Finding the Logs and Metrics

There is an API for this called Diagnostic Settings – List!
If you call the API you will get the possible diagnostic settings for that particular resource, including syntax. Using the API is a little tricky but here goes:

Authenticating the API

The caller needs to have read access to the resource. I recommend you use my “Login as yourself” post to manage the API authorization.

Setting up the URI

This is the tricky part. Here is the documentation version GET{resourceUri}/providers/Microsoft.Insights/diagnosticSettings?api-version=2021-05-01-preview.

The tricky part is the resourceUri. Here is my version from Postman.

The resourceUri has 4 different parts:
– The subscriptionId: I think you know what this is.
– The resourceGroupName: Yeah, you know this too.
Provider this is the provider name of the resource type you are trying to access.
The easiest way to find this is to look in the URL when you access the resource in the Azure Portal. This always contains a /, for instance Microsoft.DataFactory/factories.
ResourceName: This is simply the name of the resource you are trying to access.

In my scenario:

This replies back with this body:

    "value": [
                "metrics": [
                        "category": "AllMetrics",
                        "enabled": false,
                        "retentionPolicy": {
                            "enabled": false,
                            "days": 0
                "logs": [
                        "category": "AutoscaleEvaluations",
                        "categoryGroup": null,
                        "enabled": false,
                        "retentionPolicy": {
                            "enabled": false,
                            "days": 0
                        "category": "AutoscaleScaleActions",
                        "categoryGroup": null,
                        "enabled": true,
                        "retentionPolicy": {
                            "enabled": false,
                            "days": 0
                "logAnalyticsDestinationType": null
            "identity": null

Note! There is two small issues here.
1. If you have selected any logging, then that is the configuration that will show up. To know which features are available, the option has to be unconfigured.
2. If you have not added any diagnostic settings before, the APIs returns an empty list.

So now I know which features are available. I need both logging options, so I will add allLogs to my Bicep.

Updating the Bicep file

My finished Bicep looks like this:

param logAnalyticsResourceId string
resource LogAnalyticsConnection 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = {
  name: 'scaling'
  scope: FunctionplanElastic_Autoscale
  properties: {
    workspaceId: logAnalyticsResourceId
    logs: [
        enabled: true
        categoryGroup: 'allLogs'
        retentionPolicy: {
          days: 30


Adding diagnostic settings needs to be done using another process since they cannot be downloaded. You can access and API to get the settings available to you.

If you need the files used in this scenario you can find them in my GitHub Repo.

The parts of a SAS Key

Whenever I generated a SAS key for allowing access to a storage I have always wondered what all the different parts actually mean, today I found out.

(Source: MS Learn I added some information)

Lets look at this generated key:

Parameter Example Description
Resource URI Defines the Azure Storage endpoint and other parameters. This example defines an endpoint for Blob Storage and indicates that the SAS applies to service-level operations. When the URI is used with GET, the Storage properties are retrieved. When the URI is used with SET, the Storage properties are configured
Storage version sv=2015-04-05 For Azure Storage version 2012-02-12 and later, this parameter indicates the version to use. This example indicates that version 2015-04-05 (April 5, 2015) should be used.
Storage service ss=bf Specifies the Azure Storage to which the SAS applies. This example indicates that the SAS applies to Blob Storage (b) and Azure Files (f). Also available are Queue (q) and Table (t)
Start time st=2015-04-29T22%3A18%3A26Z (Optional) Specifies the start time for the SAS in UTC time. This example sets the start time as April 29, 2015 22:18:26 UTC. If you want the SAS to be valid immediately, omit the start time.
Expiry time se=2015-04-30T02%3A23%3A26Z Specifies the expiration time for the SAS in UTC time. This example sets the expiry time as April 30, 2015 02:23:26 UTC.
Allowed resource type srt=s Specifies which resource types are accessible via the SAS. This example specifies that the accessible resource is in Blob Storage. Service (s), Container (c) and Object (o).
Permissions sp=rw Lists the permissions to grant. This example grants access to read (r) and write (w) operations.
IP range sip= Specifies a range of IP addresses from which a request is accepted. This example defines the IP address range through
Protocol spr=https Specifies the protocols from which Azure Storage accepts the SAS. This example indicates that only requests by using HTTPS are accepted, and why should you accept anything else?
Signature sig=F%6GRVAZ5Cdj2Pw4tgU7IlSTkWgn7bUkkAg8P6HESXwmf%4B Specifies that access to the resource is authenticated by using an HMAC signature. The signature is computed over a string-to-sign with a key by using the SHA256 algorithm, and encoded by using Base64 encoding.

Since permissions (sp) is a little more complex I listed them in a separate table.

Allowed persmission Description
r Read
w Write
d Delete
l List
a Add
c Create
u Update
p Process
i Immutable storage
y Permanently delete
x Enable deletion of versions
t Read/Write blob index
f Filter Blog index

Now I can finally decrypt a signature to find out which access rights have been assigned.

Pitfall when deploying Azure Function v4 and .net 6

I was tasked with setting up a Function environment. The Function was supposed to be hosted as a version 4 (~4) and was written in .net 6. No problem, right? Wrong!

The error

This was the error I got:

Your app is pinned to an unsupported runtime version for '~4'. For better performance, we recommend using one of our supported versions instead: ~3.

To me that seems like a you problem but as ever Azure says it’s mine.

The hunt

I used Bicep to deploy the Azure function, but in order to find all the settings that might be needed I opened the JSON view of the Function in the Azure Portal. The Function was pushed to development by the developer and it was up to me to deploy it in TEST and PROD.

The JSON view is an awesome feature. On the overview page of almost all services, you will find it up and to the right. It’s just a link:

If you click it, you get the JSON/ARM representation of the service:

You can find all the settings that you might want to look up, such as Always On or which server farm it is connected to.

The find

I found this article from Microsoft, which I recommend, but at the same time it should not have to exists. It addresses all kinds of issues you can encounter when using Azure Functions 4.

The solution

You need to tell Azure what version of dotnet you are using. This only needs to be done when using version 4 and .net 6.
Add this setting to your Bicep:

properties: {
     siteConfig: {
         netFrameworkVersion: 'v6.0'

Then redeploy and the error message dissappears.

The grief

Now the pitfall part is that if you look into the JSON view of the Function and try to find that setting … it is null. Even after your redeploy.

So, how are we supposed to know that needs to be set?

Boo! Boo I say.

Custom domain for a static webapp using Bicep

Bicep and static web apps

In my experience, static web apps is almost too easy. Setting up one is really easy and deploying code is only one single step! This too easy approach is found in setting up custom domains.

There is a really good and easy to follow article in the official docs. There is even a video showing you the process. It does not, however, show you how to do it using Bicep.

My Bicep for provisioning the static app

Here is the Bicep I use to provision a new staticweb app:

var webappName = 'Identifier-${env}-stapp'

resource staticwebApplication 'Microsoft.Web/staticSites@2021-03-01' = {
  name: webappName
  location: location
    allowConfigFileUpdates: true
  sku: {
    tier: 'Free'
    name: 'Free'

  tags: resourceGroup().tags

How to add a custom domain using Bicep

The steps for adding a custom domain are outlined in the documentation linked above, but the steps are these:

  1. Find your static web app’s autogenerated URL.
  2. Update your DNS with a CNAME-record pointing your domain to that autogenerated URL.
  3. Update the Custom Domain setting. (Run the Bicep update)

Step 1 and 2 has to be done before running the Bicep update.

The Bicep

The official documentation is very limited I am sorry to say. That is the reason I wrote this post.

Here is my Bicep for setting a custom domain:

var domain = {
  PROD: {
    fqdn: ''
  TEST: {
    fqdn: ''
  DEV: {
    fqdn: ''

resource staticwebApplicationDomain 'Microsoft.Web/staticSites/customDomains@2022-03-01' = {
  name: domain[env].fqdn
  parent: staticwebApplication

A couple of things to point out.

  • The parent is the static web app created above.
  • You do not need to add your SSL-cert, Azure takes care of that for you. Almost too simple.
  • The name is the domain you need to add.
  • Adding the domain takes about 10-15 minutes. So don’t give up.

Configuring Network settings for PostgreSQL using Bicep

At work I always get new Azure Services to deploy and I always use Bicep. Here is how to manage network settings for PostgrSQL flexible server. This is the service one, not the “run on a Linux VM one”. ALWAYS use the service flavor.

Basic PostgreSQL bicep

Getting the Bicep from an existing Azure resource is super simple. Use VS code and the Command Palette (Ctrl+Shift P) and type Bicep: Insert Resource. Bom! There is your Bicep code and to understand it, here is the documentation reference.

Network settings

These are not found in the export and you have to add them manually (not great), but that is because it is a subtype. It is a separate type but can only exist when connected to another type. The definition is not hard to find:

resource symbolicname 'Microsoft.DBforPostgreSQL/flexibleServers/firewallRules@2022-01-20-preview' = {
  name: 'string'
  parent: resourceSymbolicName
  properties: {
    endIpAddress: 'string'
    startIpAddress: 'string'

These are the same settings that you would use for an Azure SQL Server, it is just connected to a DBforPostgreSQL flexible server.
Simply add all the IP-ranges you need to allow. Such as “the office in Stockholm” or “the consultant”.

Allow Azure Services

This is a special case and you need to configure a specific rule for it, allowing the IP-range to

resource AllowAzureServices 'Microsoft.DBforPostgreSQL/flexibleServers/firewallRules@2022-01-20-preview' = {
  parent: PostgreSQLDB
  name: 'AllowAllAzureIps'
  properties: {
    endIpAddress: ''
    startIpAddress: ''