Skip to content

Terraform State Storage Architecture & Best Practices

Terraform state is the single source of truth that maps declared configuration code to real-world cloud infrastructure. Because state files contain complete topological metadata, dependency graphs, and unencrypted sensitive attributes, state storage is a critical control plane attack vector. This guide establishes the architectural, security, and operational standards for hosting and managing Terraform state across enterprise platform and workload environments.


The Threat Profile of Terraform State

Terraform state (.tfstate) is not merely passive tracking metadata; it is an active map of an entire cloud estate. State files pose unique security and operational risks:

  1. Cleartext Secret Exposure: By design, Terraform records every resource attribute returned by provider APIs in plaintext JSON within the state file. This includes database connection strings, TLS private certificates, initial administrator passwords (random_password), API keys, and Service Principal client secrets.
  2. Blast Radius Amplification: Compromise of state read permissions permits an adversary to reverse-engineer entire network topologies, internal IP schemes, IAM role boundaries, and secret locations.
  3. Execution Hijacking & Tamper Risk: Unauthorized write access allows an attacker to alter resource references, inject malicious configuration, or delete state pointers. On the subsequent pipeline run, Terraform interprets missing state entries as destroyed resources or orphan-managed infrastructure, precipitating catastrophic outages.
  4. Concurrency & Race Conditions: Concurrent pipeline executions without distributed atomic locking lead to state corruption ("split-brain"), leaving cloud resources orphaned or mutated unpredictably.

Securing Terraform state is therefore an Enterprise Access Model (EAM) Control Plane mandate.


Platform-Level vs. Workload-Level State

An enterprise cloud platform must enforce a strict separation between Platform State (control plane and shared services) and Workload State (application and service deployments). Mixing these scopes destroys isolation boundaries, expands blast radii, and creates operational gridlock.

flowchart TD
    subgraph PLATFORM_LAYER["Platform Layer (Tier 0 & Tier 1 Control Plane)"]
        direction TB
        ROOT["azure-tenant-root<br/>(Lane A — Human CLI)"]
        SPV["azure-service-principals<br/>(Lane A — Human CLI)"]
        MG["azure-management-groups<br/>(Lane B — CI Apply)"]
        SUB["subscription-vending<br/>(Lane B — CI Apply)"]
        HUB["hub-networking<br/>(Lane B — CI Apply)"]

        ROOT_ST[("sa{tenant}tenantroot<br/>Container: tenant-root")]
        SPV_ST[("sa{tenant}tenantroot<br/>Container: service-principals")]
        MG_ST[("sa{tenant}tenantroot<br/>Container: management-groups")]
        SUB_ST[("sa{tenant}tenantroot<br/>Container: subscription-vending")]

        ROOT --> ROOT_ST
        SPV --> SPV_ST
        MG --> MG_ST
        SUB --> SUB_ST
    end

    subgraph WORKLOAD_LAYER["Workload Layer (Tier 2 Applications)"]
        direction TB
        WL_A["Workload Alpha CI/CD<br/>(sp-sub-tf-alpha-rw)"]
        WL_B["Workload Beta CI/CD<br/>(sp-sub-tf-beta-rw)"]

        WL_A_ST[("sttfstate{hex_a}<br/>Container: alpha-prod")]
        WL_B_ST[("sttfstate{hex_b}<br/>Container: beta-prod")]

        WL_A --> WL_A_ST
        WL_B --> WL_B_ST
    end

    PLATFORM_LAYER -.->|Loosely coupled contracts<br/>(DNS, Azure Policy, Managed Identities)| WORKLOAD_LAYER

    classDef platform fill:#1e293b,stroke:#38bdf8,stroke-width:2px,color:#fff;
    classDef workload fill:#1e1e2e,stroke:#a6e3a1,stroke-width:2px,color:#fff;
    classDef storage fill:#0f172a,stroke:#f59e0b,stroke-width:2px,color:#fff;
    class PLATFORM_LAYER platform;
    class WORKLOAD_LAYER workload;
    class ROOT_ST,SPV_ST,MG_ST,SUB_ST,WL_A_ST,WL_B_ST storage;

