Skip to content

Security

Authentication Model

Terraform itself does not authenticate users — authentication is handled by the state backend and the cloud providers it manages:

Component Authentication Method
Terraform Cloud/Enterprise User accounts, SSO (SAML/OIDC), API tokens, Team tokens
AWS Provider Static keys, IAM roles, AssumeRoleWithWebIdentity (OIDC)
Azure Provider Service principals, managed identity, OIDC federated credentials
GCP Provider Service accounts, Workload Identity Federation
State backends (S3, GCS, Azure) Backend-specific credentials
graph TB
    subgraph Operators
        Dev["Developer\n(terraform CLI)"]
        VCS["VCS Webhook\n(GitHub/GitLab)"]
        API["API Token\n(CI/CD)"]
    end
    subgraph TFCloud["Terraform Cloud / Enterprise"]
        AuthN["Identity\n(SAML/OIDC/Tokens)"]
        Sentinel["Sentinel\n(Policy Engine)"]
        StateStore["State Storage\n(Encrypted)"]
    end
    subgraph Providers
        AWS["AWS\n(IAM/OIDC)"]
        Azure["Azure\n(MSI/OIDC)"]
        GCP["GCP\n(WIF/OIDC)"]
    end
    Dev --> AuthN
    VCS --> AuthN
    API --> AuthN
    AuthN --> Sentinel
    Sentinel --> StateStore
    Sentinel --> Providers

State File Security

The Terraform state file is the most critical security asset — it contains all resource IDs, computed attributes, and potentially plaintext secrets.

State File Risks

Risk Impact
Plaintext secrets Database passwords, API keys stored in cleartext
Resource manipulation Modified state can destroy or modify infrastructure
Sensitive outputs IP addresses, internal DNS names, instance IDs
Drift concealment Altered state masks unauthorized infrastructure changes

Terraform Cloud State Security

  • State encrypted at rest (AES-256) and in transit (TLS 1.2+)
  • State access URLs are time-limited (25 hours) with embedded secrets
  • State never written to disk during Terraform Cloud operations
  • Full state versioning with rollback capability

Self-Managed Backend Hardening

For S3, GCS, Azure Blob backends:

terraform {
  backend "s3" {
    bucket         = "terraform-state-prod"
    key            = "infra/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    kms_key_id     = "arn:aws:kms:..."
    dynamodb_table = "terraform-locks"
  }
}
  • Enable server-side encryption with customer-managed keys
  • Enable state locking (DynamoDB for S3, native for GCS/Azure)
  • Restrict bucket access with IAM policies (least privilege)
  • Enable versioning to recover from accidental deletion
  • Enable access logging for audit trail

Sentinel Policy as Code

Terraform Enterprise and HCP Terraform enforce organizational policies via Sentinel:

import "tfplan"

main = rule {
    version.new(tfplan.terraform_version).greater_than("1.5.0")
}

Common Sentinel Policies

Policy Category Example
Resource restrictions Only allow specific instance types
Encryption enforcement Require KMS encryption on S3, EBS, RDS
Network security Deny public S3 buckets, require security groups
Tagging compliance Mandate cost-center and environment tags
Provider version pinning Enforce minimum provider versions

Sentinel enforcement levels: - Advisory: Log violations, allow apply - Soft-mandatory: Allow override with justification - Hard-mandatory: Block apply entirely

Provider Credential Management

Use OIDC federation to generate short-lived credentials instead of static keys:

Provider OIDC Method
AWS AssumeRoleWithWebIdentity with Terraform Cloud OIDC
Azure Workload Identity Federation
GCP Workload Identity Federation

Static Credentials (Legacy)

When OIDC is unavailable:

  • Store credentials in environment variables, never in .tf files
  • Use HashiCorp Vault to generate short-lived credentials
  • Rotate access keys on a defined schedule
  • Use IAM roles with conditional trust policies (IP restrictions, MFA)

Credential Exposure Prevention

output "db_password" {
  value     = aws_db_instance.main.password
  sensitive = true
}
  • Use sensitive = true on all outputs containing secrets
  • Enable .terraform.lock.hcl to pin provider versions
  • Verify provider checksums with terraform providers lock

Module Registry Security

Module Verification

  • Pin module versions to specific tags or commit SHAs — never use latest
  • Verify module sources: prefer Terraform Registry or internal Git repos
  • Use terraform providers lock to create a dependency lock file with SHA256 checksums
module "vpc" {
  source  = "terraform-aws-modules/vpc/aws"
  version = "5.5.0"
}

Module Security Checklist

  • Module source is from trusted registry or internal repository
  • Module version is pinned to a specific tag
  • Provider versions are constrained with >= and < bounds
  • No default credentials in variable definitions
  • All sensitive variables declared with sensitive = true

Terraform Cloud RBAC

Role Scope Permissions
Organization Owner Org Manage users, teams, policies, billing
Organization Member Org Read org settings, create workspaces
Team Lead Team Manage team membership
Write Workspace Plan and apply
Read Workspace View state and outputs only
Plan Workspace Run plans but not applies
Admin Workspace Manage workspace settings and permissions

Workspace Isolation

Each workspace in Terraform Cloud has isolated state storage, variable sets, run history, and access controls. Use separate workspaces for each environment (dev, staging, prod) with escalating RBAC restrictions.

Hardening Checklist

  • Use Terraform Cloud/Enterprise as state backend (encrypted, audited)
  • Enable state encryption on self-managed backends
  • Use dynamic provider credentials (OIDC) — eliminate static keys
  • Configure Sentinel policies with hard-mandatory enforcement
  • Pin all module and provider versions with lock file
  • Mark all sensitive outputs with sensitive = true
  • Enable SSO (SAML/OIDC) for Terraform Cloud access
  • Use variable sets with sensitive flag for secrets
  • Restrict workspace access with team-level RBAC
  • Enable audit logging in Terraform Enterprise
  • Rotate API tokens quarterly

Known Pitfalls

Pitfall Risk Mitigation
Plaintext secrets in state Credential exposure Use sensitive = true, encrypt state
terraform state pull on local disk Unencrypted state file Use remote backends exclusively
Unpinned module versions Supply chain attacks Pin versions, use lock file
Broad workspace write access Unauthorized infrastructure changes Least-privilege RBAC per workspace
Missing state locking Concurrent apply corruption Enable DynamoDB/native locking
Stale provider credentials Compromised long-lived keys Use OIDC dynamic credentials