Secure Secrets Management in Terraform — Part2: AWS Secret Manager

Secure Secrets Management in Terraform — Part2: AWS Secret Manager

Following our previous tutorial about using AWS KMS for secrets management , this second part explores how to leverage AWS Secrets Manager with Terraform/OpenTofu for more advanced secrets management capabilities. AWS Secrets Manager provides additional features like automatic rotation, fine-grained access control, and centralized secrets management.

Prerequisites

1 — Setting Up AWS Secrets Manager

First, let’s create the necessary resources to store and manage our secrets:

    # Create a Secrets Manager secret  
    resource "aws_secretsmanager_secret" "application_secret" {  
      name                    = "awsmorocco/application/secrets"  
      description            = "Secrets for the AWS Morocco application"  
      recovery_window_in_days = 7  
        
      # Optional: Use our KMS key from Part 1   
      # ARN or Id of the AWS KMS key to be used to encrypt the secret   
      # values in the versions stored in this secret.  
      kms_key_id = aws_kms_key.secrets_key.arn  
    }  
      
    resource "random_password" "password" {  
      length           = 24  
      special          = true  
      override_special = "!#$%&*()-_=+[]{}<>:?"  
    }  
      
    # Create a secret version with initial secret values  
    resource "aws_secretsmanager_secret_version" "application_secret_version" {  
      secret_id = aws_secretsmanager_secret.application_secret.id  
        
      secret_string = jsonencode({  
        db_username = "awsmorocco-user1"  
        db_password = random_password.password.result  
        api_key     = data.aws_kms_secrets.application_secrets.plaintext["api_key"]  
      })  
    }  
      
    # Create an IAM policy for accessing the secret  
    resource "aws_iam_policy" "secret_access_policy" {  
      name        = "awsmorocco-secret-access-policy"  
      description = "Policy for accessing application secrets"  
      
      policy = jsonencode({  
        Version = "2012-10-17"  
        Statement = [  
          {  
            Effect = "Allow"  
            Action = [  
              "secretsmanager:GetSecretValue",  
              "secretsmanager:DescribeSecret"  
            ]  
            Resource = aws_secretsmanager_secret.application_secret.arn  
          }  
        ]  
      })  
    }

As AWS Secrets Manager is fully managed by AWS, it integrates seamlessly with IAM to support granular access rules. Here’s how to implement fine-grained access control:

    # Example of a more detailed IAM policy with granular permissions  
    resource "aws_iam_policy" "detailed_secret_access_policy" {  
      name        = "awsmorocco-granular-secret-access"  
      description = "Granular access policy for application secrets"  
      
      policy = jsonencode({  
        Version = "2012-10-17"  
        Statement = [  
          {  
            Sid    = "AllowSecretsRead"  
            Effect = "Allow"  
            Action = [  
              "secretsmanager:GetSecretValue",  
              "secretsmanager:DescribeSecret"  
            ]  
            Resource = [  
              aws_secretsmanager_secret.application_secret.arn  
            ]  
            Condition = {  
              StringEquals = {  
                "aws:PrincipalTag/Environment": ["development", "production"]  
              }  
            }  
          },  
          {  
            Sid    = "AllowSecretsWrite"  
            Effect = "Allow"  
            Action = [  
              "secretsmanager:PutSecretValue",  
              "secretsmanager:UpdateSecret"  
            ]  
            Resource = [  
              aws_secretsmanager_secret.application_secret.arn  
            ]  
            Condition = {  
              StringEquals = {  
                "aws:PrincipalTag/Role": "administrator"  
              }  
            }  
          }  
        ]  
      })  
    }  
      
    # Resource-based policy for cross-account access  
    resource "aws_secretsmanager_secret_policy" "secret_resource_policy" {  
      secret_arn = aws_secretsmanager_secret.application_secret.arn  
      
      policy = jsonencode({  
        Version = "2012-10-17"  
        Statement = [  
          {  
            Sid    = "EnableCrossAccountAccess"  
            Effect = "Allow"  
            Principal = {  
              AWS = "arn:aws:iam::<aws-account-id>:root"  # Replace with actual account ID  
            }  
            Action = [  
              "secretsmanager:GetSecretValue"  
            ]  
            Resource = "*"  
          }  
        ]  
      })  
    }

2 — Secrets Rotation