Architectural Comparison

Dimension Platform-Level State (Tier 0 / Tier 1) Workload-Level State (Tier 2)
Operational Scope Tenant Root, Management Group hierarchy, Central SP vending, Subscription Factory, Hub Networking, Global Policies Application spoke networks, compute, databases, PaaS services, ingress
Blast Radius Tenant-Wide: Corruption or compromise halts entire organization onboarding and breaks global governance Bounded: Corruption or compromise impacts only the specific application subscription
Delivery Model Lane A (Plan in CI, apply via elevated human PIM) for Tier 0; Lane B (Automated apply on merge) for platform infrastructure Lane B (Automated apply on merge via subscription-scoped identity)
Identity Privilege Elevated management group / tenant root rights (Owner, User Access Administrator, Management Group Contributor) Bounded subscription-level rights (Contributor + Lock Contributor scoped to single subscription)
State Storage Location Central platform state storage accounts in the dedicated Platform Management Subscription Dedicated per-subscription state storage accounts hosted in the Platform Management Subscription
Coupling / Ingestion Publishes infrastructure endpoints and configuration contracts via Azure resource properties, Key Vault, or Private DNS Consumes platform contracts via data lookups (azurerm_client_config, DNS resolution), never raw terraform_remote_state

The Anti-Pattern of Cross-State Coupling (terraform_remote_state)

Historically, teams linked platform and workload configurations using data "terraform_remote_state". This pattern is strongly discouraged in enterprise platform engineering:

  1. Permission Leakage: To read a single VNet ID from platform state, the workload pipeline's service principal requires read access to the entire platform state blob, exposing Tier 0/1 secrets and topology.
  2. Schema Brittle Coupling: If the platform team refactors, renames, or moves a resource output in their state, every dependent workload plan breaks immediately.
  3. Locking & Latency Overhead: Workload plans become dependent on external storage endpoints, increasing plan times and introducing cross-boundary failure modes.

Approved Alternative: Workload teams must discover platform dependencies via native cloud query data sources (data "azurerm_virtual_network", data "azurerm_private_dns_zone"), tagged resource lookups, or centrally populated Azure Key Vault secrets.


Storage Topology: Where State Must Exist and Why

State storage account placement is the cornerstone of control-plane security. In an Azure enterprise environment aligned to the Microsoft Cloud Adoption Framework (CAF) and EAM, state storage location must obey two non-negotiable rules:

1. State Storage Must Reside in a Dedicated Platform Subscription

All Terraform state backends—both for platform deployments and workload deployments—must reside within a secure, platform-controlled management subscription (e.g., gt-management-prod-westeu inside mg-platform), isolated in a dedicated resource group (rg-terraform-state).

graph LR
    subgraph MG_PLATFORM["mg-platform / Management Subscription"]
        RG_STATE["rg-terraform-state<br/>(Locked: CanNotDelete)"]
        ROOT_SA["sa<tenant>tenantroot<br/>(Platform Tier 0 State)"]
        W1_SA["sttfstate4b2e9a1c<br/>(Workload Alpha State)"]
        W2_SA["sttfstate8f1d3c5e<br/>(Workload Beta State)"]

        RG_STATE --> ROOT_SA
        RG_STATE --> W1_SA
        RG_STATE --> W2_SA
    end

    subgraph MG_LANDINGZONES["mg-landingzones / Workload Subscriptions"]
        SUB_A["Subscription: Workload Alpha<br/>Pipeline SP: sp-sub-tf-alpha-rw"]
        SUB_B["Subscription: Workload Beta<br/>Pipeline SP: sp-sub-tf-beta-rw"]
    end

    SUB_A -.->|Entra ID RBAC: Container-only<br/>No access to account keys or other SAs| W1_SA
    SUB_B -.->|Entra ID RBAC: Container-only<br/>No access to account keys or other SAs| W2_SA

