Creating an Azure Blob storage container with Terraform in an Azure storage account with network rules restricting access

Photo by moollyem on Unsplash

Creating an Azure Blob storage container with Terraform in an Azure storage account with network rules restricting access

If you need to create an Azure Blob storage container in an Azure storage account with network rules restricting access, this post will outline the issues you'll encounter if you don't have appropriate access and how to create the Azure Blob storage container without adding additional Azure storage firewall rules.

Background

Recently I encountered a case where I needed to create an Azure Blob storage container in an Azure storage account with network rules enabled from a CI provider hosted runner with Terraform. The issue was that the CI provider hosted runners weren't permitted to access the Azure storage account due to the network rules. As of 2022-08-19, the Terraform AzureRM provider uses the Microsoft Storage APIs for provisioning of objects within an Azure storage account. Therefore, if you attempt to create an Azure Blob storage container within an Azure storage account with network rules preventing access from the host running Terraform, you'll get an error similar to the following

$ terraform apply
...

azurerm_storage_container.name: Creating...
│ Error: containers.Client#GetProperties: Failure responding to request: StatusCode=403 -- Original Error: autorest/azure: Service returned an error. Status=403 Code="AuthorizationFailure" Message="This request is not authorized to perform this operation."

Working around this issue

The Microsoft Storage APIs are subject to the network rules on Azure storage account. I needed a way to create the Azure Blob storage container using the Azure Resource Provider APIs for Blob containers which are not subject to the network rules on Azure storage account.

To achieve that goal, I leveraged the AzAPI Terraform Provider which according to the documentation

... is a very thin layer on top of the Azure ARM REST APIs. This provider compliments the AzureRM provider by enabling the management of Azure resources that are not yet or may never be supported in the AzureRM provider such as private/public preview services and features.

For example, to create an Azure Blob storage container inside of an Azure storage account with network rules enabled you can use the following azapi_resource snippet from the AzAPI provider

# Other resources omitted for brevity

resource "azapi_resource" "example_storage_container" {
  type      = "Microsoft.Storage/storageAccounts/blobServices/containers@2021-09-01"
  name      = "azapi-example"
  parent_id = "${azurerm_storage_account.example.id}/blobServices/default"

  body = jsonencode({
    properties = {
      # Set publicAccess to `None` to match the default behavior of
      # azurerm_storage_container. May also be set to `Blob` or `Container`.
      publicAccess = "None"
    }
  })
}

and a full working example of creating an Azure resource group, storage account, and blob storage container using the AzureRM and AzAPI provider to show how to tie it all together.

terraform {
  required_providers {
    azapi = {
      source = "azure/azapi"
    }
    azurerm = {
      source  = "hashicorp/azurerm"
    }
    random = {
      source  = "hashicorp/random"
    }
  }
}

provider "azapi" {}

provider "azurerm" {
  features {}
}

resource "random_string" "example" {
  length  = 24

  numeric = true
  special = false
  upper   = false
}

resource "azurerm_resource_group" "example" {
  name     = random_string.example.result
  location = "East US"
}

resource "azurerm_storage_account" "example" {
  name                     = random_string.example.result
  resource_group_name      = azurerm_resource_group.example.name
  location                 = azurerm_resource_group.example.location
  account_tier             = "Standard"
  account_replication_type = "LRS"

  network_rules {
    # Deny all access for proving the example. In reality you'd
    # have Virtual Network rules or Private Link access.
    default_action = "Deny"
  }
}

resource "azapi_resource" "example_storage_container" {
  type      = "Microsoft.Storage/storageAccounts/blobServices/containers@2021-09-01"
  name      = "azapi-example"
  parent_id = "${azurerm_storage_account.example.id}/blobServices/default"

  body = jsonencode({
    properties = {
      # Set publicAccess to `None` to match the default behavior of
      # azurerm_storage_container. May also be set to `Blob` or `Container`.
      publicAccess = "None"
    }
  })
}

Applying the previous Terraform configuration leads to a successful creation of the resource group, storage account, and blob storage container.

$ terraform apply

...

Plan: 4 to add, 0 to change, 0 to destroy.

...

Apply complete! Resources: 4 added, 0 changed, 0 destroyed.

Summary

I've shown you how you can leverage the AzAPI Terraform Provider to create an Azure Blob storage container in an Azure storage account with network rules restricting access by using the azapi_resource. If you commonly manage Azure resources with Terraform, keep the AzAPI provider in mind as it is a valuable tool for augmenting the AzureRM Terraform provider and can easily prevent you from having to implement worse workarounds to solve the same problem.

To quote a peer of mine

AzAPI feels like a cheat code