Introduction Link to heading

We want to manage our Azure IaC with terraform so we have as little manual work as we can. I was working one day and got the request from developers, the application is trying to access blob storage but it’s blocked by CORS rules. Off course I got to the work and came up with this terraform module I will share now.

Terraform time Link to heading

First as usual I have everything as modules. Terraform modules allow us to group multiple resources into one .tf file which can be later used in some other .tf files like your project files. To mention I also keep modules in one git repo and the project terraform files in another. This approach gives me easy way to update my modules.

Let’s start now by looking at storage module

resource "azurerm_storage_account" "main" {
  name                = var.name
  location            = var.location
  resource_group_name = var.resource_group_name

  account_kind             = "StorageV2"
  account_tier             = var.account_tier
  account_replication_type = var.replication
  access_tier              = var.access_tier

  min_tls_version = "TLS1_2"

  blob_properties {
    last_access_time_enabled = var.blob_last_access_time
    versioning_enabled       = var.blob_versioning
    delete_retention_policy {
      days = var.blob_delete_retention
    }
    container_delete_retention_policy {
      days = var.container_delete_retention
    }

    dynamic "cors_rule" {
      for_each = var.cors_rules
      content {
        allowed_origins    = cors_rule.value.allowed_origins
        allowed_methods    = cors_rule.value.allowed_methods
        allowed_headers    = cors_rule.value.allowed_headers
        exposed_headers    = cors_rule.value.exposed_headers
        max_age_in_seconds = cors_rule.value.max_age_in_seconds
      }
    }
  }

  share_properties {
    dynamic "cors_rule" {
      for_each = var.cors_rules
      content {
        allowed_origins    = cors_rule.value.allowed_origins
        allowed_methods    = cors_rule.value.allowed_methods
        allowed_headers    = cors_rule.value.allowed_headers
        exposed_headers    = cors_rule.value.exposed_headers
        max_age_in_seconds = cors_rule.value.max_age_in_seconds
      }
    }

  }

  tags = var.tags
}

resource "azurerm_storage_container" "main" {
  for_each              = var.containers
  name                  = each.key
  storage_account_name  = azurerm_storage_account.main.name
  container_access_type = each.value
}

resource "azurerm_storage_queue" "main" {
  for_each             = var.queues
  name                 = each.value
  storage_account_name = azurerm_storage_account.main.name
}

resource "azurerm_storage_table" "main" {
  for_each             = var.tables
  name                 = each.value
  storage_account_name = azurerm_storage_account.main.name
}

resource "azurerm_management_lock" "storage_account" {
  name       = var.name
  scope      = resource.azurerm_storage_account.main.id
  lock_level = "CanNotDelete"
  notes      = "Locked to avoid accidental removal"
}

This is what I came up as a module for all blob storages in my environments. In this module we have two cors_rules, first is dynamic cors_rule and another is under share_properties. This is because the first cors_rules are for blob_service and the share_properties is for file_service. To see full module with variables you can go to my github account here

Next, I have my project setup

storage.tf

module "main_storage" {
  source = "git@github.com:dewa55/terraform/modules.git"

  name                = "myblobstorage"
  location            = azurerm_resource_group.main.location
  resource_group_name = azurerm_resource_group.main.name

  account_tier = "Standard"
  replication  = "LRS"
  access_tier  = "Hot"

  cors_rules = local.api_cors

  tags = merge(
    tomap(
      {
        environment = var.environment
      }
    ),
    var.tags
  )

  containers = {
    "my-container" = "private"
  }
}

locals.tf

locals {
    api_cors = var.environment == "dev" ? [
    {
      allowed_origins    = ["https://localhost:4200", "http://localhost:4200"]
      allowed_methods    = ["GET", "DELETE", "POST", "PUT", "OPTIONS"]
      allowed_headers    = ["*"]
      exposed_headers    = ["*"]
      max_age_in_seconds = 10
    }
  ] : []
}

That would be it :)