Learn how to use Terraform and the azurerm_storage_management_policy resource to automate Azure Blob Storage lifecycle rules. This guide keeps the implementation practical: move blobs to cool or archive tiers, delete old data, and target specific containers or paths without overcomplicating the Terraform code.

Terraform Azure Blob Storage azurerm_storage_management_policy Lifecycle Management Checked: 27 June 2026

Terraform azurerm_storage_management_policy: Azure Blob Lifecycle Rules Made Simple

This guide shows a simple, production-friendly way to create Azure Blob Storage lifecycle management rules with Terraform. The goal is not to build a complicated module. The goal is to help you implement the resource, understand the moving parts, and avoid the common mistakes around prefix matching, blob types, and rule actions.

Quick answer What the resource does Simple Terraform example Dynamic rules Prefix match Deploy and verify FAQ

Quick answer

Use azurerm_storage_management_policy when you want Azure Storage to automatically move blobs between access tiers or delete old blobs based on age. It is commonly used for cost optimisation, retention, archive rules, and cleanup of logs or exported data.

Move data to coolFor blobs that are not accessed often but still need quicker retrieval than archive.
Move data to archiveFor long-term retention where retrieval can be slower and more deliberate.
Delete old dataFor logs, exports, temporary data, or records that should expire after a defined period.
Simple rule of thumb: start with one clear lifecycle rule, confirm it targets the correct container or path, then add more rules only when there is a real business need.

What azurerm_storage_management_policy does

The Terraform azurerm_storage_management_policy resource manages a lifecycle management policy on an Azure Storage Account. In practice, that means Terraform creates the policy once, and Azure Storage evaluates the rules later as part of the storage lifecycle process.

Azure Blob lifecycle management policies can move blob data between hot, cool, cold, and archive tiers, and can also expire data at the end of its life. Microsoft documents this as a rule-based policy for automating blob tiering and deletion. The feature is supported for block blobs and append blobs in supported storage account types. Microsoft Learn: Azure Blob Storage lifecycle management overview

HashiCorp documents the Terraform resource with a rule block, optional filters, and an actions block. The actions block can include base_blob, snapshot, and version actions. Terraform Registry: azurerm_storage_management_policy

Important: lifecycle policies are not instant backup jobs. They are evaluated by Azure Storage after the policy exists. Use Azure Backup, snapshots, versioning, immutable storage, or replication when you need protection or recovery guarantees.

When this resource is a good fit

ScenarioGood use of lifecycle policyWhat to be careful with
Application logs Move old logs to cool/archive and delete them after a retention period. Make sure legal or audit retention requirements are understood before deleting.
Data exports Clean up daily or monthly exports that are only needed temporarily. Avoid applying the rule to the whole storage account unless that is intentional.
Compliance archive Move inactive records to archive after a defined period. Archive retrieval is slower and may involve rehydration planning.
General backups Manage storage cost for backup-like blob copies. Lifecycle policy itself is not a full backup strategy.

Simple Terraform example

If you are new to this resource, start with a normal static rule before introducing dynamic blocks. This is easier to read, easier to debug, and usually enough for one storage account.

main.tf

terraform {
  required_version = ">= 1.6.0"

  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 4.0"
    }
  }
}

provider "azurerm" {
  features {}
}

data "azurerm_resource_group" "example" {
  name = var.resource_group_name
}

resource "azurerm_storage_account" "example" {
  name                     = var.storage_account_name
  resource_group_name      = data.azurerm_resource_group.example.name
  location                 = data.azurerm_resource_group.example.location
  account_tier             = "Standard"
  account_replication_type = "LRS"
  account_kind             = "StorageV2"
  access_tier              = "Hot"

  min_tls_version                 = "TLS1_2"
  allow_nested_items_to_be_public = false
}

resource "azurerm_storage_container" "documents" {
  name                  = "documents"
  storage_account_name  = azurerm_storage_account.example.name
  container_access_type = "private"
}

resource "azurerm_storage_management_policy" "example" {
  storage_account_id = azurerm_storage_account.example.id

  rule {
    name    = "documents-cool-archive-delete"
    enabled = true

    filters {
      blob_types   = ["blockBlob"]
      prefix_match = ["documents/active/"]
    }

    actions {
      base_blob {
        tier_to_cool_after_days_since_modification_greater_than    = 30
        tier_to_archive_after_days_since_modification_greater_than = 180
        delete_after_days_since_modification_greater_than          = 2555
      }
    }
  }
}

variables.tf

variable "resource_group_name" {
  description = "Name of the existing resource group."
  type        = string
}

variable "storage_account_name" {
  description = "Globally unique Azure Storage Account name."
  type        = string
}

terraform.tfvars

resource_group_name  = "rg-storage-demo"
storage_account_name = "stlifecycleexample01"
Why this version is easier: it shows the real shape of the Terraform resource first. Once this works, you can decide whether you actually need a dynamic module.

The prefix_match detail people often get wrong

