Search code examples
azurenetworkingterraformazure-storage

Creating Azure Storage Containers in a storage account with network rules, with Terraform


I am trying to write Terraform that will create an Azure Storage account and then a bunch of storage containers inside it. An important detail is that the storage account has network rules that restrict access to a specific address space. This was causing the container creation to fail.

I managed to get around this by using azurerm_storage_account_network_rules, depending on the containers so not to block their creation. Something like this:

resource "azurerm_storage_account" "this" {
  name                = local.storage_name
  resource_group_name = azurerm_resource_group.this.name
  location            = var.location

  account_tier             = "Standard"
  account_kind             = "StorageV2"
  is_hns_enabled           = true
  account_replication_type = "LRS"
}

resource "azurerm_storage_container" "data" {
  for_each = toset(var.storage_containers)

  name                  = each.value
  storage_account_name  = azurerm_storage_account.this.name
  container_access_type = "private"
}

# FIXME This order prevents destruction of infrastructure :(
resource "azurerm_storage_account_network_rules" "this" {
  storage_account_id   = azurerm_storage_account.this.id

  default_action = "Deny"
  bypass         = ["AzureServices"]

  virtual_network_subnet_ids = [
    # Some address space here...
  ]

  # NOTE The order here matters: We cannot create storage
  # containers once the network rules are locked down
  depends_on = [
    azurerm_storage_container.data
  ]
}

This works for creating the infrastructure, but when I try to terraform destroy, I get a 403 authentication error:

Error: retrieving Container "data" (Account "XXX" / Resource Group "XXX"): 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.\nRequestId:XXX\nTime:XXX"

This is with my Service Principal, which has Contributor and User Access Administrator roles on the same subscription. Interestingly, when I'm logged in to the Azure Portal as myself (with the Owner role), I can add and remove storage containers regardless of the network rules being present.

So, is there a way of setting the Terraform dependencies such that they can both be built and destroyed without hitting any authentication conflicts? Alternatively, would upgrading my SP's role to Owner (or adding another, more targeted role) solve the problem?


Solution

  • This is an expected behavior as you are setting up the network rules for the storage account to deny and only bypassing the Azure Services.

    When you deny and bypass Azure Services the Azure Services like Azure Portal's IP gets the access to the storage account and you are able to delete it . But at the same time , when you use terraform to perform a destroy , then it denies because your IP which is being used by your machine to send terraform requests to Azure is not bypassed.

    I tested your code like below with the same permissions:

    enter image description here

    enter image description here

    As a Solution you will have to add ip rules while creating the storage account to the add the client_ip like below :

    enter image description here

    provider "azurerm" {
        features{}
      client_id="f6a2f33d-xxxx-xxxx-xxxx-xxxx"
      client_secret= "GZ67Q~xxxx~3N-qLT"
      tenant_id = "72f988bf-xxxx-xxxx-xxxx-2d7cd011db47"
      subscription_id="948d4068-xxxx-xxxx-xxxx-xxxx"
    }
    
    locals {
      storage_name = "ansumantestsacc12"
      subnet_id_list = [
          "/subscriptions/xxxx/resourceGroups/xxxx/providers/Microsoft.Network/virtualNetworks/xxxx/subnets/xxxx"
      ]
      my_ip = ["xx.xx.xx.xxx"] # IP used by me
    }
    variable "storage_containers" {
      default = [
          "test",
          "terraform"
      ]
    }
    data "azurerm_resource_group" "this" {
      name = "ansumantest"
    }
    
    resource "azurerm_storage_account" "this" {
      name                = local.storage_name
      resource_group_name = data.azurerm_resource_group.this.name
      location            = data.azurerm_resource_group.this.location
    
      account_tier             = "Standard"
      account_kind             = "StorageV2"
      is_hns_enabled           = true
      account_replication_type = "LRS"
    }
    
    resource "azurerm_storage_container" "data" {
      for_each = toset(var.storage_containers)
    
      name                  = each.value
      storage_account_name  = azurerm_storage_account.this.name
      container_access_type = "private"
    }
    
    # FIXED
    resource "azurerm_storage_account_network_rules" "this" {
      storage_account_id   = azurerm_storage_account.this.id
    
      default_action = "Deny"
      bypass         = ["AzureServices"]
      ip_rules       = local.my_ip # need to set this to use terraform in our machine
      virtual_network_subnet_ids = local.subnet_id_list
    
      # NOTE The order here matters: We cannot create storage
      # containers once the network rules are locked down
      depends_on = [
        azurerm_storage_container.data
      ]
    }
    

    Output:

    enter image description here