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:
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¶
Dynamic Credentials (Recommended)¶
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
.tffiles - 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¶
- Use
sensitive = trueon all outputs containing secrets - Enable
.terraform.lock.hclto 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 lockto create a dependency lock file with SHA256 checksums
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-mandatoryenforcement - 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
sensitiveflag 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 |