Terraform is used to define Azure Storage Accounts consistently with security and networking settings from day one. Almost every Azure project needs at least one storage account, and Terraform makes it easy to define them consistently with the right security and networking settings from day one. The practice covers creating storage accounts in Terraform with all the production-ready configuration options including replication, networking rules, lifecycle management, and encryption. The configuration is typically split between provider setup, resource group creation, and the storage account resource itself. The same patterns extend to remote state storage, module-based provisioning, and storage task definition.
The operational impact of a storage account definition is broad. A storage account becomes the durable landing zone for blobs, files, queues, tables, and more. The choice of replication, tier, and network rules directly affects cost, availability, and attack surface. State management with Azure Storage as a backend changes collaboration safety because Terraform state is stored in plain text and may contain secrets. If the state is incorrectly secured, unauthorized access to systems and data loss can result. In a production deployment, public network access is allowed by default in basic examples, which creates exposure. The recommendation is to restrict access to this storage account using a storage firewall, service endpoint, or private endpoint.
Provider Configuration and Version Constraints
The Terraform configuration starts with a versions.tf block that declares requiredversion and requiredproviders. The reference configuration uses:
terraform {
required_version = ">= 1.5.0"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 4.0"
}
}
}
The provider block sets features {} and subscriptionid = var.subscriptionid. An alternative example uses:
terraform {
required_providers {
azurerm={
source = "hashicorp/azurerm"
version = "~>4.8.0"
}
}
required_version = ">=1.9.0"
}
provider "azurerm" {
features {
}
subscription_id = var.subscription_id
tenant_id = var.tenant_id
}
The direct fact is that the azurerm provider source is hashicorp/azurerm and the version constraint uses ~> 4.0 or ~>4.8.0. The impact for the user is reproducible builds across teams because version pinning prevents breaking changes from silently entering the pipeline. The contextual layer ties this to the resource definitions that follow, because without a locked provider, attribute names for azurermstorageaccount can shift between major releases.
In the step-by-step guide, the terraform block is described as declaring the provider plugin to use and ensuring Terraform version 1.9.0 or above. The ~>4.8.0 notation means use the latest compatible version like 4.8.x. This is set up as the tools you need before building something.
Resource Group Foundation
A resource group is created before the storage account. The example uses:
resource "azurerm_resource_group" "storage" {
name = "rg-storage-production"
location = "East US"
}
Another example names the resource azurermresourcegroup.example with name var.resourcegroupname and location var.location.
The direct fact is that the storage account resource references the resource group via resourcegroupname = azurermresourcegroup.storage.name and location = azurermresourcegroup.storage.location. The impact is that the storage account is scoped to a named resource group and region, which determines data residency and network latency. The contextual layer links this to state backend creation commands where a resource group named tfstate is created with az group create --name $RESOURCEGROUPNAME --location eastus before the storage account is provisioned.
Basic Storage Account Definition
A straightforward storage account configuration starts with general purpose v2.
resource "azurerm_storage_account" "main" {
name = var.storage_account_name
resource_group_name = azurerm_resource_group.storage.name
location = azurerm_resource_group.storage.location
account_tier = "Standard"
account_replication_type = "GRS"
account_kind = "StorageV2"
https_traffic_only_enabled = true
min_tls_version = "TLS1_2"
infrastructure_encryption_enabled = true
access_tier = "Hot"
}
A second example uses:
resource "azurerm_storage_account" "example" {
name = var.storage_account_name
resource_group_name = azurerm_resource_group.example.name
location = azurerm_resource_group.example.location
account_tier = "Standard"
account_replication_type = "LRS"
}
The direct fact is accounttier is Standard. accountreplication_type can be GRS for Geo-redundant storage or LRS for Locally Redundant Storage. LRS stands for Locally Redundant Storage — your data is copied three times within one region. The impact of GRS versus LRS is cost versus durability across regions. Standard is a cost-efficient tier. Storage accounts are used to store blobs, files, queues, tables, and more.
The name must be globally unique. Azure storage accounts require a globally unique name. This constraint affects naming strategies and variable defaults.
Production-Ready Configuration Options
Production-ready configuration includes replication, networking rules, lifecycle management, and encryption. The reference configuration shows:
- httpstrafficonly_enabled = true to require HTTPS for all connections
- mintlsversion = "TLS1_2" to set minimum TLS version
- infrastructureencryptionenabled = true for double encryption
- access_tier = "Hot" for blob storage access tier
- Allow or deny public blob access at the account
The direct fact is infrastructure encryption enabled provides double encryption. The impact is defense in depth for sensitive data. The contextual layer connects this to the module support for customer-managed keys for encrypting the data in the storage account, which adds key management control beyond Microsoft-managed encryption.
Networking rules are mentioned as part of production-ready options. The module supports creation of a storage account with various configuration options such as account kind, tier, replication type, network rules, and identity settings. In a production deployment, it's recommended to restrict access to this storage account using a storage firewall, service endpoint, or private endpoint. Public network access is allowed to this Azure storage account in the example.
The module also supports enable private endpoint for the storage account, providing secure access over a private network. The storage account name must be globally unique. The module creates resources in the same region as the storage account.
Module-Based Provisioning with AVM
The Terraform module is designed to create Azure Storage Accounts and its related resources, including blob containers, queues, tables, and file shares. It also supports the creation of a storage account private endpoint which provides secure and direct connectivity to Azure Storage over a private network.
Key capabilities include:
- Create a storage account with various configuration options such as account kind, tier, replication type, network rules, and identity settings
- Create blob containers, queues, tables, and file shares within the storage account
- Support for customer-managed keys for encrypting the data in the storage account
- Enable private endpoint for the storage account, providing secure access over a private network
- The storage account name must be globally unique
- The module creates resources in the same region as the storage account
IMPORTANT This module manages the Storage Account itself, plus its child containers, queues, tables, file shares, private endpoints and role assignments, through the AzAPI provider, which always authenticates with Microsoft Entra ID and never requires a Storage shared key.
A warning notes major version Zero is for initial development. Anything MAY change at any time. A module SHOULD NOT be considered stable till at least it is major version one or greater. Changes will always be via new versions being published and no changes will be made to existing published versions.
The impact for users is that module-based provisioning centralizes best practices for networking and identity, while the version zero warning signals breaking change risk in pipelines.
Variables and Project Structure
The step-by-step guide uses two Terraform files:
- main.tf contains the Terraform configuration
- variables.tf holds variable declarations and default values
variables.tf example:
variable "tenant_id"{
default = "<ur-tenant-id>"
}
variable "subscription_id"{
default = "<ur-subscription-id>"
}
variable "resource_group_name" {
default = "<ur-resource-group-name>"
}
variable "location" {
default = "<ur-location>"
}
variable "storage_account_name"{
default = "<ur-storage-account-name>"
}
Use variables to keep your script clean and reusable for multiple environments such as Dev, UAT, Prod.
The direct fact is variables are declared with defaults. The impact is environment promotion without editing core logic. The contextual layer ties this to provider authentication where subscriptionid and tenantid can be supplied via variables or via CLI login.
Authentication Patterns
Authentication tips from the guide:
- If you don’t provide subscriptionid and tenantid in the provider block, you can log in via CLI using:
az login
- To use a Service Principal preferred for automation, run:
az ad sp create-for-rbac -n <name-of-service-principal-u-want-o-give> --role="Contributor" --scopes="/subscriptions/<ur-subscription-id>
The provider configuration in the reference uses subscriptionid = var.subscriptionid. In the example with tenantid, the provider sets tenantid = var.tenant_id.
The module authenticates through AzAPI provider which always authenticates with Microsoft Entra ID and never requires a Storage shared key.
In the state backend example, Terraform authenticates to the Azure storage account using an Access Key. In a production deployment, it's recommended to evaluate the available authentication options supported by the azurerm backend and to use the most secure option for your use case.
Terraform State Backend with Azure Storage
State allows Terraform to know what Azure resources to add, update, or delete. By default, Terraform state is stored locally, which isn't ideal for the following reasons:
- Local state doesn't work well in a team or collaborative environment
- Terraform state can include sensitive information
- Storing state locally increases the chance of inadvertent deletion
The article teaches how to create an Azure storage account, use Azure storage to store remote Terraform state, understand state locking, understand encryption at rest.
Before you use Azure Storage as a backend, you must create a storage account. Run the following commands or configuration to create an Azure storage account and container:
```
!/bin/bash
RESOURCEGROUPNAME=tfstate
STORAGEACCOUNTNAME=tfstate$RANDOM
CONTAINER_NAME=tfstate
Create resource group
az group create --name $RESOURCEGROUPNAME --location eastus
Create storage account
az storage account create --resource-group $RESOURCEGROUPNAME --name $STORAGEACCOUNTNAME --sku Standard_LRS --encryption-services blob
Create blob container
az storage container create --name $CONTAINERNAME --account-name $STORAGEACCOUNT_NAME
```
Key points:
- Azure storage accounts require a globally unique name
To configure the backend state, you need the following Azure storage information:
- storageaccountname: The name of the Azure Storage account
- container_name: The name of the blob container
- key: The name of the state store file to be created
- access_key: The storage access key
Each of these values can be specified in the Terraform configuration file or on the command line. We recommend that you use an environment variable for the access_key value.
State is stored in plain text and may contain secrets. If the state is incorrectly secured, unauthorized access to systems and data loss can result.
Execution Workflow
Steps to execute the Terraform script:
```
terraform init # Initialize the Terraform project
terraform validate # Validate the configuration files
terraform plan # See what Terraform intends to do
terraform apply # Apply the changes (you’ll be prompted to confirm)
or use this for non-interactive execution:
terraform apply -auto-approve
```
The direct fact is init, validate, plan, apply sequence. The impact is safe change preview before production mutation. The contextual layer links plan output to the resource definitions for storage account tier, replication, and network settings.
Storage Task Integration
A storage task can perform operations on blobs in an Azure Storage account. As you create a task, you can define the conditions that must be met by each object, and the operations to perform on the object. You can also identify one or more Azure Storage account targets.
The how-to article teaches how to create a storage task using Terraform. Terraform enables the definition, preview, and deployment of cloud infrastructure. Using Terraform, you create configuration files using HCL syntax. The HCL syntax allows you to specify the cloud provider such as Azure and the elements that make up your cloud infrastructure. After you create your configuration files, you create an execution plan that allows you to preview your infrastructure changes before they're deployed.
The direct fact is storage tasks operate on blobs with defined conditions and operations. The impact is automated lifecycle actions on storage data without separate orchestration. The contextual layer connects this to the storage account provisioning patterns, because a storage task targets an existing storage account created via Terraform.
Conclusion
Terraform Azure Storage Account provisioning combines provider versioning, resource group scoping, account kind and replication choices, and security hardening. The basic resource definition sets accounttier to Standard and replication to GRS or LRS, with accountkind StorageV2. Production hardening adds httpstrafficonlyenabled, mintlsversion TLS12, infrastructureencryptionenabled, and access_tier Hot, plus network controls and private endpoints. Module-based approaches via AVM add child resources, customer-managed keys, and Entra ID authentication without storage keys. State backend usage moves Terraform state to a globally unique storage account with a blob container, but requires careful access key handling and network restriction. Variable-driven project structure supports multi-environment reuse, and authentication can be via CLI login or service principal. Storage task definition via Terraform extends the storage account lifecycle with conditional blob operations. Each decision propagates to cost, durability, security, and team collaboration outcomes.