Secrets management in Terraform
Learn how to securely use secrets for deploying red team infrastructure and secrets management best practices for red team operators.
Up till now we have either used secrets which were pre-configured (e.g. AWS) or were hard coded in the configuration (e.g. SSH user). However, in any infrastructure deployment—especially in red team contexts—secrets management is a foundational skill. In this post, let’s dive deep into how to properly manage these secrets in a way that’s both secure and operationally sustainable.
Don’t hardcode keys
One of the cardinal rules in infrastructure automation—especially when working in red team environments—is to never hardcode secrets directly into .tf
files or commit them into version control. This includes access tokens, cloud provider keys, passwords, and any sensitive variables. Hardcoding secrets into Terraform configurations introduces a high risk of accidental leaks, especially when code is pushed to public or shared repositories, or reused in multiple environments.
Follow my journey of 100 Days of Red Team on WhatsApp, Telegram or Discord.
For example, a naïve configuration might look like this:
provider "aws" {
access_key = "AKIAEXAMPLE123"
secret_key = "abc123exampleSECRET"
region = "us-east-1"
}
This is a dangerous pattern and must be avoided. Even in private repositories, secrets should not live directly in source files. It only takes one misconfigured GitHub Actions pipeline or a cloned repository on a compromised operator machine to expose everything.
Terraform doesn’t encrypt .tf
files, and the state file (terraform.tfstate
) also stores sensitive data in plaintext by default. This means that even if secrets are not in the source, they might still leak if not handled correctly in state files or logs. This brings us to better alternatives.
There are several methods for managing secrets securely in Terraform. Three common approaches are using environment variables, variable definition files (.tfvars
), and pulling secrets from managed services like AWS Systems Manager (SSM).
1. Environment Variables
Terraform allows providers like AWS to read credentials from environment variables, which is often the most straightforward way to inject secrets without exposing them in code.
export AWS_ACCESS_KEY_ID="AKIAEXAMPLE123"
export AWS_SECRET_ACCESS_KEY="abc123exampleSECRET"
terraform apply
This keeps secrets out of the Terraform files and can be easily integrated into shell scripts, CI pipelines, or custom wrappers. However, caution must still be exercised—logs and command history should not capture these values.
2. Variable Files (.tfvars)
Terraform supports external variable files that can be referenced during execution. These files can be excluded from version control using .gitignore
. For example, a configuration using secrets.tfvars variable file will look like this:
secrets.tfvars
aws_access_key = "AKIAEXAMPLE123"
aws_secret_key = "abc123exampleSECRET"
main.tf
variable "aws_access_key" {}
variable "aws_secret_key" {}
provider "aws" {
access_key = var.aws_access_key
secret_key = var.aws_secret_key
region = "us-east-1"
}
This can be executed like:
terraform apply -var-file="secrets.tfvars"
This method separates secrets from logic but still leaves them at rest in plaintext. Encryption or secure storage mechanisms should be considered if .tfvars
files must be shared between operators.
Note: Terraform will still load the terraform.tfvars file automatically along with any additional variable files.
3. AWS Systems Manager (SSM) Parameter Store
For teams deploying red team infrastructure in AWS, using SSM Parameter Store provides a secure way to retrieve secrets at runtime. SSM allows storing parameters as "SecureString" values, which are encrypted at rest with KMS.
Storing a secret:
aws ssm put-parameter \
--name "/redteam/ssh_password" \
--value "secure-ssh-password" \
--type "SecureString"
Fetching within Terraform (via data block):
data "aws_ssm_parameter" "ssh_password" {
name = "/redteam/ssh_password"
with_decryption = true
}
user_data = <<-EOF
#!/bin/bash
echo '${var.ssh_user}:${data.aws_ssm_parameter.ssh_password.value}' | chpasswd
sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/' /etc/ssh/sshd_config
systemctl restart sshd
EOF
This approach avoids storing any secrets (used by infrastructure) locally. It's especially useful when rotating credentials regularly or managing multiple operator teams.
Note: AWS SSM cannot be used to bootstrap the AWS provider.
Note: I have not included secrets.tfvars file. So remember to create one before executing terraform plan and terraform apply commands for this project.
Following permissions need to be added to the
TerraformEC2Access
IAM policy before using the above mentioned project:
ssm:GetParameter
ssm:PutParameter
This project will also need the SSH password for ec2-user to be stored in AWS SSM. You can directly create it via the console or execute the following command from AWS CLI:
aws ssm put-parameter --name "/100daysofredteam/ec2-user" --value "secure-ssh-password" --type "SecureString"
Secrets hygiene in red team workflows
Secrets hygiene is more than just how secrets are stored. It's a discipline that includes rotating credentials, limiting access based on the principle of least privilege, and minimizing the blast radius of leaks. For red teamers, this also means understanding the lifecycle of secrets across different phases of an operation.
Operators must consider how secrets are:
Created - Where do cloud credentials originate? Are they tied to a red team identity or borrowed from target simulations?
Distributed - How are secrets shared among team members or automation scripts?
Used - Are secrets injected via CI/CD pipelines, command-line tools, or third-party wrappers?
Revoked - Are secrets automatically cleaned up when infrastructure is destroyed?
In earlier posts, especially those covering remote backends, workspaces, and Terraform state, the idea of isolating environments and abstracting configurations was emphasized. This post ties directly into that principle—secrets should be treated as transient, scoped to specific environments, and wiped clean when infrastructure is no longer needed.
Secrets as operational risk
In red team campaigns, secrets are not just configuration parameters—they’re targets. Credential exposure can compromise the entire operation or even reveal infrastructure to blue teams or third-party observers. Hardcoded keys can leak in screenshots, screen recordings, or be picked up by cloud security tools watching for patterns like AKIA
prefixes in files.
Red teams must assume secrets will leak eventually and design systems that minimize damage when that happens. That means:
Use temporary, expiring access tokens wherever possible.
Always prefer role-based access (IAM roles, federated identities) over static keys.
Never run Terraform from machines that also contain client data or logs from an engagement.
Treat Terraform state files as sensitive assets—encrypt them, store them securely, and audit access.
TL;DR
- Avoid hardcoding secrets like API keys or passwords directly in Terraform files.
- Use .tfvars, environment variables, or external secret stores like AWS SSM Parameter Store to manage sensitive data securely.
- AWS SSM cannot be used to bootstrap the AWS provider itself
- Keep secrets out of version control, sanitize Terraform state, and rotate credentials after use.
- Treat infrastructure secrets (like SSH passwords or C2 tokens) as burnable assets—manage them with the same discipline as access keys.
Follow my journey of 100 Days of Red Team on WhatsApp, Telegram or Discord.