Managing Azure Storage Account with Terraform

Learn how to set up and manage Azure Storage Account using Terraform, including blobs, files, queues, and tables

Managing Azure Storage Account with Terraform

Azure Storage Account is a foundational service that provides scalable and secure storage in the cloud. This guide demonstrates how to set up and manage Storage Account using Terraform.

Video Tutorial

Learn more about managing Azure Storage with Terraform in this comprehensive video tutorial:

Prerequisites

  • Azure CLI configured with appropriate permissions
  • Terraform installed (version 1.0.0 or later)
  • Resource group created
  • Understanding of storage concepts

Project Structure

terraform-azure-storage/
├── main.tf
├── variables.tf
├── outputs.tf
├── modules/
│   └── storage/
│       ├── main.tf
│       ├── variables.tf
│       └── outputs.tf
└── policies/
    └── lifecycle.json

Storage Account Configuration

Create modules/storage/main.tf:

# Storage Account
resource "azurerm_storage_account" "main" {
  name                     = "${var.project_name}storage"
  resource_group_name      = var.resource_group_name
  location                 = var.location
  account_tier             = "Standard"
  account_replication_type = "GRS"
  account_kind            = "StorageV2"

  enable_https_traffic_only = true
  min_tls_version          = "TLS1_2"
  allow_nested_items_to_be_public = false

  network_rules {
    default_action             = "Deny"
    ip_rules                  = var.allowed_ips
    virtual_network_subnet_ids = [azurerm_subnet.storage.id]
    bypass                    = ["AzureServices"]
  }

  blob_properties {
    versioning_enabled       = true
    change_feed_enabled      = true
    last_access_time_enabled = true

    container_delete_retention_policy {
      days = 7
    }

    delete_retention_policy {
      days = 30
    }
  }

  queue_properties {
    logging {
      delete                = true
      read                  = true
      write                 = true
      version              = "2.0"
      retention_policy_days = 10
    }
  }

  static_website {
    index_document     = "index.html"
    error_404_document = "404.html"
  }

  identity {
    type = "SystemAssigned"
  }

  customer_managed_key {
    key_vault_key_id          = azurerm_key_vault_key.storage.id
    user_assigned_identity_id = azurerm_user_assigned_identity.storage.id
  }

  tags = var.tags
}

# Blob Container
resource "azurerm_storage_container" "data" {
  name                  = "data"
  storage_account_name  = azurerm_storage_account.main.name
  container_access_type = "private"

  metadata = {
    environment = var.environment
  }
}

# File Share
resource "azurerm_storage_share" "documents" {
  name                 = "documents"
  storage_account_name = azurerm_storage_account.main.name
  quota                = 50

  acl {
    id = "MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTI"

    access_policy {
      permissions = "rwdl"
      start      = "2025-01-01T00:00:00.0000000Z"
      expiry     = "2025-12-31T00:00:00.0000000Z"
    }
  }
}

# Queue
resource "azurerm_storage_queue" "messages" {
  name                 = "messages"
  storage_account_name = azurerm_storage_account.main.name

  metadata = {
    environment = var.environment
  }
}

# Table
resource "azurerm_storage_table" "configuration" {
  name                 = "configuration"
  storage_account_name = azurerm_storage_account.main.name
}

# Management Policy
resource "azurerm_storage_management_policy" "lifecycle" {
  storage_account_id = azurerm_storage_account.main.id

  rule {
    name    = "cleanupOldBlobs"
    enabled = true
    filters {
      prefix_match = ["data/archive"]
      blob_types   = ["blockBlob"]
    }
    actions {
      base_blob {
        tier_to_cool_after_days_since_modification_greater_than    = 90
        tier_to_archive_after_days_since_modification_greater_than = 180
        delete_after_days_since_modification_greater_than          = 365
      }
      snapshot {
        delete_after_days_since_creation_greater_than = 30
      }
      version {
        delete_after_days_since_creation = 90
      }
    }
  }
}

Network Configuration

  1. Virtual Network Integration
resource "azurerm_virtual_network" "main" {
  name                = "${var.project_name}-vnet"
  location            = var.location
  resource_group_name = var.resource_group_name
  address_space       = ["10.0.0.0/16"]

  subnet {
    name           = "storage-subnet"
    address_prefix = "10.0.1.0/24"
    service_endpoints = ["Microsoft.Storage"]
  }

  tags = var.tags
}

resource "azurerm_private_endpoint" "storage" {
  name                = "${var.project_name}-pe"
  location            = var.location
  resource_group_name = var.resource_group_name
  subnet_id           = azurerm_subnet.endpoints.id

  private_service_connection {
    name                           = "${var.project_name}-psc"
    private_connection_resource_id = azurerm_storage_account.main.id
    is_manual_connection          = false
    subresource_names            = ["blob", "file", "queue", "table"]
  }

  private_dns_zone_group {
    name                 = "default"
    private_dns_zone_ids = [
      azurerm_private_dns_zone.blob.id,
      azurerm_private_dns_zone.file.id,
      azurerm_private_dns_zone.queue.id,
      azurerm_private_dns_zone.table.id
    ]
  }
}