AWS Secrets Manager supports automatic secret rotation. Here’s how to set it up:

    # Lambda execution role for the rotation function  
    resource "aws_iam_role" "rotation_lambda_role" {  
      name = "awsmorocco-secret-rotation-role"  
      
      assume_role_policy = jsonencode({  
        Version = "2012-10-17"  
        Statement = [  
          {  
            Action = "sts:AssumeRole"  
            Effect = "Allow"  
            Principal = {  
              Service = "lambda.amazonaws.com"  
            }  
          }  
        ]  
      })  
    }  
      
    # Enable rotation for the secret  
    resource "aws_secretsmanager_secret_rotation" "secret_rotation" {  
      secret_id           = aws_secretsmanager_secret.application_secret.id  
      rotation_lambda_arn = "arn:aws:lambda:${data.aws_region.current.name}:${data.aws_caller_identity.current.account_id}:function:awsmorocco-rotation-function"  
      
      rotation_rules {  
        automatically_after_days = 30  
      }  
    }

3 — Accessing Secrets in Terraform

You can access secrets from AWS Secrets Manager in your Terraform using a Data Source:

    # Fetch the entire secret  
    data "aws_secretsmanager_secret_version" "application_secrets" {  
      secret_id = aws_secretsmanager_secret.application_secret.id  
      version_stage = "AWSCURRENT"  
    }  
      
    locals {  
      secrets = jsondecode(data.aws_secretsmanager_secret_version.application_secrets.secret_string)  
    }  
      
    # Use in resources  
    resource "aws_db_instance" "application_db" {  
      identifier          = "awsmorocco-db-2"  
      engine              = "mysql"  
      instance_class      = "db.t3.micro"  
      username            = local.secrets.db_username  
      password            = local.secrets.db_password  
      skip_final_snapshot = true  
    }

4 — Examples — Integration with EC2 and ECS

  • EC2 Integration
    # IAM role for EC2 instances  
    resource "aws_iam_role" "ec2_role" {  
      name = "awsmorocco-ec2-secrets-role"  
      
      assume_role_policy = jsonencode({  
        Version = "2012-10-17"  
        Statement = [  
          {  
            Action = "sts:AssumeRole"  
            Effect = "Allow"  
            Principal = {  
              Service = "ec2.amazonaws.com"  
            }  
          }  
        ]  
      })  
    }  
      
    # Attach secrets access policy to EC2 role  
    resource "aws_iam_role_policy_attachment" "ec2_secret_policy" {  
      policy_arn = aws_iam_policy.secret_access_policy.arn  
      role       = aws_iam_role.ec2_role.name  
    }
  • ECS Integration
    # Task definition with secrets  
    resource "aws_ecs_task_definition" "application" {  
      family                   = "awsmorocco-application"  
      requires_compatibilities = ["FARGATE"]  
      network_mode            = "awsvpc"  
      cpu                     = 256  
      memory                  = 512  
      
      container_definitions = jsonencode([  
        {  
          name  = "application"  
          image = "application:latest"  
          secrets = [  
            {  
              name      = "DB_PASSWORD"  
              valueFrom = "${aws_secretsmanager_secret.application_secret.arn}:db_password::"  
            },  
            {  
              name      = "API_KEY"  
              valueFrom = "${aws_secretsmanager_secret.application_secret.arn}:api_key::"  
            }  
          ]  
        }  
      ])  
    }

5 — AWS Services Integration with Secrets Manager and KMS

AWS Secrets Manager and KMS integrate natively with many AWS services to provide automated password management capabilities. Let’s explore some of these integrations:

5.1 — RDS Password Management Integration

Amazon RDS provides built-in integration with Secrets Manager for managing master user passwords. This integration simplifies password management and enhances security:

    resource "aws_db_instance" "default" {  
      allocated_storage           = 10  
      db_name                    = "awsmorocco-db"  
      engine                     = "mysql"  
      engine_version             = "8.0"  
      instance_class             = "db.t3.micro"  
      manage_master_user_password = true  # Enable Secrets Manager integration  
      username                   = "admin"  
      parameter_group_name       = "default.mysql8.0"  
    }