Why Workload State Must NEVER Live Inside the Workload Subscription

A common architectural error is provisioning the state storage account inside the workload subscription that Terraform is managing. This design violates core security principles:

  • Privilege Inversion & Circular Dependency: The workload deployment pipeline's service principal possesses Contributor on the subscription. If the state account is in that subscription, the workload identity has full administrative power over its own state backend. A malicious or compromised workload pipeline could alter, corrupt, or permanently wipe out its state file.
  • Loss of Independent Forensics: If an application team or rogue credential destroys the subscription or wipes out its resource groups, the state file is destroyed concurrently. Disaster recovery and point-in-time forensic reconstruction become impossible.
  • Lack of Uniform Governance: Platform security engineers cannot uniformly enforce Azure Policies (such as mandatory CMK encryption, private endpoints, or 365-day blob retention) if state storage accounts are scattered across hundreds of decentralized application subscriptions.

2. Dedicated Storage Account per Subscription (sttfstate{8-hex}) vs. Monolithic Multi-Tenant Storage

Within the central rg-terraform-state resource group, how should state be partitioned? Organizations frequently debate between One Multi-Tenant Storage Account vs. Dedicated Storage Accounts Per Subscription.

GRINNTEC enforces Dedicated Storage Accounts per Subscription (sttfstate{8-hex}):

Architectural Consideration Multi-Tenant Single Storage Account (All Workloads in 1 SA) Dedicated Storage Account per Subscription (GRINNTEC Pattern)
Blast Radius & Cross-Talk High. Any misconfigured RBAC or container-level leak risks cross-tenant state visibility. Isolated. Each workload has an isolated storage account and separate DNS namespace.
API Rate Limits & Throttling High risk. Azure Storage enforces per-account ingress/egress and IOPS limits (20,000 IOPS / account). Multiple simultaneous CI runs trigger HTTP 429 throttling and plan timeouts. Eliminated. IOPS and egress limits are distributed per workload.
Network & Firewall Posture Uniform across all teams. Cannot customize firewall rules or Private Endpoints per team security tier without impacting other teams. Modular. Workload-specific network isolation, Private Endpoints, and IP allowlists can be tailored without shared blast radius.
Lifecycle & Decommissioning Risky. Offboarding a workload requires surgical container deletion from a shared account; human error can delete another team's container. Clean. When a subscription is decommissioned, its dedicated storage account is archived and destroyed cleanly without touching neighboring workloads.
Storage Account Naming N/A Deterministic 8-hex hash generated via random_id keyed to the subscription name: stable, globally unique, and reproducible.

Security, Durability, and Availability Controls

State storage must be protected by defense-in-depth controls covering Identity, Network, Encryption, and Resiliency.

flowchart TD
    subgraph CONTROLS["Enterprise State Storage Control Matrix"]
        direction TB

        subgraph SEC["1. Confidentiality & Access"]
            C1["Entra ID RBAC Only<br/>(Storage Blob Data Contributor)"]
            C2["Shared Keys Disabled<br/>(shared_access_key_enabled = false)"]
            C3["Container-Level Scope<br/>(Zero Account-level data rights)"]
        end

        subgraph NET["2. Network Boundaries"]
            N1["Public Access Disabled<br/>(public_network_access_enabled = false)"]
            N2["Private Endpoints<br/>(privatelink.blob.core.windows.net)"]
            N3["TLS 1.2+ & HTTPS Only<br/>(https_traffic_only_enabled = true)"]
        end

        subgraph CRYPT["3. Cryptography"]
            K1["Encryption at Rest<br/>(Platform / CMK with AKV)"]
            K2["Infrastructure Encryption<br/>(Double 256-bit AES)"]
            K3["Terraform 1.4+ / OpenTofu<br/>(Client-Side State Encryption)"]
        end

        subgraph DUR["4. Durability & Resiliency"]
            D1["Blob Versioning<br/>(Enabled for instant rollback)"]
            D2["Soft Delete (Blobs & Containers)<br/>(30-day retention minimum)"]
            D3["Management Locks<br/>(CanNotDelete at RG & SA scope)"]
            D4["Redundancy<br/>(ZRS intra-region / GZRS DR)"]
        end
    end

