Skip to content
Back to Blog
2 min read

Managing Data with Azure Blob Storage Lifecycle Policies

Storage bills creep. They never spike, they never alert, they just slowly become a line item somebody at finance asks about, and by then you’ve got several terabytes of three-year-old log files at Hot tier rates. Lifecycle policies are the answer, and they cost nothing to set up — but I keep meeting accounts that don’t have any. Walking through how I configure them, and the gotchas that have caught me out.

Storage Tiers Overview

Azure Blob Storage offers three access tiers:

  • Hot - Frequently accessed data, highest storage cost, lowest access cost
  • Cool - Infrequently accessed data (30+ days), lower storage cost, higher access cost
  • Archive - Rarely accessed data (180+ days), lowest storage cost, highest access cost

Creating a Lifecycle Policy

Using Azure CLI:

# Create a storage account with blob versioning
az storage account create \
    --name mystorageaccount2020 \
    --resource-group rg-storage \
    --location australiaeast \
    --sku Standard_LRS \
    --kind StorageV2 \
    --access-tier Hot

# Enable blob versioning
az storage account blob-service-properties update \
    --account-name mystorageaccount2020 \
    --enable-versioning true

Policy Definition

Create a policy file lifecycle-policy.json:

{
  "rules": [
    {
      "enabled": true,
      "name": "move-to-cool",
      "type": "Lifecycle",
      "definition": {
        "actions": {
          "baseBlob": {
            "tierToCool": {
              "daysAfterModificationGreaterThan": 30
            }
          }
        },
        "filters": {
          "blobTypes": ["blockBlob"],
          "prefixMatch": ["logs/", "data/"]
        }
      }
    },
    {
      "enabled": true,
      "name": "archive-old-data",
      "type": "Lifecycle",
      "definition": {
        "actions": {
          "baseBlob": {
            "tierToArchive": {
              "daysAfterModificationGreaterThan": 90
            }
          }
        },
        "filters": {
          "blobTypes": ["blockBlob"],
          "prefixMatch": ["archive/"]
        }
      }
    },
    {
      "enabled": true,
      "name": "delete-old-logs",
      "type": "Lifecycle",
      "definition": {
        "actions": {
          "baseBlob": {
            "delete": {
              "daysAfterModificationGreaterThan": 365
            }
          }
        },
        "filters": {
          "blobTypes": ["blockBlob"],
          "prefixMatch": ["logs/"]
        }
      }
    }
  ]
}

Apply the policy:

az storage account management-policy create \
    --account-name mystorageaccount2020 \
    --policy @lifecycle-policy.json

Managing Versions and Snapshots

{
  "rules": [
    {
      "enabled": true,
      "name": "version-management",
      "type": "Lifecycle",
      "definition": {
        "actions": {
          "version": {
            "tierToCool": {
              "daysAfterCreationGreaterThan": 30
            },
            "tierToArchive": {
              "daysAfterCreationGreaterThan": 90
            },
            "delete": {
              "daysAfterCreationGreaterThan": 365
            }
          },
          "snapshot": {
            "tierToCool": {
              "daysAfterCreationGreaterThan": 30
            },
            "delete": {
              "daysAfterCreationGreaterThan": 180
            }
          }
        },
        "filters": {
          "blobTypes": ["blockBlob"]
        }
      }
    }
  ]
}

Using .NET SDK

using Azure.Storage.Management;
using Azure.Storage.Management.Models;

public class LifecycleManager
{
    public async Task CreatePolicyAsync(string resourceGroup, string accountName)
    {
        var credential = new DefaultAzureCredential();
        var client = new StorageManagementClient(subscriptionId, credential);

        var policy = new ManagementPolicy
        {
            Rules = new List<ManagementPolicyRule>
            {
                new ManagementPolicyRule
                {
                    Name = "move-to-cool",
                    Enabled = true,
                    Type = "Lifecycle",
                    Definition = new ManagementPolicyDefinition
                    {
                        Actions = new ManagementPolicyAction
                        {
                            BaseBlob = new ManagementPolicyBaseBlob
                            {
                                TierToCool = new DateAfterModification
                                {
                                    DaysAfterModificationGreaterThan = 30
                                }
                            }
                        },
                        Filters = new ManagementPolicyFilter
                        {
                            BlobTypes = new List<string> { "blockBlob" },
                            PrefixMatch = new List<string> { "data/" }
                        }
                    }
                }
            }
        };

        await client.ManagementPolicies.CreateOrUpdateAsync(
            resourceGroup,
            accountName,
            policy);
    }
}

Monitoring Policy Execution

Lifecycle policies run once per day. Monitor execution through:

# Check last execution
az storage account management-policy show \
    --account-name mystorageaccount2020 \
    --query 'policy.rules[].definition.actions'

# View metrics in Azure Monitor
az monitor metrics list \
    --resource /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Storage/storageAccounts/{account} \
    --metric "BlobCount" \
    --interval PT1H

Cost Estimation

Before implementing policies, estimate savings:

TierStorage (per GB/month)Operations
Hot~$0.0184Low cost
Cool~$0.01Higher cost
Archive~$0.00099Highest cost

Things I’ve been burned by

  • Archive tier rehydration is slow and not free. Standard rehydration is up to 15 hours; high-priority is faster but costs more. If “rare access” turns out to mean “a couple of times a quarter from a Power BI report nobody told me about,” Archive will hurt.
  • “Days after modification” is exact, not approximate. I once tiered files to Archive at 30 days only to discover a downstream process was reading them weekly. Every read pulled the file back to Hot — which meant the tier transition counted as “modification” and reset the clock. Net effect: paying tiering costs in both directions, every week.
  • Policies run once per day, on Microsoft’s schedule. Don’t expect immediate transitions when you create a policy. It can take 24-48 hours for the first run.
  • Test with a non-critical container first. Once Archive happens, undoing it is a rehydrate, which costs money and time.

For most clients, just moving anything older than 90 days into Cool produces visible savings within a billing cycle. Start there. Get fancier when you actually understand your access patterns.\n\n## Takeaways\n\nAdd a concise, personal takeaway and recommended next steps here.\n

Michael John Peña

Michael John Peña

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.