Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions terraform-encrypted-state/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions terraform-encrypted-state/kms.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
resource "aws_kms_key" "tf_state" {
description = "KMS key for Terraform/OpenTofu state encryption"
}
Comment on lines +1 to +3
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add an explicit key policy – default policy is not recommended

Static analysis (CKV2_AWS_64) flagged the absence of a policy block. Relying on the implicit default KMS key policy can break least-privilege and cross-account access scenarios.

 resource "aws_kms_key" "tf_state" {
   description = "KMS key for Terraform/OpenTofu state encryption"
+
+  policy = jsonencode({
+    Version : "2012-10-17",
+    Id      : "key-default-1",
+    Statement : [
+      {
+        Sid      : "Enable IAM root and principals",
+        Effect   : "Allow",
+        Principal : { AWS : "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" },
+        Action   : "kms:*",
+        Resource : "*"
+      }
+    ]
+  })
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
resource "aws_kms_key" "tf_state" {
description = "KMS key for Terraform/OpenTofu state encryption"
}
resource "aws_kms_key" "tf_state" {
description = "KMS key for Terraform/OpenTofu state encryption"
policy = jsonencode({
Version : "2012-10-17",
Id : "key-default-1",
Statement : [
{
Sid : "Enable IAM root and principals",
Effect : "Allow",
Principal : { AWS : "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" },
Action : "kms:*",
Resource : "*"
}
]
})
}
🧰 Tools
🪛 Checkov (3.2.334)

[MEDIUM] 1-3: Ensure KMS key Policy is defined

(CKV2_AWS_64)

🤖 Prompt for AI Agents
In terraform-encrypted-state/kms.tf at lines 1 to 3, the aws_kms_key resource
lacks an explicit key policy block, which is flagged by static analysis for
security reasons. Add a `policy` block to define a custom key policy that
explicitly grants necessary permissions, ensuring least-privilege access and
supporting cross-account scenarios instead of relying on the default implicit
policy.


output "kms_key_id" {
value = aws_kms_key.tf_state.key_id
description = "KMS key id"
}
20 changes: 20 additions & 0 deletions terraform-encrypted-state/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generate a random password
resource "random_password" "main_password" {
length = 128
special = false
upper = true
lower = true
numeric = true
}

# Create AWS Secrets Manager secret
resource "aws_secretsmanager_secret" "main_password" {
name = "main-application-password"
description = "Main application password"
}

# Store the password in the secret
resource "aws_secretsmanager_secret_version" "main_password" {
secret_id = aws_secretsmanager_secret.main_password.id
secret_string = random_password.main_password.result
}
3 changes: 3 additions & 0 deletions terraform-encrypted-state/providers.tofu
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
provider "aws" {
region = "us-east-1"
}
36 changes: 36 additions & 0 deletions terraform-encrypted-state/versions.tofu
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
terraform {
required_version = ">= 1.7" # Encrypted state available in OpenTofu 1.7 and later

required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 6.0.0"
}
random = {
source = "hashicorp/random"
version = ">= 3.0.0"
}
}