1. Identity & Access Governance (RBAC)

  • Eliminate Shared Access Keys: shared_access_key_enabled = false must be enforced across all state storage accounts. Shared Keys grant un-audited, root-level administrative access to the entire storage account, bypass Entra ID Conditional Access, and produce anonymous logs.
  • Entra ID Workload Identity Federation (OIDC): CI/CD runners (GitLab CI, GitHub Actions) authenticate to Azure via ephemeral OIDC tokens without static client secrets or certificates.
  • Container-Level Scoping: The workload pipeline's service principal (sp-sub-tf-{name}-rw) is granted Storage Blob Data Contributor strictly at the container level (/blobServices/default/containers/{name}). It is granted no data permissions at the storage account level.
  • Account-Level Metadata Reader: To execute terraform init, Terraform validates storage account metadata via the Azure Resource Manager API. The service principal is granted Reader at the resource group scope (rg-terraform-state) or storage account scope, which permits metadata inspection without exposing underlying blob contents.
# Example: Enforcing Entra ID RBAC and container-scoped access
resource "azurerm_storage_account" "state" {
  name                          = "sttfstate${random_id.storage_suffix.hex}"
  resource_group_name           = azurerm_resource_group.state.name
  location                      = azurerm_resource_group.state.location
  account_tier                  = "Standard"
  account_replication_type       = "ZRS" # Zone-Redundant Storage
  min_tls_version               = "TLS1_2"
  https_traffic_only_enabled    = true
  shared_access_key_enabled     = false # CIS Azure Benchmark 3.5
  public_network_access_enabled = false # CIS Azure Benchmark 3.3

  blob_properties {
    versioning_enabled = true # Rollback protection
    delete_retention_policy {
      days = 30
    }
    container_delete_retention_policy {
      days = 30
    }
  }
}

# Workload identity receives Contributor ONLY at the specific container
resource "azurerm_role_assignment" "workload_sp_state_access" {
  scope                = azurerm_storage_container.workload_state.resource_manager_id
  role_definition_name = "Storage Blob Data Contributor"
  principal_id         = azuread_service_principal.workload_sp.object_id
}

2. Network Isolation

  • Disable Public Network Access: public_network_access_enabled = false. State endpoints must not be exposed to the public internet.
  • Private Endpoints: Connect state storage accounts to dedicated management or shared services virtual networks via Azure Private Endpoints (privatelink.blob.core.windows.net).
  • CI/CD Runner Connectivity: Self-hosted runners, Azure DevOps scale sets, or GitLab private runners deployed inside peered virtual networks access the private endpoint directly over secure, non-routable private IP addresses.
  • Transport Security: https_traffic_only_enabled = true and min_tls_version = "TLS1_2" (or TLS1_3) enforced via Azure Policy at the management group root.

3. Cryptography & Secrets Protection

  • Storage Service Encryption (SSE): All Azure Storage data is encrypted at rest using 256-bit AES encryption.
  • Customer-Managed Keys (CMK): For high-assurance workloads (PCI DSS, HIPAA, government), state storage accounts must be encrypted using keys hosted in Azure Key Vault Premium or Managed HSM, with automated key rotation and Purge Protection enabled.
  • Infrastructure Encryption (Double Encryption): Enables two independent layers of 256-bit AES encryption (one at the service level, one at the infrastructure layer) to mitigate hardware or platform key compromise.
  • Client-Side / State Encryption (Terraform 1.4+ / OpenTofu): Beginning with modern Terraform versions and OpenTofu, client-side encryption can be configured directly in the backend block using PBKDF2/AES-GCM or KMS providers. Even if storage access is compromised, the raw .tfstate blob remains an unreadable ciphertext payload.

