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

1

7

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?

Delectable answered 7/2, 2022 at 17:29 Comment(1)
AIUI, Contributor + User Access Administrator is already pretty close to Owner. I think all that's missing is Microsoft.Blueprint/blueprintAssignments/write, Microsoft.Blueprint/blueprintAssignments/delete and Microsoft.Compute/galleries/share/action; none of which seem relevant. How is it that I am therefore free to perform these container actions in the Azure Portal?Delectable
B
12

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

Bedtime answered 8/2, 2022 at 7:12 Comment(1)
This gets trickier if the module is executed in CICD pipeline on publicly hosted runners or Microsoft hosted agents.Molloy

© 2022 - 2024 — McMap. All rights reserved.