In Azure lifecycle policies, prefix_match is not just a random folder name. Microsoft’s policy structure documentation says each prefix must start with a container name. For example, to match blobs under sample-container/blob1/..., the prefix should look like sample-container/blob1. Microsoft Learn: lifecycle management policy structure

What you wantExample prefix_matchMeaning
All blobs in one container ["documents"] Targets blobs in the documents container.
Only blobs under a virtual folder ["documents/active/"] Targets blobs whose path starts with documents/active/.
Multiple containers or paths ["documents/", "logs/"] Targets more than one container or prefix in the same rule.
Practical warning: if you write prefix_match = ["active/"], Azure may not target what you expect unless active is actually the container name. For most real designs, use container-name/path/.

Dynamic rules without overengineering

If you need several lifecycle rules, use a simple map of rules. Avoid wrapping everything inside an extra policy1 layer unless you are building a reusable enterprise module that truly needs multiple policy definitions.

variables.tf

variable "lifecycle_rules" {
  description = "Lifecycle rules for the storage management policy."

  type = map(object({
    enabled      = optional(bool, true)
    blob_types   = optional(list(string), ["blockBlob"])
    prefix_match = optional(list(string), [])

    tier_to_cool_after_days    = optional(number)
    tier_to_archive_after_days = optional(number)
    delete_after_days          = optional(number)
  }))

  default = {}
}

terraform.tfvars

lifecycle_rules = {
  active_documents = {
    prefix_match               = ["documents/active/"]
    tier_to_cool_after_days    = 30
    tier_to_archive_after_days = 180
    delete_after_days          = 2555
  }

  old_exports = {
    prefix_match      = ["exports/"]
    delete_after_days = 90
  }
}

main.tf

resource "azurerm_storage_management_policy" "example" {
  storage_account_id = azurerm_storage_account.example.id

  dynamic "rule" {
    for_each = var.lifecycle_rules

    content {
      name    = rule.key
      enabled = rule.value.enabled

      filters {
        blob_types   = rule.value.blob_types
        prefix_match = rule.value.prefix_match
      }

      actions {
        base_blob {
          tier_to_cool_after_days_since_modification_greater_than    = rule.value.tier_to_cool_after_days
          tier_to_archive_after_days_since_modification_greater_than = rule.value.tier_to_archive_after_days
          delete_after_days_since_modification_greater_than          = rule.value.delete_after_days
        }
      }
    }
  }
}

This version is still flexible, but it remains readable. Each rule has a name, a target, and one or more actions. That is usually what people implementing this Terraform resource need.

Deploy and verify

After saving the Terraform files, run the normal Terraform workflow.

terraform fmt
terraform init
terraform validate
terraform plan
terraform apply

What to check in the plan

  • The storage account ID is correct.
  • Each lifecycle rule has the expected name.
  • blob_types is set to the correct blob type, usually blockBlob.
  • prefix_match starts with the container name.
  • The tiering and delete day values match your retention requirements.

What to check in Azure

  • Open the Storage Account in the Azure portal.
  • Go to Data management > Lifecycle management.
  • Confirm the rules are visible and enabled.
  • Confirm the filters target the expected containers or paths.
Do not test delete rules on important data first. Use a test container and short-lived sample blobs before applying delete rules to production containers.

Common mistakes

MistakeWhy it mattersBetter approach
Using a prefix without the container name The rule may not target the blobs you expect. Use container-name/path/, for example documents/active/.
Applying the rule to the whole account accidentally You may tier or delete more data than intended. Use prefix_match for the first version, then broaden later if needed.
Treating lifecycle management as backup Lifecycle rules manage cost and retention; they do not replace recovery design. Use backup, soft delete, versioning, replication, or immutability where needed.
Overbuilding the Terraform module A complicated variable structure makes the resource harder to understand and maintain. Start with one static rule, then move to a simple map only when needed.

FAQ

Does azurerm_storage_management_policy create backups?

No. It creates lifecycle management rules. Those rules can move or delete blobs based on conditions, but they are not a backup service. For backup and recovery, look at Azure Backup, blob soft delete, versioning, snapshots, replication, or immutable storage depending on the requirement.

Can one storage account have multiple lifecycle rules?

Yes. A storage management policy can contain multiple rules. In Terraform, you can write multiple rule blocks manually or use a dynamic rule block with for_each.

Should I use dynamic blocks?

Use dynamic blocks when the rules are genuinely repeated across environments or storage accounts. If you only have one or two rules, static Terraform is often easier to read and safer for the next person maintaining it.

What blob type should I use?

Most common Azure Blob Storage lifecycle examples use blockBlob. Microsoft’s lifecycle management overview says lifecycle policies are supported for block blobs and append blobs in supported storage account types. Check your workload before assuming the blob type.

How do I target a specific container?

Use prefix_match and start the prefix with the container name. For example, prefix_match = ["documents"] targets the documents container, while prefix_match = ["documents/active/"] targets a path inside that container.

Useful links

Final view: use azurerm_storage_management_policy to make Azure Blob lifecycle management repeatable and consistent. Keep the first version simple, test with safe sample data, and only introduce dynamic Terraform patterns when they make the configuration easier to maintain.