4. Durability, Availability & Concurrency

  • Blob Versioning: Every terraform apply overwrites terraform.tfstate. With blob versioning enabled, Azure automatically creates an immutable, timestamped historical version of previous states. In the event of a botched apply or state corruption, rollback to a prior state version requires seconds.
  • Soft Delete: A minimum 30-day soft delete retention policy protects blobs and containers from accidental deletion, malicious purge scripts, or misconfigured decommissioning routines.
  • Management Resource Locks: Apply an Azure Resource Manager CanNotDelete lock on the rg-terraform-state resource group and individual state storage accounts. This prevents human operators or administrative scripts from deleting state infrastructure without explicit two-step manual intervention.
  • High Availability & Disaster Recovery:
  • Intra-Region (Baseline): Zone-Redundant Storage (ZRS) distributes data across three physically separated availability zones within the primary region, surviving entire datacenter failures without downtime.
  • Cross-Region (High Resilience): Geo-Zone-Redundant Storage (GZRS) replicates data asynchronously to a secondary paired region, ensuring continuity against catastrophic regional outages.
  • Distributed State Locking: The AzureRM backend leverages native Azure blob leasing (LeaseState: leased, LeaseDuration: infinite). When an operation initiates (plan with write intent, or apply), Terraform acquires an exclusive lease on terraform.tfstate.env.lease. Any concurrent execution receives an immediate HTTP 412 (Precondition Failed) and exits safely, preventing race conditions.

Operational State Lifecycle (CRUD Operations)

Managing state operationally across its lifecycle requires standardized tooling, automated pipelines, and strict separation between routine CI operations and emergency break-glass procedures.

sequenceDiagram
    autonumber
    actor Dev as Engineer
    participant VCS as GitLab / GitHub
    participant CI as CI Runner (OIDC)
    participant Lock as Azure Blob Lease
    participant State as Remote State Blob
    participant Cloud as Target Azure Resources

    Dev->>VCS: Open Merge Request (code changes)
    VCS->>CI: Trigger Plan Pipeline
    CI->>State: Read terraform.tfstate (Entra ID Auth)
    CI->>Cloud: Query real-world state (API GETs)
    CI-->>VCS: Post Plan diff for peer review

    Dev->>VCS: Merge to main (Peer Approved)
    VCS->>CI: Trigger Apply Pipeline
    CI->>Lock: Acquire Blob Lease (Lock)
    Lock-->>CI: Lease ID Granted
    CI->>Cloud: Apply delta changes (POST/PUT/DELETE)
    CI->>State: Write updated terraform.tfstate (New Version)
    CI->>Lock: Release Blob Lease
    CI-->>VCS: Pipeline Success

1. Create (Bootstrap & Provisioning)

  • The Day-0 Paradox: Because Terraform state is required to manage infrastructure, the first state storage account cannot be created by a remote-state pipeline.
  • Resolution: As implemented in Tenant Root Bootstrap, Day-0 uses a human-gated Step 001 script executed by an elevated engineer using throwaway local state. Once the storage account is live, all future platform deployments immediately bind to it as a remote backend.
  • Workload State Vending: When a new workload subscription is vended via Subscription Vending, the subscription factory module automatically provisions sttfstate{8-hex}, the private container, and the container-scoped RBAC assignments. The workload project receives a pre-configured backend configuration on day zero.

2. Read (Planning & Drift Detection)

  • Workload pipelines run terraform plan on merge requests using scoped machine identities.
  • Read operations validate that the remote state matches desired code declarations.
  • Scheduled nightly pipelines execute terraform plan -detailed-exitcode to detect configuration drift against real-world Azure APIs without mutating state or infrastructure.