resource "azurerm_private_dns_zone" "blob" {
  name                = "privatelink.blob.core.windows.net"
  resource_group_name = var.resource_group_name
}

resource "azurerm_private_dns_zone" "file" {
  name                = "privatelink.file.core.windows.net"
  resource_group_name = var.resource_group_name
}

resource "azurerm_private_dns_zone" "queue" {
  name                = "privatelink.queue.core.windows.net"
  resource_group_name = var.resource_group_name
}

resource "azurerm_private_dns_zone" "table" {
  name                = "privatelink.table.core.windows.net"
  resource_group_name = var.resource_group_name
}

Security Configuration

  1. Role Assignments
resource "azurerm_role_assignment" "storage_blob_contributor" {
  scope                = "${azurerm_storage_account.main.id}/blobServices/default/containers/data"
  role_definition_name = "Storage Blob Data Contributor"
  principal_id         = var.contributor_principal_id
}

resource "azurerm_role_assignment" "storage_queue_contributor" {
  scope                = "${azurerm_storage_account.main.id}/queueServices/default/queues/messages"
  role_definition_name = "Storage Queue Data Contributor"
  principal_id         = var.contributor_principal_id
}
  1. Encryption Configuration
resource "azurerm_storage_account_customer_managed_key" "main" {
  storage_account_id = azurerm_storage_account.main.id
  key_vault_id       = azurerm_key_vault.main.id
  key_name           = azurerm_key_vault_key.storage.name
}

resource "azurerm_key_vault_key" "storage" {
  name         = "${var.project_name}-key"
  key_vault_id = azurerm_key_vault.main.id
  key_type     = "RSA"
  key_size     = 2048

  key_opts = [
    "decrypt",
    "encrypt",
    "sign",
    "unwrapKey",
    "verify",
    "wrapKey",
  ]
}

Monitoring Configuration

  1. Diagnostic Settings
resource "azurerm_monitor_diagnostic_setting" "storage" {
  name                       = "${var.project_name}-diag"
  target_resource_id        = azurerm_storage_account.main.id
  log_analytics_workspace_id = var.log_analytics_workspace_id

  metric {
    category = "Transaction"
    enabled  = true

    retention_policy {
      enabled = true
      days    = 30
    }
  }

  metric {
    category = "Capacity"
    enabled  = true

    retention_policy {
      enabled = true
      days    = 30
    }
  }
}

resource "azurerm_monitor_metric_alert" "storage" {
  name                = "${var.project_name}-capacity-alert"
  resource_group_name = var.resource_group_name
  scopes              = [azurerm_storage_account.main.id]
  description         = "Alert when storage capacity is high"

  criteria {
    metric_namespace = "Microsoft.Storage/storageAccounts"
    metric_name      = "UsedCapacity"
    aggregation      = "Average"
    operator         = "GreaterThan"
    threshold        = 85
  }

  action {
    action_group_id = var.action_group_id
  }
}

Advanced Features

  1. Object Replication
resource "azurerm_storage_object_replication" "main" {
  source_storage_account_id      = azurerm_storage_account.source.id
  destination_storage_account_id = azurerm_storage_account.destination.id

  rules {
    source_container_name      = azurerm_storage_container.source.name
    destination_container_name = azurerm_storage_container.destination.name
    copy_blobs_created_after  = "2025-01-01T00:00:00Z"
    
    filters {
      prefix_match = ["data/"]
      blob_types   = ["blockBlob"]
    }
  }
}
  1. CORS Configuration
resource "azurerm_storage_account" "cors" {
  # ... other configuration ...

  blob_properties {
    cors_rule {
      allowed_headers    = ["*"]
      allowed_methods    = ["GET", "POST"]
      allowed_origins    = ["https://example.com"]
      exposed_headers    = ["*"]
      max_age_in_seconds = 3600
    }
  }
}

Best Practices

  1. Performance

    • Choose appropriate tier
    • Enable CDN for static content
    • Configure caching
    • Use appropriate replication
  2. Security

    • Enable encryption
    • Use private endpoints
    • Implement RBAC
    • Configure firewalls
  3. Cost Optimization

    • Use appropriate tier
    • Implement lifecycle management
  4. Data Protection

    • Enable soft delete
    • Configure backup
    • Use versioning
    • Implement replication

Conclusion

You’ve learned how to set up and manage Azure Storage Account using Terraform. This setup provides:

  • Secure storage solution
  • Multiple storage services
  • Network isolation
  • Data protection

Remember to:

  • Monitor capacity usage
  • Review security settings
  • Manage lifecycle policies
  • Update access controls