5.2 — RDS with Custom KMS Key

    resource "aws_kms_key" "database_key" {  
      description = "KMS key for RDS master password encryption"  
      enable_key_rotation = true  
    }  
      
    resource "aws_db_instance" "default" {  
      allocated_storage             = 10  
      db_name                       = "mydb"  
      engine                        = "mysql"  
      engine_version                = "8.0"  
      instance_class                = "db.t3.micro"  
      manage_master_user_password   = true  
      master_user_secret_kms_key_id = aws_kms_key.database_key.key_id  
      username                      = "admin"  
      parameter_group_name          = "default.mysql8.0"  
    }

5.3 — AWS Managed Services Integration

Many AWS services automatically create and manage secrets in Secrets Manager (see more here ) :

  • Amazon RDS (rds)
  • Amazon Redshift (redshift)
  • Amazon ECS (ecs-sc)
  • AWS DataSync (datasync)
  • Amazon AppFlow (appflow)
  • AWS Glue DataBrew (databrew)
  • AWS Direct Connect (directconnect)
  • Amazon EventBridge (events)
  • AWS OpsWorks (opsworks-cm)

5.4 — Managed Secrets Characteristics

Managed secrets provided by AWS services include:

  • Automated creation and lifecycle management
  • Built-in rotation capabilities
  • Protection against accidental modifications
  • Required recovery period for deletions
  • Service-specific access controls

6 — Best Practices

1. Secret Organization
— Use hierarchical naming for secrets (e.g., /env/service/secret)
— Tag secrets for better organization and cost allocation
— Use separate secrets for different environments

2. Access Control
— Implement least-privilege access using IAM policies
— Use resource-based policies for cross-account access
— Regularly audit secret access using CloudTrail

3. Rotation
— Enable automatic rotation for supported secret types
— Implement custom rotation functions for application-specific secrets
— Test rotation procedures in non-production environments

4. Security
— Enable encryption at rest using KMS keys
— Use VPC endpoints for secure access
— Implement proper backup and recovery procedures

6 — Monitoring and Logging

    # CloudWatch Log Group for Secrets Manager  
    resource "aws_cloudwatch_log_group" "secrets_logs" {  
      name              = "/aws/secretsmanager/awsmorocco"  
      retention_in_days = 14  
    }  
      
    # CloudWatch Metric Alarm for failed secret rotations  
    resource "aws_cloudwatch_metric_alarm" "rotation_failures" {  
      alarm_name          = "awsmorocco-secret-rotation-failures"  
      comparison_operator = "GreaterThanThreshold"  
      evaluation_periods  = "1"  
      metric_name         = "RotationFailed"  
      namespace           = "AWS/SecretsManager"  
      period              = "300"  
      statistic           = "Sum"  
      threshold           = "0"  
      alarm_description   = "This metric monitors failed secret rotations"  
        
      dimensions = {  
        SecretId = aws_secretsmanager_secret.application_secret.id  
      }  
    }

Conclusion

AWS Secrets Manager, when properly integrated with Terraform, provides a robust solution for managing application secrets. By following these practices and implementing proper security controls, you can ensure your sensitive information remains secure while being easily accessible to your applications.

The combination of AWS KMS (covered in Part 1) and AWS Secrets Manager gives you a comprehensive secrets management solution that can scale with your infrastructure needs.

ℹ️ Complete Terraform code is available in our GitHub repository :

aws-morocco-samples/secrets-management-in-terraform/terraform at main ·Z4ck404/aws-morocco-samples


Secure Secrets Management in Terraform — Part2: AWS Secret Manager was originally published in AWS Morocco on Medium, where people are continuing the conversation by highlighting and responding to this story.

Related Posts

All you need to know about Terraform provisioners and why you should avoid them.

All you need to know about Terraform provisioners and why you should avoid them.

Photo by Danist Soh on Unsplash As defined in the Terraform documentation, provisioners can be used to model specific actions on the local machine running the Terraform Core or on a remote machine to prepare servers or other infrastructure objects.

Read More
Machine learning on Elastic Search using Apache Spark and ES-Hadoop — Part 2

Machine learning on Elastic Search using Apache Spark and ES-Hadoop — Part 2

In the previous article (Part1), we installed the ELK stack along with the ES-Hadoop connector and spark, then we did some visualizations in Kibana with the houses price prediction data set from kaggle.

Read More
CSI Drivers (EBS, EFS, S3) on EKS And How To Use Them

CSI Drivers (EBS, EFS, S3) on EKS And How To Use Them

Photo by frank mckenna on Unsplash Container Storage Interface (CSI) drivers play a crucial role in managing persistent storage for containerized applications.

Read More