3. Update (Apply & Refactoring)

  • Pipeline Exclusivity: Applies must occur exclusively within automated CI/CD runners (Lane B for workloads, Lane A human-gated for Tier 0 control plane). Local human terraform apply is strictly prohibited in production.
  • Refactoring via moved Blocks (Terraform 1.1+): When renaming modules, refactoring variables, or restructuring code, engineers must never execute manual state operations. Modern Terraform provides declarative moved blocks:
    moved {
      from = azurerm_storage_account.legacy_name
      to   = azurerm_storage_account.standard_name
    }
    
    Declarative moved blocks are committed to git, peer-reviewed in the merge request, and evaluated deterministically by CI during the apply phase.
  • Break-Glass Emergency Surgery (state mv, rm, import): When human intervention is unavoidable (e.g., resolving corrupted resources or importing brownfield assets):
  • Mandatory State Backup: The operator must pull an immutable local copy before running any command:
    terraform state pull > "terraform_backup_$(date +%Y%m%d_%H%M%S).json"
    
  • Just-In-Time Elevation: Elevate via Privileged Identity Management (PIM) for transient access.
  • Audit Trail: Execute the operation with interactive terminal logging recorded to secure SIEM/Log Analytics.

4. Delete (Decommissioning & Offboarding)

  • When a workload is decommissioned, its resources are destroyed in reverse dependency order (terraform destroy).
  • Once resources are eliminated, the workload's state storage account is placed on legal/retention hold (e.g., 90–365 days depending on regulatory framework) before the storage account is permanently decommissioned.
  • Soft delete retention ensures accidental subscription teardowns can be fully recovered within the retention window.

Anti-Patterns and Bad Practices

The following patterns are frequently encountered in immature cloud environments. Each is classified below with its technical failure mode and why it violates good platform practice:

1. Committing State to Version Control (Git)

  • The Practice: Checking .tfstate files directly into Git repositories alongside .tf code.
  • Why it fails:
  • Catastrophic Secret Leakage: Git history is permanent and distributed. Anyone with repository read access (developers, CI runners, third-party integrations) gains immediate access to unencrypted database passwords, private keys, and administrative secrets.
  • No Atomic Locking: Git merge resolution cannot resolve concurrent JSON tree modifications. Merging conflicting state branches creates silent JSON corruption, orphaning resources.
  • Violates: NIST SP 800-53 SC-28, CIS Benchmark 3.5, PCI DSS Req 3.4.

2. The Monolithic State File ("The Single State Fallacy")

  • The Practice: Managing an entire enterprise, or all environments (dev, test, prod) of a complex application, within a single root state file.
  • Why it fails:
  • Extreme Blast Radius: A typo, network timeout, or crashed apply can lock or corrupt the entire organization's cloud foundation.
  • Lock Contention: Only one engineer or pipeline can execute plan or apply at any given time. Other teams are queued or blocked.
  • Plan Degradation: Every run requires evaluating thousands of resources against cloud APIs, causing terraform plan execution times to exceed 30–60 minutes.
  • Violates: NIST SP 800-53 AC-5 (Separation of Duties).

3. Local State Files on Engineer Workstations

  • The Practice: Running Terraform from laptops with state stored on local disk (local backend).
  • Why it fails:
  • SPOF & Data Loss: If the engineer's workstation crashes, is lost, or is wiped, the source of truth is permanently destroyed.
  • Split-Brain Infrastructure: Multiple team members running local applies overwrite each other's changes blindly.
  • Zero Auditability: Impossible to reconstruct who changed what or verify compliance against security baselines.
  • Violates: NIST SP 800-53 AU-2, SOC 2 CC7.1.

