Hi.
I’m trying to create Azure storage accounts using a module that takes a list of storage accounts to create. No problem with this.
I’m am now trying to extend this a little by permitting a list of storage containers for each storage account to be supplied as sub-components of each storage account.
The variable structure looks like this:
variable "storage_account" {
type = map(object({
name = string
access_tier = string
account_kind = string
account_replication_type = string
account_tier = string
public_network_access_enabled = bool
containers = optional(list(object({
name = string
container_access_type = optional(string, "private")
})))
network_rules = list(object({
default_action = string
bypass = optional(set(string))
ip_rules = optional(set(string))
virtual_network_subnet_ids = optional(set(string))
}))
}))
}
The module code looks like this:
resource "azurerm_storage_account" "storage" {
for_each = var.storage_account
name = each.key
resource_group_name = var.resource_group_name
location = var.location
account_tier = each.value.account_tier
account_replication_type = each.value.account_replication_type
account_kind = each.value.account_kind
access_tier = each.value.access_tier
public_network_access_enabled = each.value.public_network_access_enabled != null ? each.value.public_network_access_enabled : false
}
locals {
containers_list = merge([
for storage_account_name, details in var.storage_account: {
for container in details.containers:
"${storage_account_name}-${container.name}" =>
merge(details, {
storage_account_name = storage_account_name
container = container
})
}
]...)
}
resource "azurerm_storage_container" "storage" {
depends_on = [ azurerm_storage_account.storage ]
for_each = local.containers_list
name = each.value.name
storage_account_name = each.value.storage_account_name
}
output "test_output" {
value = local.containers_list
}
And my calling code looks like this:
module "storage_account" {
source = "./modules/azurerm_storage_account"
resource_group_name = "rg-temp"
location = "uksouth"
tags = {}
storage_account = {
"storage01" = {
name = "storagerandom0001"
account_kind = "StorageV2"
account_tier = "Standard"
account_replication_type = "LRS"
access_tier = "Hot"
public_network_access_enabled = true
network_rules = []
containers = [
{
name = "container01",
container_access_type = "private"
},
{
name = "container02",
container_access_type = "private"
}
]
},
"storage02" = {
name = "storagerandom0002"
account_kind = "StorageV2"
account_tier = "Standard"
account_replication_type = "LRS"
access_tier = "Hot"
public_network_access_enabled = true
network_rules = []
containers = [
{
name = "container03"
container_access_type = "private"
},
{
name = "container04"
container_access_type = "private"
}
]
}
}
}
The TF plan runs with this output:
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# module.storage_account.azurerm_storage_account.storage["storage01"] will be created
+ resource "azurerm_storage_account" "storage" {
+ access_tier = "Hot"
+ account_kind = "StorageV2"
+ account_replication_type = "LRS"
+ account_tier = "Standard"
+ allow_nested_items_to_be_public = true
+ cross_tenant_replication_enabled = true
+ default_to_oauth_authentication = false
+ dns_endpoint_type = "Standard"
+ enable_https_traffic_only = (known after apply)
+ https_traffic_only_enabled = (known after apply)
+ id = (known after apply)
+ infrastructure_encryption_enabled = false
+ is_hns_enabled = false
+ large_file_share_enabled = (known after apply)
+ local_user_enabled = true
+ location = "uksouth"
+ min_tls_version = "TLS1_2"
+ name = "storage01"
+ nfsv3_enabled = false
+ primary_access_key = (sensitive value)
+ primary_blob_connection_string = (sensitive value)
+ primary_blob_endpoint = (known after apply)
+ primary_blob_host = (known after apply)
+ primary_blob_internet_endpoint = (known after apply)
+ primary_blob_internet_host = (known after apply)
+ primary_blob_microsoft_endpoint = (known after apply)
+ primary_blob_microsoft_host = (known after apply)
+ primary_connection_string = (sensitive value)
+ primary_dfs_endpoint = (known after apply)
+ primary_dfs_host = (known after apply)
+ primary_dfs_internet_endpoint = (known after apply)
+ primary_dfs_internet_host = (known after apply)
+ primary_dfs_microsoft_endpoint = (known after apply)
+ primary_dfs_microsoft_host = (known after apply)
+ primary_file_endpoint = (known after apply)
+ primary_file_host = (known after apply)
+ primary_file_internet_endpoint = (known after apply)
+ primary_file_internet_host = (known after apply)
+ primary_file_microsoft_endpoint = (known after apply)
+ primary_file_microsoft_host = (known after apply)
+ primary_location = (known after apply)
+ primary_queue_endpoint = (known after apply)
+ primary_queue_host = (known after apply)
+ primary_queue_microsoft_endpoint = (known after apply)
+ primary_queue_microsoft_host = (known after apply)
+ primary_table_endpoint = (known after apply)
+ primary_table_host = (known after apply)
+ primary_table_microsoft_endpoint = (known after apply)
+ primary_table_microsoft_host = (known after apply)
+ primary_web_endpoint = (known after apply)
+ primary_web_host = (known after apply)
+ primary_web_internet_endpoint = (known after apply)
+ primary_web_internet_host = (known after apply)
+ primary_web_microsoft_endpoint = (known after apply)
+ primary_web_microsoft_host = (known after apply)
+ public_network_access_enabled = true
+ queue_encryption_key_type = "Service"
+ resource_group_name = "rg-temp"
+ secondary_access_key = (sensitive value)
+ secondary_blob_connection_string = (sensitive value)
+ secondary_blob_endpoint = (known after apply)
+ secondary_blob_host = (known after apply)
+ secondary_blob_internet_endpoint = (known after apply)
+ secondary_blob_internet_host = (known after apply)
+ secondary_blob_microsoft_endpoint = (known after apply)
+ secondary_blob_microsoft_host = (known after apply)
+ secondary_connection_string = (sensitive value)
+ secondary_dfs_endpoint = (known after apply)
+ secondary_dfs_host = (known after apply)
+ secondary_dfs_internet_endpoint = (known after apply)
+ secondary_dfs_internet_host = (known after apply)
+ secondary_dfs_microsoft_endpoint = (known after apply)
+ secondary_dfs_microsoft_host = (known after apply)
+ secondary_file_endpoint = (known after apply)
+ secondary_file_host = (known after apply)
+ secondary_file_internet_endpoint = (known after apply)
+ secondary_file_internet_host = (known after apply)
+ secondary_file_microsoft_endpoint = (known after apply)
+ secondary_file_microsoft_host = (known after apply)
+ secondary_location = (known after apply)
+ secondary_queue_endpoint = (known after apply)
+ secondary_queue_host = (known after apply)
+ secondary_queue_microsoft_endpoint = (known after apply)
+ secondary_queue_microsoft_host = (known after apply)
+ secondary_table_endpoint = (known after apply)
+ secondary_table_host = (known after apply)
+ secondary_table_microsoft_endpoint = (known after apply)
+ secondary_table_microsoft_host = (known after apply)
+ secondary_web_endpoint = (known after apply)
+ secondary_web_host = (known after apply)
+ secondary_web_internet_endpoint = (known after apply)
+ secondary_web_internet_host = (known after apply)
+ secondary_web_microsoft_endpoint = (known after apply)
+ secondary_web_microsoft_host = (known after apply)
+ sftp_enabled = false
+ shared_access_key_enabled = true
+ table_encryption_key_type = "Service"
+ blob_properties (known after apply)
+ network_rules (known after apply)
+ queue_properties (known after apply)
+ routing (known after apply)
+ share_properties (known after apply)
+ static_website (known after apply)
}
# module.storage_account.azurerm_storage_account.storage["storage02"] will be created
+ resource "azurerm_storage_account" "storage" {
+ access_tier = "Hot"
+ account_kind = "StorageV2"
+ account_replication_type = "LRS"
+ account_tier = "Standard"
+ allow_nested_items_to_be_public = true
+ cross_tenant_replication_enabled = true
+ default_to_oauth_authentication = false
+ dns_endpoint_type = "Standard"
+ enable_https_traffic_only = (known after apply)
+ https_traffic_only_enabled = (known after apply)
+ id = (known after apply)
+ infrastructure_encryption_enabled = false
+ is_hns_enabled = false
+ large_file_share_enabled = (known after apply)
+ local_user_enabled = true
+ location = "uksouth"
+ min_tls_version = "TLS1_2"
+ name = "storage02"
+ nfsv3_enabled = false
+ primary_access_key = (sensitive value)
+ primary_blob_connection_string = (sensitive value)
+ primary_blob_endpoint = (known after apply)
+ primary_blob_host = (known after apply)
+ primary_blob_internet_endpoint = (known after apply)
+ primary_blob_internet_host = (known after apply)
+ primary_blob_microsoft_endpoint = (known after apply)
+ primary_blob_microsoft_host = (known after apply)
+ primary_connection_string = (sensitive value)
+ primary_dfs_endpoint = (known after apply)
+ primary_dfs_host = (known after apply)
+ primary_dfs_internet_endpoint = (known after apply)
+ primary_dfs_internet_host = (known after apply)
+ primary_dfs_microsoft_endpoint = (known after apply)
+ primary_dfs_microsoft_host = (known after apply)
+ primary_file_endpoint = (known after apply)
+ primary_file_host = (known after apply)
+ primary_file_internet_endpoint = (known after apply)
+ primary_file_internet_host = (known after apply)
+ primary_file_microsoft_endpoint = (known after apply)
+ primary_file_microsoft_host = (known after apply)
+ primary_location = (known after apply)
+ primary_queue_endpoint = (known after apply)
+ primary_queue_host = (known after apply)
+ primary_queue_microsoft_endpoint = (known after apply)
+ primary_queue_microsoft_host = (known after apply)
+ primary_table_endpoint = (known after apply)
+ primary_table_host = (known after apply)
+ primary_table_microsoft_endpoint = (known after apply)
+ primary_table_microsoft_host = (known after apply)
+ primary_web_endpoint = (known after apply)
+ primary_web_host = (known after apply)
+ primary_web_internet_endpoint = (known after apply)
+ primary_web_internet_host = (known after apply)
+ primary_web_microsoft_endpoint = (known after apply)
+ primary_web_microsoft_host = (known after apply)
+ public_network_access_enabled = true
+ queue_encryption_key_type = "Service"
+ resource_group_name = "rg-temp"
+ secondary_access_key = (sensitive value)
+ secondary_blob_connection_string = (sensitive value)
+ secondary_blob_endpoint = (known after apply)
+ secondary_blob_host = (known after apply)
+ secondary_blob_internet_endpoint = (known after apply)
+ secondary_blob_internet_host = (known after apply)
+ secondary_blob_microsoft_endpoint = (known after apply)
+ secondary_blob_microsoft_host = (known after apply)
+ secondary_connection_string = (sensitive value)
+ secondary_dfs_endpoint = (known after apply)
+ secondary_dfs_host = (known after apply)
+ secondary_dfs_internet_endpoint = (known after apply)
+ secondary_dfs_internet_host = (known after apply)
+ secondary_dfs_microsoft_endpoint = (known after apply)
+ secondary_dfs_microsoft_host = (known after apply)
+ secondary_file_endpoint = (known after apply)
+ secondary_file_host = (known after apply)
+ secondary_file_internet_endpoint = (known after apply)
+ secondary_file_internet_host = (known after apply)
+ secondary_file_microsoft_endpoint = (known after apply)
+ secondary_file_microsoft_host = (known after apply)
+ secondary_location = (known after apply)
+ secondary_queue_endpoint = (known after apply)
+ secondary_queue_host = (known after apply)
+ secondary_queue_microsoft_endpoint = (known after apply)
+ secondary_queue_microsoft_host = (known after apply)
+ secondary_table_endpoint = (known after apply)
+ secondary_table_host = (known after apply)
+ secondary_table_microsoft_endpoint = (known after apply)
+ secondary_table_microsoft_host = (known after apply)
+ secondary_web_endpoint = (known after apply)
+ secondary_web_host = (known after apply)
+ secondary_web_internet_endpoint = (known after apply)
+ secondary_web_internet_host = (known after apply)
+ secondary_web_microsoft_endpoint = (known after apply)
+ secondary_web_microsoft_host = (known after apply)
+ sftp_enabled = false
+ shared_access_key_enabled = true
+ table_encryption_key_type = "Service"
+ blob_properties (known after apply)
+ network_rules (known after apply)
+ queue_properties (known after apply)
+ routing (known after apply)
+ share_properties (known after apply)
+ static_website (known after apply)
}
# module.storage_account.azurerm_storage_container.storage["storage01-container01"] will be created
+ resource "azurerm_storage_container" "storage" {
+ container_access_type = "private"
+ default_encryption_scope = (known after apply)
+ encryption_scope_override_enabled = true
+ has_immutability_policy = (known after apply)
+ has_legal_hold = (known after apply)
+ id = (known after apply)
+ metadata = (known after apply)
+ name = "storagerandom0001"
+ resource_manager_id = (known after apply)
+ storage_account_name = "storage01"
}
# module.storage_account.azurerm_storage_container.storage["storage01-container02"] will be created
+ resource "azurerm_storage_container" "storage" {
+ container_access_type = "private"
+ default_encryption_scope = (known after apply)
+ encryption_scope_override_enabled = true
+ has_immutability_policy = (known after apply)
+ has_legal_hold = (known after apply)
+ id = (known after apply)
+ metadata = (known after apply)
+ name = "storagerandom0001"
+ resource_manager_id = (known after apply)
+ storage_account_name = "storage01"
}
# module.storage_account.azurerm_storage_container.storage["storage02-container03"] will be created
+ resource "azurerm_storage_container" "storage" {
+ container_access_type = "private"
+ default_encryption_scope = (known after apply)
+ encryption_scope_override_enabled = true
+ has_immutability_policy = (known after apply)
+ has_legal_hold = (known after apply)
+ id = (known after apply)
+ metadata = (known after apply)
+ name = "storagerandom0002"
+ resource_manager_id = (known after apply)
+ storage_account_name = "storage02"
}
# module.storage_account.azurerm_storage_container.storage["storage02-container04"] will be created
+ resource "azurerm_storage_container" "storage" {
+ container_access_type = "private"
+ default_encryption_scope = (known after apply)
+ encryption_scope_override_enabled = true
+ has_immutability_policy = (known after apply)
+ has_legal_hold = (known after apply)
+ id = (known after apply)
+ metadata = (known after apply)
+ name = "storagerandom0002"
+ resource_manager_id = (known after apply)
+ storage_account_name = "storage02"
}
Plan: 6 to add, 0 to change, 0 to destroy.
However, the apply fails with this:
Plan: 6 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
module.storage_account.azurerm_storage_account.storage["storage01"]: Creating...
module.storage_account.azurerm_storage_account.storage["storage02"]: Creating...
╷
│ Error: creating Storage Account (Subscription: "e286703f-8ba4-4a0d-bd44-8fb115bdebcd"
│ Resource Group Name: "rg-temp"
│ Storage Account Name: "storage02"): performing Create: unexpected status 409 (409 Conflict) with error: StorageAccountAlreadyTaken: The storage account named storage02 is already taken.
│
│ with module.storage_account.azurerm_storage_account.storage["storage02"],
│ on modules/azurerm_storage_account/main.tf line 1, in resource "azurerm_storage_account" "storage":
│ 1: resource "azurerm_storage_account" "storage" {
│
╵
╷
│ Error: creating Storage Account (Subscription: "e286703f-8ba4-4a0d-bd44-8fb115bdebcd"
│ Resource Group Name: "rg-temp"
│ Storage Account Name: "storage01"): performing Create: unexpected status 409 (409 Conflict) with error: StorageAccountAlreadyTaken: The storage account named storage01 is already taken.
│
│ with module.storage_account.azurerm_storage_account.storage["storage01"],
│ on modules/azurerm_storage_account/main.tf line 1, in resource "azurerm_storage_account" "storage":
│ 1: resource "azurerm_storage_account" "storage" {
I think my loop syntax might be off a bit? I think for each container it’s trying to create a new storage account, rather than associating the storage containers with the storage accounts being created?
Can anyone see where I’ve messed up at all?
Appreciate any insights.