encryption {
method "unencrypted" "migrate" {} # to support the current unencrypted state

key_provider "aws_kms" "kms_key" {
kms_key_id = "INSERT_KMS_KEY_ID_HERE"
region = "us-east-1"
key_spec = "AES_256"
}
Comment on lines +18 to +22
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid committing placeholder KMS key IDs

kms_key_id = "INSERT_KMS_KEY_ID_HERE" will make terraform init fail. Inject the ID via variable or reference the aws_kms_key.tf_state resource if this file lives in the same configuration.

-  kms_key_id = "INSERT_KMS_KEY_ID_HERE"
+  # If defined in the same workspace:
+  # kms_key_id = aws_kms_key.tf_state.key_id
+  # Otherwise pass it through a variable:
+  # kms_key_id = var.kms_key_id
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
key_provider "aws_kms" "kms_key" {
kms_key_id = "INSERT_KMS_KEY_ID_HERE"
region = "us-east-1"
key_spec = "AES_256"
}
key_provider "aws_kms" "kms_key" {
# If defined in the same workspace:
# kms_key_id = aws_kms_key.tf_state.key_id
# Otherwise pass it through a variable:
# kms_key_id = var.kms_key_id
region = "us-east-1"
key_spec = "AES_256"
}
🤖 Prompt for AI Agents
In terraform-encrypted-state/versions.tofu around lines 18 to 22, the kms_key_id
is set to a placeholder string which causes terraform init to fail. Replace the
hardcoded placeholder with a variable reference or use a reference to the
aws_kms_key.tf_state resource if it exists in the same configuration, ensuring
the actual KMS key ID is dynamically injected during terraform execution.


method "aes_gcm" "kms" {
keys = key_provider.aws_kms.kms_key
}
Comment on lines +24 to +26
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

keys attribute must be a list

OpenTofu/TF 1.7 expects a list for keys. Current syntax will error.

-  keys = key_provider.aws_kms.kms_key
+  keys = [key_provider.aws_kms.kms_key]
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
method "aes_gcm" "kms" {
keys = key_provider.aws_kms.kms_key
}
method "aes_gcm" "kms" {
- keys = key_provider.aws_kms.kms_key
+ keys = [key_provider.aws_kms.kms_key]
}
🤖 Prompt for AI Agents
In terraform-encrypted-state/versions.tofu around lines 24 to 26, the keys
attribute is currently assigned a single value but it must be a list according
to OpenTofu/TF 1.7 requirements. To fix this, wrap the existing value of keys in
square brackets to make it a list, ensuring the syntax matches the expected
format and prevents errors.


state {
method = method.aes_gcm.kms
fallback {
method = method.unencrypted.migrate
}
}
}

}
45 changes: 45 additions & 0 deletions tf-ephemeral/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions tf-ephemeral/ephemeral-resources.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
ephemeral "random_password" "random_password_ephemeral" {
length = 16
special = true
override_special = "!#$%&*()-_=+[]{}<>:?"
}

resource "aws_secretsmanager_secret" "random_password_ephemeral_secret" {
name = "random_password_ephemeral_secret"
}

resource "aws_secretsmanager_secret_version" "random_password_ephemeral_secret_version" {
secret_id = aws_secretsmanager_secret.random_password_ephemeral_secret.id
secret_string_wo = ephemeral.random_password.random_password_ephemeral.result
secret_string_wo_version = 1
}
Comment on lines +11 to +15
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Invalid attribute names on aws_secretsmanager_secret_version

secret_string_wo and secret_string_wo_version do not exist in the AWS provider schema. Use secret_string (optionally with version_stages) or the binary_secret attributes.