4. Authentication via Storage Account Shared Keys

  • The Practice: Supplying ARM_ACCESS_KEY or storage_account_key to CI/CD pipelines or developer workstations.
  • Why it fails:
  • Non-Attributable Administrative Access: Shared Access Keys provide root access over all data within the storage account. Activity cannot be traced to an individual or specific pipeline identity in audit logs.
  • Static Secret Sprawl: Keys must be stored as long-lived secrets in CI variables, exposing them to theft.
  • Revocation Hazard: Rotating an account key breaks every pipeline simultaneously.
  • Violates: CIS Azure Benchmark 3.5, PCI DSS Req 8.6.

5. Colocating State Storage inside the Workload Subscription

  • The Practice: Creating the state storage account inside the application subscription being managed.
  • Why it fails:
  • Security Boundary Inversion: The workload deployment identity has Contributor access across the subscription, granting it the power to delete or manipulate its own state backend.
  • Single Failure Domain: Accidental subscription deletion or resource group wiping destroys both the application and the state required to rebuild or recover it.
  • Violates: Microsoft CAF Landing Zone standards, NIST SP 800-53 AC-6.

6. Bypassing State Locking (-lock=false)

  • The Practice: Adding -lock=false to pipeline scripts or command lines to bypass an active lease lock.
  • Why it fails:
  • Silent Data Corruption: Overriding locks allows concurrent writes to the same state blob. Terraform JSON schemas will be written out of sequence, producing unparseable state trees and dangling infrastructure.
  • Violates: NIST SP 800-218 SSDF PW.6.1.

7. Direct Manual Edits to the State JSON

  • The Practice: Downloading terraform.tfstate from the storage container, opening it in an editor, altering strings, and re-uploading it directly.
  • Why it fails:
  • Schema & Lineage Desynchronization: Terraform tracks serial, lineage, and schema version hashes. Manual JSON modifications break cryptographic consistency checks, causing subsequent applies to crash.
  • Bypasses State Backup Mechanisms: Native CLI commands (terraform state rm/mv) automatically generate pre-flight safety backups; direct storage uploads bypass this safety net.
  • Violates: SOC 2 CC7.1, NIST SP 800-218 PW.1.2.

Regulatory and Industry Standards Mapping

State storage architecture must be defensible under external audits. The controls outlined in this guide directly satisfy the following international standards:

