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. This approach shifts storage account creation from manual portal clicks into declarative configuration that can be versioned, reviewed, and reproduced across development, staging, and production environments. The reference material covers creating storage accounts in Terraform with all the production-ready configuration options including replication, networking rules, lifecycle management, and encryption. The discussion also extends to remote state management using Azure Storage itself, module-based provisioning through the Azure Verified Module, and the operational trade-offs of using Terraform for storage account lifecycles.
The core workflow begins with provider definition, resource group placement, and a baseline storage account resource. From that baseline the configuration expands into hardened security settings such as HTTPS enforcement, minimum TLS version, infrastructure encryption, customer-managed keys via Azure Key Vault, private endpoints, and network rules. State management considerations are intertwined with the storage account itself because the storage account can serve as the remote backend for Terraform state, creating a dependency loop that requires careful bootstrapping.
Provider and Version Constraints
Terraform provider configuration establishes the contract between the Terraform CLI and the Azure Resource Manager API.
hcl
terraform {
required_version = ">= 1.5.0"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = "~> 4.0"
}
}
}
The requiredversion constraint of >= 1.5.0 ensures compatibility with features introduced in recent Terraform releases. The azurerm provider is pinned to ~> 4.0 which allows patch updates within the major version while preventing breaking changes. The provider block itself is defined with an empty features block and an explicit subscriptionid variable.
hcl
provider "azurerm" {
features {}
subscription_id = var.subscription_id
}
The impact of pinning versions is stability across teams. When multiple engineers run terraform plan and terraform apply, identical provider versions reduce drift and unexpected behavior. The empty features block is a deliberate opt-in for default behavior; future feature toggles can be added there without modifying resource definitions.
Contextually, this provider block sits at the top of versions.tf and is evaluated before any resource graph is built. It is a prerequisite for the resource group and storage account resources that follow.
Resource Group Foundations
A storage account must live inside an Azure resource group. The example defines a dedicated group for production storage.
hcl
resource "azurerm_resource_group" "storage" {
name = "rg-storage-production"
location = "East US"
}
The name rg-storage-production signals intent and environment separation. East US is used as the location for both the resource group and the storage account that references it.
The real-world consequence of resource group placement is lifecycle management. Deleting or moving the resource group cascades to all contained resources. Using a named group per workload simplifies RBAC scoping and cost allocation tagging.
The module documentation notes that the module creates resources in the same region as the storage account. This keeps latency low and avoids cross-region data transfer costs for child resources such as blob containers, queues, tables, and file shares.
Basic Storage Account Configuration
The baseline storage account uses a general purpose v2 account kind.
hcl
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"
}
The name is supplied via variable because the storage account name must be globally unique across Azure. The reference material emphasizes this uniqueness requirement multiple times. The account tier Standard is paired with replication type GRS for Geo-redundant storage, which provides copies in a secondary region for disaster recovery. Account kind StorageV2 enables the full set of Blob, Queue, Table, and File services.
Impact for the user is cost versus durability. Standard tier with GRS increases cost compared to LRS but reduces risk of data loss from regional outages. The choice of StorageV2 over BlobStorage or FileStorage unlocks future service upgrades without renaming the account.
Production-Ready Security and Networking Settings
Beyond basic creation, production hardening is applied directly in the storage account resource.
hcl
https_traffic_only_enabled = true
min_tls_version = "TLS1_2"
infrastructure_encryption_enabled = true
access_tier = "Hot"
HTTPS traffic only enabled forces all client connections to use TLS and rejects plaintext HTTP. The minimum TLS version set to TLS1_2 blocks older protocols that have known vulnerabilities. Infrastructure encryption enabled adds double encryption where Microsoft-managed keys encrypt customer-managed keys, providing defense in depth. Access tier Hot is selected for workloads with frequent access patterns.
The reference material also notes the option to allow or deny public blob access at the account level. This is a critical security toggle that controls anonymous read access to blobs.
These settings translate into compliance posture. HTTPS only and TLS 1.2 satisfy regulatory requirements for data in transit. Infrastructure encryption addresses data at rest protection requirements beyond the default Microsoft-managed key.
The module supports creation of a storage account with various configuration options such as account kind, tier, replication type, network rules, and identity settings. Network rules can be used to restrict access to specific IP ranges or virtual networks. Identity settings enable system-assigned or user-assigned managed identities for the storage account, which is required for customer-managed key scenarios.
Customer-Managed Keys and Key Vault Integration
For organizations requiring control over encryption keys, Terraform can provision Azure Key Vault and link it to the storage account.
The client access policy is defined to allow the deploying principal to manage keys.
hcl
resource "azurerm_key_vault_access_policy" "client" {
key_vault_id = azurerm_key_vault.storage.id
tenant_id = data.azurerm_client_config.current.tenant_id
object_id = data.azurerm_client_config.current.object_id
key_permissions = [
"Create",
"Delete",
"Decrypt",
"Encrypt",
"Get",
"GetRotationPolicy",
"List",
"Purge",
"Recover",
"Restore",
"SetRotationPolicy",
"Sign",
"Update",
"UnwrapKey",
"Verify",
"WrapKey"
]
}
The storage account identity is granted limited key permissions.
hcl
resource "azurerm_key_vault_access_policy" "storage" {
key_vault_id = azurerm_key_vault.storage.id
tenant_id = data.azurerm_client_config.current.tenant_id
object_id = azurerm_storage_account.main.identity[0].principal_id
key_permissions = [
"Get",
"WrapKey",
"UnwrapKey"
]
}
The encryption key is created as an RSA key.
hcl
resource "azurerm_key_vault_key" "storage" {
name = "storage-encryption-key"
key_vault_id = azurerm_key_vault.storage.id
key_type = "RSA"
key_size = 2048
key_opts = ["decrypt", "encrypt", "wrapKey", "unwrapKey"]
depends_on = [
azurerm_key_vault_access_policy.client
]
}
Current client configuration is referenced via data source.
hcl
data "azurerm_client_config" "current" {}
The customer-managed key is applied to the storage account.
hcl
resource "azurerm_storage_account_customer_managed_key" "main" {
storage_account_id = azurerm_storage_account.main.id
key_vault_key_id = azurerm_key_vault_key.storage.id
depends_on = [
azurerm_key_vault_access_policy.storage
]
}
The impact of this chain is key governance. The organization retains root control over encryption material and can rotate keys independently of Microsoft. The operational consequence is additional dependencies: Key Vault access policies must be created before the key, and the storage account identity must exist before granting it permissions.
The module documentation confirms support for customer-managed keys for encrypting data in the storage account. The 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.
Module-Based Provisioning with Azure Verified Modules
The Terraform module described 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.
A warning is issued for major version zero. Major version Zero 0.y.z 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 1.0.0 or greater. Changes will always be via new versions being published and no changes will be made to existing published versions.
The module creates resources in the same region as the storage account. The storage account name must be globally unique.
For private connectivity, enabling a private endpoint removes the storage account from public internet exposure. Access is then limited to the virtual network and private DNS zone. This aligns with the recommendation to restrict access to this storage account using a storage firewall, service endpoint, or private endpoint in a production deployment.
Remote State Backend with Azure Storage
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. By default, Terraform state is stored locally, which isn't ideal for team collaboration, sensitive information exposure, and risk of inadvertent deletion.
The article on state storage recommends creating an Azure storage account and container to host remote state.
Key points for backend setup:
- Azure storage accounts require a globally unique name
- Storage account name, container name, key, and access key are required for backend configuration
Example bootstrap commands:
bash
RESOURCE_GROUP_NAME=tfstate
STORAGE_ACCOUNT_NAME=tfstate$RANDOM
CONTAINER_NAME=tfstate
bash
az group create --name $RESOURCE_GROUP_NAME --location eastus
bash
az storage account create --resource-group $RESOURCE_GROUP_NAME --name $STORAGE_ACCOUNT_NAME --sku Standard_LRS --encryption-services blob
bash
az storage container create --name $CONTAINER_NAME --account-name $STORAGE_ACCOUNT_NAME
The backend configuration requires:
- 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.
In this 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.
State allows Terraform to know what Azure resources to add, update, or delete. Remote state with locking prevents concurrent apply conflicts. Encryption at rest is provided by the storage account's encryption services.
Advantages and Disadvantages of Terraform for Storage Accounts
Advantages of Create Azure Storage Account using Terraform include:
- Infrastructure as Code: Your Azure Storage Account can now be represented in the Terraform configuration files that can be handled by using version control, shared, are replicated across many different environments. This aids organization in maintaining best practices, consistency, reproducibility and collaboration within their group.
- Automation and Repeatability: When you Terraform and configure automation, you can easily provision and manage Azure Storage Accounts, without any need of human interventions and consequently mistakes
The cloud-based storage management gives you control of your storage accounts in any part of the consequently enabling you to manage your entire infrastructure using one common programming frontend.
Disadvantages of Create Azure Storage Account using Terraform include:
- Learning Curve: Terrraform uses its own domain-specific language HashiCorp Configuration Language or HCL and workflow and thus it can be challenging to master them for the first time provided that you may not be familiar with IaC tools. It is probable that grasp of concepts, syntax and best practices of Terraform may take some time and a lot of work.
- State File Management: terraform employs a state file in order to maintain the status of your infrastructure resources. Proper management and storing of state files should be paid enough attention, because any inconsistency or loss of the state file will cause trouble in the management or updating of resources. A well-structured state file management system is an absolute necessity, particularly for groups or multi-member work.
- Vendor Lock-in: While Terraform has the flexibility to support cloud services offered by different providers, its configuration files and modules can be classified as provider/version specific
The learning curve impacts onboarding time for teams new to IaC. State file management impacts operational reliability; a corrupted or lost state file can cause Terraform to attempt to recreate resources or lose track of existing ones. Vendor lock-in means migration to another IaC tool requires rewriting HCL.
Verification and Lifecycle Operations
After apply, verification can be performed in the Azure portal or via Terraform commands.
The example workflow notes:
Step 7: Verify the Resources
Once the apply operation completes successfully, you can verify the created resources in the Azure portal or by running the following command:
bash
terraform show
This command will display the current state of the resources managed by Terraform.
Deletion of unwanted resources to avoid extra cost from Azure is performed with:
bash
terraform destroy
The command type yes and press Enter to confirm and create the Azure Storage Account and Resource Group during apply.
The reference material also points to troubleshooting storage account names with Resolve errors for storage account names.
Configuration Summary Table
| Setting | Value in Reference | Purpose |
|---|---|---|
| terraform required_version | >= 1.5.0 | Minimum Terraform version |
| azurerm provider version | ~> 4.0 | Provider compatibility |
| resource group name | rg-storage-production | Naming convention |
| resource group location | East US | Region placement |
| account_tier | Standard | Performance tier |
| accountreplicationtype | GRS | Geo-redundant storage |
| account_kind | StorageV2 | General purpose v2 |
| httpstrafficonly_enabled | true | Enforce TLS |
| mintlsversion | TLS1_2 | Protocol minimum |
| infrastructureencryptionenabled | true | Double encryption |
| access_tier | Hot | Blob access tier |
| key_type | RSA | Key Vault key type |
| key_size | 2048 | RSA key size |