---
name: terraform-style-guide-security
description: Generate Terraform HCL code following HashiCorp's security practices
---

# Terraform Style Guide - Security

When generating code, apply security hardening:

- Enable encryption at rest by default
- Configure private networking where applicable
- Apply principle of least privilege for security groups
- Enable logging and monitoring
- Never hardcode credentials or secrets
- Mark sensitive outputs with `sensitive = true`
- Use `ephemeral` resources and write-only attributes
  for sensitive data when possible

## Example: Secure S3 Bucket

```hcl
resource "aws_s3_bucket" "data" {
  bucket = "${var.project}-${var.environment}-data"
  tags   = local.common_tags
}

resource "aws_s3_bucket_versioning" "data" {
  bucket = aws_s3_bucket.data.id

  versioning_configuration {
    status = "Enabled"
  }
}

resource "aws_s3_bucket_server_side_encryption_configuration" "data" {
  bucket = aws_s3_bucket.data.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm     = "aws:kms"
      kms_master_key_id = aws_kms_key.s3.arn
    }
  }
}

resource "aws_s3_bucket_public_access_block" "data" {
  bucket = aws_s3_bucket.data.id

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}
```

## Ephemeral resources

Ephemeral resources prevent sensitive data being stored in state.
For more information on ephemeral resources, see the
[Terraform documentation](https://developer.hashicorp.com/terraform/language/block/ephemeral).

Before you generate code for an ephemeral resource, check that the Terraform
version is greater than or equal to 1.11.0.

Then, follow this priority order for managing sensitive attributes:

1. **First priority: Native secrets manager integration**
   If a resource has the ability to automatically manage a sensitive attribute by
   storing it in a secrets manager (e.g., AWS Secrets Manager, Azure Key Vault),
   use that configuration. This is the preferred approach.

   ```hcl
   # Bad
   resource "aws_rds_cluster" "example" {
     cluster_identifier = "example"
     database_name      = "test"
     master_username    = "test"
     master_password    = var.db_master_password
   }

   # Good, managed by AWS Secrets Manager by default
   resource "aws_rds_cluster" "test" {
     cluster_identifier          = "example"
     database_name               = "test"
     manage_master_user_password = true
     master_username             = "test"
   }
   ```

2. **Second priority: Write-only attributes with ephemeral resources**
   If a resource has a write-only attribute but no native secrets manager integration,
   use an `ephemeral` resource for the sensitive data and pass that to the write-only
   attribute. Default the write-only version to 1.

   ```hcl
   # Bad
   resource "random_password" "password" {
     length           = 16
     special          = true
     override_special = "!#$%&*()-_=+[]{}<>:?"
   }

   resource "vault_kv_secret_v2" "example" {
     mount               = vault_mount.kvv2.path
     name                = "secret"

     data_json = jsonencode(
       {
         password = "${random_password.password.result}",
       }
     )
   }

   # Good
   ephemeral "random_password" "password" {
     length           = 16
     special          = true
     override_special = "!#$%&*()-_=+[]{}<>:?"
   }

   resource "vault_kv_secret_v2" "example" {
     mount               = vault_mount.kvv2.path
     name                = "secret"

     data_json_wo = jsonencode(
       {
         password = "${ephemeral.random_password.password.result}",
       }
     )
     data_json_wo_version = 1
   }
   ```

   If you need to retrieve a secret from a secrets manager to pass
   to a resource, use the `ephemeral` version of the resource to
   retrieve the secret and pass it to another resource.

   ```hcl
   # Good
   ephemeral "vault_kv_secret_v2" "db_secret" {
     mount = vault_mount.kvv2.path
     mount_id = vault_mount.kvv2.id
     name = vault_kv_secret_v2.db_root.name
   }

   resource "vault_database_secret_backend_connection" "postgres" {
     backend       = vault_mount.db.path
     name          = "postrgres-db"
     allowed_roles = ["*"]

     postgresql {
       connection_url = "postgresql://{{username}}:{{password}}@localhost:5432/postgres"
       password_authentication = ""
       username = "postgres"
       password_wo = tostring(ephemeral.vault_kv_secret_v2.db_secret.data.password)
       password_wo_version = 1
     }
   }
   ```

3. **Last resort: Regular resources**
   Only use a regular resource that has sensitive data written to state if neither of the above
   options are available, resource does not offer a write-only attribute or ephemeral resource
   alternative, or the Terraform version is less than 1.11.0.