Standard & Identifier Control Requirement GRINNTEC State Storage Architecture Implementation
NIST SP 800-53 Rev. 5: AC-6 Least Privilege: Employ the principle of least privilege, allowing only authorized access for necessary tasks. Pipeline service principals receive container-scoped Storage Blob Data Contributor. Account-level data access is strictly forbidden.
NIST SP 800-53 Rev. 5: AC-5 Separation of Duties: Separate duties of individuals to prevent unauthorized modifications. Strict division between Platform State (Lane A / Tier 0) and Workload State (Lane B / Tier 2). Dedicated storage accounts per subscription.
NIST SP 800-53 Rev. 5: SC-8 & SC-13 Transmission & Cryptographic Protection: Protect confidentiality and integrity of transmitted data using approved cryptography. Enforced TLS 1.2+ minimum, HTTPS-only traffic, and private endpoints over isolated VNet backbones.
NIST SP 800-53 Rev. 5: SC-28 Protection at Rest: Protect the confidentiality and integrity of information at rest. 256-bit AES platform/CMK encryption at rest, infrastructure double encryption, and client-side Terraform 1.4+ state encryption.
NIST SP 800-53 Rev. 5: SI-12 Information Handling & Retention: Manage information retention and protect against accidental loss. Blob versioning enabled on state containers; 30-day soft delete retention for blobs and containers; ARM CanNotDelete locks.
NIST SP 800-53 Rev. 5: AU-2 & AU-6 Audit Events & Review: Record and review events to monitor unauthorized access. Entra ID authentication logs all data-plane access per user/SP. Shared Access Keys disabled to prevent anonymous access.
NIST SP 800-218 (SSDF v1.1): PW.1.2 & PW.6.1 Software Integrity & Configuration Security: Protect configuration files and build artifacts from tampering. OIDC federated machine identities, branch protection, automated state lease locking, and declarative moved blocks.
CIS Azure Benchmark v3.0: Control 3.1 Ensure 'Secure transfer required' is set to 'Enabled'. https_traffic_only_enabled = true enforced across all state storage accounts via Azure Policy.
CIS Azure Benchmark v3.0: Control 3.3 Ensure 'Storage Accounts' default network access is set to Deny. public_network_access_enabled = false; access mediated strictly through Azure Private Endpoints.
CIS Azure Benchmark v3.0: Control 3.5 Ensure Storage Account keys are not used / authentication via Entra ID. shared_access_key_enabled = false set on all vended state storage accounts.
CIS Azure Benchmark v3.0: Control 3.8 Ensure 'Blob soft delete' is set to 'Enabled'. Minimum 30-day retention policy configured on all blob services.
CIS Azure Benchmark v3.0: Control 5.2 Ensure resource locks are applied to critical resources. CanNotDelete management lock applied to rg-terraform-state and state storage accounts.
PCI DSS v4.0: Requirement 3.4 & 3.5 Protect Authentication Credentials at Rest: Protect cardholder data and cryptographic secrets everywhere stored. State files containing database passwords, private keys, or API tokens are encrypted with AES-256 and restricted to scoped machine identities.
PCI DSS v4.0: Requirement 8.3 & 8.6 Strong Authentication & Account Management: Prevent shared authentication credentials for administrative operations. Shared Keys disabled. CI/CD uses short-lived, verifiable OIDC tokens; human break-glass requires MFA and PIM elevation.
ISO/IEC 27001:2022: Control A.8.9 Configuration Management: Manage configurations to ensure security baselines are maintained. Single source of truth managed in versioned remote state; drift detection alerts on unauthorized changes.
ISO/IEC 27001:2022: Control A.8.24 Use of Cryptography: Enforce cryptography for data protection. Enforced in-transit (TLS 1.2+), at-rest (SSE/CMK), and client-side (Terraform native state encryption).
SOC 2 Type II: CC6.1 & CC6.3 Logical Access Controls: Limit access to system components to authorized identities. RBAC container-level scoping; automated pipeline access via OIDC without persistent credentials.
SOC 2 Type II: CC7.1 System Vulnerability & Integrity: Monitor and maintain system processing integrity. Distributed blob lease locking prevents split-brain state corruption; blob versioning enables immediate rollback.
Microsoft CAF Landing Zones Enterprise-Scale State Management: Dedicated management subscription hosting isolated remote state backends. Implemented via GRINNTEC azure-tenant-root and subscription-vending architecture.

Production Backend Configuration Reference

Below is the standard, production-hardened versions.tf backend declaration for a workload deployment:

terraform {
  required_version = ">= 1.5.0"

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

  backend "azurerm" {
    # Storage Account located in central Platform Management Subscription
    resource_group_name  = "rg-terraform-state"
    storage_account_name = "sttfstate4b2e9a1c" # Unique per-workload SA
    container_name       = "gt-mkdocs-prod-westeu"
    key                  = "workload.terraform.tfstate"

    # Enforce Entra ID authentication (no storage account keys)
    use_azuread_auth     = true
    use_oidc             = true

    # Subscription hosting the storage account (Platform Management)
    subscription_id      = "00000000-0000-0000-0000-000000000000"
  }
}
# Example: Non-interactive CI initialisation using Workload Identity Federation
export ARM_USE_OIDC=true
export ARM_CLIENT_ID="<vended-service-principal-client-id>"
export ARM_TENANT_ID="<tenant-id>"
export ARM_SUBSCRIPTION_ID="<workload-subscription-id>"

terraform init
terraform plan -out=tfplan