Skip to content

Terraform Level 1, Task 24: Managing Secrets with AWS Secrets Manager

Today's task was a crucial lesson in cloud security best practices. I used Terraform to create a secret in AWS Secrets Manager. This is the standard, secure way to handle sensitive data like database credentials, API keys, and other application secrets.

This was a fantastic exercise because it taught me to stop thinking about secrets as simple strings and start thinking about them as managed, versioned resources. I learned how to create a structured secret (with a username and password) and define it as code. This document is my very detailed, first-person guide to that entire process, with a deep dive into the code and the core concepts of secrets management.

Table of Contents


The Task

My objective was to use Terraform to create a new secret in AWS Secrets Manager. The specific requirements were: 1. All code had to be in a single main.tf file. 2. The secret's name had to be devops-secret. 3. The secret's value had to be a structured key-value pair: username: admin and password: Namin123. 4. The resource had to be created in the us-east-1 region.


My Step-by-Step Solution

The process involved writing a Terraform file that defined two resources: one for the secret "container" and one for the secret's actual value.

Phase 1: Writing the Code

In the /home/bob/terraform directory, I created my main.tf file. I wrote the following declarative code.

# 1. Configure the AWS Provider
provider "aws" {
  region = "us-east-1"
}

# 2. Define the Secrets Manager Secret Resource
# This resource creates the "shell" or container for the secret.
resource "aws_secretsmanager_secret" "devops_secret_resource" {
  name = "devops-secret"
  tags = {
    Name = "devops-secret"
  }
}

# 3. Define the Secret Version Resource
# This resource populates the secret with its first version of the actual value.
resource "aws_secretsmanager_secret_version" "devops_secret_version" {
  secret_id = aws_secretsmanager_secret.devops_secret_resource.id

  # The 'secret_string' is the actual sensitive data.
  # We use jsonencode to create a well-formatted JSON string from a map.
  secret_string = jsonencode({
    username = "admin"
    password = "Namin123"
  })
}

Phase 2: The Terraform Workflow

From my terminal in the same directory, I executed the three core commands. 1. Initialize: terraform init. 2. Plan: terraform plan. The output showed me that Terraform would create two resources: aws_secretsmanager_secret and aws_secretsmanager_secret_version. 3. Apply: terraform apply. After I confirmed with yes, Terraform created the secret in my AWS account. The success message confirmed the task was done.


Why Did I Do This? (The "What & Why")

- AWS Secrets Manager: This is a fully managed service specifically designed to help you protect and manage secrets needed to access your applications, services, and IT resources. - The Problem it Solves (Decoupling and Security): The absolute worst practice is to hardcode secrets (like passwords or API keys) directly in your application code or configuration files. This is a huge security risk. Secrets Manager solves this by: 1. Centralizing Secrets: It provides a single, secure place to store all your sensitive data. 2. Decoupling: My application code no longer contains the secret. Instead, it contains code that queries the Secrets Manager API at runtime to fetch the secret it needs. 3. Encryption: Secrets Manager automatically encrypts secrets at rest using AWS Key Management Service (KMS). 4. Fine-Grained Access Control: I can use IAM policies to control exactly which users or servers are allowed to retrieve which secrets. 5. Automatic Rotation: It has built-in capabilities to automatically rotate credentials for services like RDS databases, which is a powerful security feature. - Structured Secrets: My task required storing a username and a password. Instead of creating two separate secrets, the best practice is to store related data together. The standard way to do this is to store the secret value as a JSON string, which is exactly what the jsonencode function helped me do.


Deep Dive: A Line-by-Line Explanation of My main.tf Script

This script showed me that creating a secret is a two-step process in Terraform: creating the secret object, then creating its first version.

[Image of an application fetching a secret from AWS Secrets Manager]

# Standard provider configuration block.
provider "aws" {
  region = "us-east-1"
}

# This is the first resource block, which defines the Secret "container" itself.
# "aws_secretsmanager_secret" is the Resource TYPE.
# "devops_secret_resource" is the local NAME I use to refer to this secret.
resource "aws_secretsmanager_secret" "devops_secret_resource" {

  # The 'name' argument sets the unique name for the secret.
  name = "devops-secret"
  tags = {
    Name = "devops-secret"
  }
}

# This is the second resource block, which defines the actual VALUE of the secret.
# Every time I update this resource, Secrets Manager creates a new "version" of the secret.
resource "aws_secretsmanager_secret_version" "devops_secret_version" {

  # This is the most important line: The Dependency Link.
  # The 'secret_id' argument tells this version which secret it belongs to.
  # I am providing the ID by referencing an attribute from the resource I defined above.
  secret_id = aws_secretsmanager_secret.devops_secret_resource.id

  # 'secret_string' is where I put the sensitive data.
  # The 'jsonencode' function is a built-in Terraform function that takes a Terraform
  # map and converts it into a properly formatted JSON string. This is the standard
  # way to store structured data like a username/password pair.
  secret_string = jsonencode({
    username = "admin"
    password = "Namin123"
  })
}

Common Pitfalls

- Hardcoding Secrets in Code: The biggest anti-pattern. Even though the secret value is visible in my main.tf file, it is considered a security improvement because the value will be encrypted in the Terraform state file and in AWS. In a real production setup, I would use a more advanced method to inject this value from a secure vault at runtime, rather than committing it to Git. - Forgetting the secret_version resource: Just creating the aws_secretsmanager_secret resource is not enough. This only creates an empty secret with no value. The aws_secretsmanager_secret_version resource is mandatory to actually store any data. - IAM Permissions: The IAM user or role running Terraform needs secretsmanager:CreateSecret permission. The application that needs to read the secret at runtime needs secretsmanager:GetSecretValue permission (and kms:Decrypt if it's a default encrypted secret).


Exploring the Essential Terraform Commands

- terraform init: Prepared my working directory by downloading the aws provider plugin. - terraform validate: Checks the syntax of Terraform files. - terraform fmt: Auto-formats code to the standard style. - terraform plan: Showed me a "dry run" plan of the resources to be created. - terraform apply: Executed the plan and created the secret after I confirmed with yes. - terraform show: Shows the current state of my managed infrastructure. - terraform state list: Lists all the resources that Terraform is currently managing. -- terraform destroy: The opposite of apply. It creates a plan to destroy all the infrastructure managed by the current configuration.