Skip to main content

Azure Blob Storage Support for !terraform.state Function

· 6 min read
PePe Amengual
Principal @ Slalom Build

Atmos now supports Azure Blob Storage backends in the !terraform.state YAML function. Read Terraform outputs directly from Azure-backed state files without initializing Terraform—bringing the same blazing-fast performance to Azure that S3 users already enjoy.

What's New

The !terraform.state YAML function now supports Azure Blob Storage (azurerm) backends, joining existing support for S3 and local backends. This means you can retrieve Terraform outputs from Azure-backed state files at lightning speed—without the overhead of Terraform initialization.

Why This Matters

Before this feature, if you were using Azure Blob Storage as your Terraform backend, you had two options for reading remote state:

  1. !terraform.output - Slow but reliable. Requires full Terraform initialization, provider downloads, and varfile generation.
  2. !store - Fast but requires extra setup. You had to manually configure external secret stores.

Now you can use !terraform.state with Azure backends—getting 10-100x faster performance compared to !terraform.output by reading directly from blob storage.

How It Works

Backend Configuration

Configure your Terraform component with an azurerm backend:

components:
terraform:
vpc:
backend_type: azurerm
backend:
azurerm:
storage_account_name: "mystorageaccount"
container_name: "tfstate"
key: "vpc.terraform.tfstate"

Reading State

Use the !terraform.state function to read outputs:

components:
terraform:
eks-cluster:
vars:
# Get vpc_id output from vpc component in current stack
vpc_id: !terraform.state vpc vpc_id

# Get private subnet IDs
subnet_ids: !terraform.state vpc private_subnet_ids

# Get first subnet using YQ expression
subnet_id: !terraform.state vpc .private_subnet_ids[0]

Cross-Stack References

Reference components from different stacks:

components:
terraform:
tgw:
vars:
# Get VPC ID from production stack
vpc_id: !terraform.state vpc plat-ue2-prod vpc_id

# Use template for dynamic stack names
vpc_id: !terraform.state vpc {{ printf "net-%s-%s" .vars.environment .vars.stage }} vpc_id

Authentication

The Azure Blob Storage integration uses Azure DefaultAzureCredential, which supports multiple authentication methods automatically:

  1. Environment variables - AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET
  2. Managed Identity - When running in Azure (AKS, VMs, Functions)
  3. Azure CLI credentials - az login
  4. Visual Studio Code credentials - Authenticated VS Code sessions

No additional configuration needed—just authenticate using your preferred method.

Workspace Handling

Azure Blob Storage uses a specific naming convention for workspaces:

  • Default workspace: Uses the key as-is (e.g., terraform.tfstate)
  • Non-default workspaces: Appends workspace as suffix (e.g., terraform.tfstateenv:dev)

Atmos handles this automatically—you don't need to worry about the naming convention.

Example

If you have:

  • Key: apimanagement.terraform.tfstate
  • Workspace: dev-wus3-apimanagement-be

Atmos will look for: apimanagement.terraform.tfstateenv:dev-wus3-apimanagement-be

Performance Benefits

Before: Using !terraform.output

$ time atmos terraform plan eks-cluster -s plat-ue2-dev

# Must initialize Terraform for each dependency
Initializing vpc component...
Downloading providers...
Generating backend config...
Generating varfiles...
Reading outputs...

real 2m34.521s

After: Using !terraform.state

$ time atmos terraform plan eks-cluster -s plat-ue2-dev

# Direct blob storage access
Reading state from Azure Blob Storage...

real 0m3.142s

~50x faster in this example—and the speedup grows with infrastructure complexity.

Advanced Features

YQ Expressions

Use YQ expressions for complex data extraction:

vars:
# Get nested map values
db_endpoint: !terraform.state database .config_map.endpoint

# String concatenation
jdbc_url: !terraform.state postgres ".master_hostname | \"jdbc:postgresql://\" + . + \":5432/events\""

# Default values for unprovisioned components
username: !terraform.state config ".username // \"default-user\""

Caching

Results are cached in memory per CLI execution:

vars:
# All three calls use the same cached result
sg_id_1: !terraform.state vpc security_group_id
sg_id_2: !terraform.state vpc security_group_id
sg_id_3: !terraform.state vpc {{ .stack }} security_group_id

The first call reads from Azure; subsequent calls return cached data instantly.

Error Handling

  • Blob not found (404): Returns null (component not provisioned yet)
  • Permission denied (403): Returns clear error message
  • Network errors: Automatically retries up to 2 times with exponential backoff

Technical Details

Implementation Highlights

  • Azure SDK for Go - Uses official github.com/Azure/azure-sdk-for-go/sdk/storage/azblob package
  • Client caching - Azure Blob clients are cached per storage account/container
  • Retry logic - Automatic retry with exponential backoff for transient failures
  • Nil safety - Robust error handling prevents panics
  • Test coverage - Comprehensive unit tests with mocked Azure SDK
  • Cross-platform - Works on Linux, macOS, and Windows

Backend Configuration Options

All standard Azure backend options are supported:

backend:
azurerm:
storage_account_name: "mystorageaccount" # Required
container_name: "tfstate" # Required
key: "terraform.tfstate" # Optional (default: terraform.tfstate)
# Authentication happens via DefaultAzureCredential

Migration Guide

From !terraform.output to !terraform.state

The syntax is identical—just replace !terraform.output with !terraform.state:

# Before
vpc_id: !terraform.output vpc vpc_id

# After
vpc_id: !terraform.state vpc vpc_id

From !store to !terraform.state

Simplify your configuration by removing store setup:

# Before: Required store configuration
vpc_id: !store azurekeyvault plat-ue2-dev vpc vpc_id

# After: Direct state access
vpc_id: !terraform.state vpc vpc_id

Examples

Basic Usage

components:
terraform:
app:
vars:
# String output
security_group_id: !terraform.state security-group id

# List output
subnet_ids: !terraform.state vpc private_subnet_ids

# Map output
config: !terraform.state config config_map

Cross-Region References

components:
terraform:
replication:
vars:
# Reference component from different region
primary_db: !terraform.state database {{ printf "%s-use1-%s" .vars.tenant .vars.stage }} endpoint

Disaster Recovery Scenarios

components:
terraform:
failover:
vars:
# Primary region
primary_vpc: !terraform.state vpc plat-ue2-prod vpc_id

# DR region with default fallback
dr_vpc: !terraform.state vpc plat-uw2-prod ".vpc_id // \"vpc-mock-dr\""

Considerations

  • Secrets exposure: Using !terraform.state with secrets will expose them in atmos describe output
  • Permission scoping: Ensure your Azure credentials have access to all referenced storage accounts
  • Cross-region access: Consider latency when reading state across regions
  • Cold starts: Components not yet provisioned return null (use YQ default values to handle this)

Try It Now

Upgrade to the latest Atmos release and start using Azure Blob Storage backends:

# Check your version
atmos version

# Describe a component using Azure backend
atmos describe component vpc -s plat-ue2-dev

# Use !terraform.state in your stack configs
# (See examples above)

Documentation

Get Involved

We're building Atmos in the open and welcome your feedback:


Next up: Google Cloud Storage (GCS) backend support for !terraform.state. Stay tuned!