-resource "aws_secretsmanager_secret_version" "random_password_ephemeral_secret_version" {
-  secret_id                = aws_secretsmanager_secret.random_password_ephemeral_secret.id
-  secret_string_wo         = ephemeral.random_password.random_password_ephemeral.result
-  secret_string_wo_version = 1
-}
+resource "aws_secretsmanager_secret_version" "random_password_ephemeral_secret_version" {
+  secret_id     = aws_secretsmanager_secret.random_password_ephemeral_secret.id
+  secret_string = jsonencode({
+    password = ephemeral.random_password.random_password_ephemeral.result
+  })
+  version_stages = ["AWSCURRENT"]
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
resource "aws_secretsmanager_secret_version" "random_password_ephemeral_secret_version" {
secret_id = aws_secretsmanager_secret.random_password_ephemeral_secret.id
secret_string_wo = ephemeral.random_password.random_password_ephemeral.result
secret_string_wo_version = 1
}
resource "aws_secretsmanager_secret_version" "random_password_ephemeral_secret_version" {
secret_id = aws_secretsmanager_secret.random_password_ephemeral_secret.id
secret_string = jsonencode({
password = ephemeral.random_password.random_password_ephemeral.result
})
version_stages = ["AWSCURRENT"]
}
🤖 Prompt for AI Agents
In tf-ephemeral/ephemeral-resources.tf around lines 11 to 15, the attributes
secret_string_wo and secret_string_wo_version used in the
aws_secretsmanager_secret_version resource are invalid. Replace secret_string_wo
with secret_string and remove secret_string_wo_version or replace it with the
correct attribute version_stages if needed, according to the AWS provider
schema.

28 changes: 28 additions & 0 deletions tf-ephemeral/ephemeral-variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
variable "api_key" {
sensitive = true # Still stored in state!
default = "xK9#mP2$vL@4qR7!nT6&wZ8*bY3^fH5j-EXAMPLE_SECRET_IN_PLAINTEXT"
}
Comment on lines +1 to +4
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Hard-coded secret should be removed from VCS

Storing an actual API key in the default value means the secret is committed to source control, defeating both sensitive and the later “ephemeral” demo. Ship the example with no default (or a placeholder) and require the user to supply the value via TF_VAR_api_key / .tfvars.

-variable "api_key" {
-  sensitive = true # Still stored in state!
-  default   = "xK9#mP2$vL@4qR7!nT6&wZ8*bY3^fH5j-EXAMPLE_SECRET_IN_PLAINTEXT"
-}
+variable "api_key" {
+  type        = string
+  sensitive   = true # Still stored in state!
+  description = "API key to be provided at apply time (e.g. TF_VAR_api_key)."
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
variable "api_key" {
sensitive = true # Still stored in state!
default = "xK9#mP2$vL@4qR7!nT6&wZ8*bY3^fH5j-EXAMPLE_SECRET_IN_PLAINTEXT"
}
variable "api_key" {
type = string
sensitive = true # Still stored in state!
description = "API key to be provided at apply time (e.g. TF_VAR_api_key)."
}
🤖 Prompt for AI Agents
In tf-ephemeral/ephemeral-variables.tf lines 1 to 4, remove the hard-coded API
key from the default value to avoid committing secrets to version control.
Replace the default with no value or a placeholder string, and ensure the
variable is required to be set externally via TF_VAR_api_key or a .tfvars file.


# Before - using sensitive (stored in state)
resource "aws_secretsmanager_secret_version" "sensitive_secret" {
secret_id = aws_secretsmanager_secret.sensitive_secret.id
secret_string = var.api_key # ❌ Regular attribute - stored in state!
}

output "sensitive_secret" {
value = aws_secretsmanager_secret_version.sensitive_secret.secret_string
sensitive = true
}


variable "api_key_ephemeral" {
ephemeral = true # Never stored!
default = "wJalrXUtnFEMI/K7MDENG/bPxRfiCY-EXAMPLE_SECRET_WONT_BE_STORED"
}
Comment on lines +18 to +21
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Ephemeral variable example also leaks secret through VCS

Even though ephemeral = true prevents the value from reaching state, the literal secret is still baked into the repo. Replace with an empty default or a clearly invalid placeholder to avoid misconceptions.

-variable "api_key_ephemeral" {
-  ephemeral = true # Never stored!
-  default   = "wJalrXUtnFEMI/K7MDENG/bPxRfiCY-EXAMPLE_SECRET_WONT_BE_STORED"
-}
+variable "api_key_ephemeral" {
+  type        = string
+  ephemeral   = true # Never stored!
+  description = "API key supplied at apply time; never persisted to state."
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
variable "api_key_ephemeral" {
ephemeral = true # Never stored!
default = "wJalrXUtnFEMI/K7MDENG/bPxRfiCY-EXAMPLE_SECRET_WONT_BE_STORED"
}
variable "api_key_ephemeral" {
type = string
ephemeral = true # Never stored!
description = "API key supplied at apply time; never persisted to state."
}
🤖 Prompt for AI Agents
In tf-ephemeral/ephemeral-variables.tf around lines 18 to 21, the variable
"api_key_ephemeral" has a default value containing a literal secret, which leaks
sensitive information through version control. Remove the actual secret from the
default value and replace it with an empty string or a clearly invalid
placeholder to prevent accidental exposure while keeping the variable definition
valid.


# After - using ephemeral (✅ never stored)
resource "aws_secretsmanager_secret_version" "ephemeral_secret" {
secret_id = aws_secretsmanager_secret.ephemeral_secret.id
secret_string_wo = var.api_key_ephemeral # ✅ Write only attribute - NOT stored!
secret_string_wo_version = 1
}
11 changes: 11 additions & 0 deletions tf-ephemeral/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
########################################################################################
# Create the secrets in AWS Secrets Manager
# There is no secret stored in these resources. The actual secret is managed by the `aws_secretsmanager_secret_version` resource.
########################################################################################
resource "aws_secretsmanager_secret" "sensitive_secret" {
name = "demo-sensitive-secret"
}

resource "aws_secretsmanager_secret" "ephemeral_secret" {
name = "demo-ephemeral-secret"
}
3 changes: 3 additions & 0 deletions tf-ephemeral/providers.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
provider "aws" {
region = "us-east-1"
}
14 changes: 14 additions & 0 deletions tf-ephemeral/versions.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
terraform {
required_version = ">= 1.11.0" # Ephemeral resources and variables are only available in Terraform 1.11 and later

required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 6.0.0"
}
random = {
source = "hashicorp/random"
version = ">= 3.0.0"
}
}
}