Manage Secrets with AWS Secrets Manager

Khimananda Oli 7 min read Database
Manage Secrets with AWS Secrets Manager

By Khimananda Oli | Last reviewed: August 2026

Hardcoded credentials in source code or environment variables remain a primary vector for data breaches and compliance failures. To manage secrets with AWS Secrets Manager effectively, you must move beyond simple storage and implement automated rotation, strict IAM scoping, and infrastructure-as-code provisioning. This guide provides the exact configuration patterns I use to secure production databases and API keys while maintaining SOC 2 audit readiness.

How do you manage secrets with AWS Secrets Manager using Terraform?

Provisioning secrets manually through the AWS Console is unrepeatable and unauditable. When you adopt Infrastructure as Code with Terraform, you define the secret metadata and permissions declaratively. Crucially, never store the actual secret value in your Terraform state file. Instead, create the secret shell in Terraform and populate the value separately via CI/CD or CLI.

Terraform ApplySecrets Manager(Metadata Only)CI/CD PipelineCreate ShellPutSecretValueKMS Encryption
Secure provisioning flow: Terraform creates the secret structure while CI injects values, keeping sensitive data out of state files.

Terraform configuration for secret metadata

This configuration creates a secret for an RDS database. Note the lifecycle block ignoring value changes to prevent Terraform from overwriting rotated credentials.

resource "aws_secretsmanager_secret" "db_credentials" {
  name        = "prod/myapp/db-credentials"
  description = "Production PostgreSQL credentials for MyApp"
  kms_key_id  = aws_kms_key.secrets_key.arn

  tags = {
    Environment = "production"
    ManagedBy   = "terraform"
    Compliance  = "SOC2"
  }

  lifecycle {
    ignore_changes = [
      # Allow external rotation or CI updates without TF drift
    ]
  }
}

# Separate resource for initial value (optional, prefer CI injection)
resource "aws_secretsmanager_secret_version" "db_credentials_initial" {
  secret_id     = aws_secretsmanager_secret.db_credentials.id
  secret_string = jsonencode({
    username = "app_user"
    password = "INITIAL_PLACEHOLDER_ROTATE_IMMEDIATELY"
    host     = "prod-db.cluster-xyz.us-east-1.rds.amazonaws.com"
    port     = 5432
    dbname   = "myapp_prod"
  })

  lifecycle {
    ignore_changes = [secret_string]
  }
}

Injecting values securely via CLI

After Terraform applies the metadata, inject the real credential from your CI runner or secure workstation. This keeps the plaintext out of version control entirely.

aws secretsmanager put-secret-value \
  --secret-id prod/myapp/db-credentials \
  --secret-string '{"username":"app_user","password":"Str0ng!P@ssw0rd#2026","host":"prod-db.cluster-xyz.us-east-1.rds.amazonaws.com","port":5432,"dbname":"myapp_prod"}' \
  --region us-east-1

How do you configure IAM policies for least-privilege secret access?

Overly permissive IAM is the most common failure when teams implement AWS IAM best practices. Never grant secretsmanager:GetSecretValue on *. Scope access by resource ARN and condition keys. Attach this policy to the application role, not the user.

  • Resource-level permissions: Restrict to exact secret ARNs or prefixes like arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/myapp/*.
  • Condition keys: Use secretsmanager:ResourceTag/Environment to prevent dev roles from reading prod secrets.
  • Deny-by-default: Explicitly deny secretsmanager:DeleteSecret and secretsmanager:PutSecretValue for application roles; only CI/admin roles should write.
  • KMS dependency: Remember that Secrets Manager uses KMS. The role needs kms:Decrypt on the specific key, or GetSecretValue fails silently.
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowReadAppSecrets",
      "Effect": "Allow",
      "Action": [
        "secretsmanager:GetSecretValue",
        "secretsmanager:DescribeSecret"
      ],
      "Resource": "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/myapp/*",
      "Condition": {
        "StringEquals": {
          "secretsmanager:ResourceTag/Environment": "production"
        }
      }
    },
    {
      "Sid": "AllowKMSDecryptForSecrets",
      "Effect": "Allow",
      "Action": "kms:Decrypt",
      "Resource": "arn:aws:kms:us-east-1:123456789012:key/mrk-abc123def456",
      "Condition": {
        "StringEquals": {
          "kms:ViaService": "secretsmanager.us-east-1.amazonaws.com"
        }
      }
    }
  ]
}

How does automatic rotation work for RDS credentials in AWS Secrets Manager?

Static credentials are a liability. Automatic rotation eliminates stale passwords and satisfies compliance controls requiring periodic credential renewal. When you manage secrets with AWS Secrets Manager for RDS, use the managed rotation Lambda rather than writing custom rotation logic.

Secrets ManagerRotation LambdaRDS DatabaseApplication1. CreateSecret2. SetSecret3. TestSecret4. FinishSecretApp fetches new version via GetSecretValue
Four-step rotation cycle: create pending version, update DB, validate connectivity, then promote to current — ensuring zero-downtime credential refresh.

Enabling managed rotation for Aurora PostgreSQL

The managed rotation template handles the create-test-promote cycle automatically. Configure it in Terraform alongside your secret.

resource "aws_secretsmanager_secret_rotation" "db_rotation" {
  secret_id           = aws_secretsmanager_secret.db_credentials.id
  rotation_lambda_arn = aws_lambda_function.rds_rotation.arn

  rotation_rules {
    automatically_after_days = 30
    duration                = "4h"
    schedule_expression     = "rate(30 days)"
  }
}

# Use AWS-managed rotation template
resource "aws_lambda_function" "rds_rotation" {
  function_name = "secrets-manager-rds-rotation"
  runtime       = "python3.12"
  handler       = "main.lambda_handler"
  filename      = "rotation-lambda.zip"
  role          = aws_iam_role.rotation_lambda.arn

  environment {
    variables = {
      SECRETS_MANAGER_ENDPOINT = "https://secretsmanager.us-east-1.amazonaws.com"
    }
  }

  vpc_config {
    subnet_ids         = var.private_subnet_ids
    security_group_ids = [aws_security_group.rotation_lambda.id]
  }
}

A common mistake is forgetting VPC configuration. The rotation Lambda must reside in the same VPC as your RDS instance with network access to the database port. Without this, rotation fails at the TestSecret step.

How do you retrieve secrets in applications without hardcoding?

Applications should fetch secrets at runtime using the AWS SDK, never from environment variables baked into container images. For containerized Laravel applications, use the AWS SDK PHP package or a sidecar pattern to inject secrets at startup.

// PHP/Laravel example: Fetch secret at runtime
use Aws\SecretsManager\SecretsManagerClient;

$client = new SecretsManagerClient([
    'version' => 'latest',
    'region'  => env('AWS_REGION', 'us-east-1'),
]);

$result = $client->getSecretValue([
    'SecretId' => 'prod/myapp/db-credentials',
]);

$credentials = json_decode($result['SecretString'], true);

// Use directly in config, never cache in env
config([
    'database.connections.pgsql.host'     => $credentials['host'],
    'database.connections.pgsql.username' => $credentials['username'],
    'database.connections.pgsql.password' => $credentials['password'],
]);

Caching to reduce costs and latency

Each GetSecretValue call costs $0.05 per 10,000 requests. In high-traffic applications, this adds up. Implement client-side caching with TTLs aligned to your rotation window. The AWS SDK provides built-in caching; for custom implementations, cache for no longer than 5 minutes to balance cost savings against stale credential risk during rotation windows.

AWS Secrets Manager vs Parameter Store vs HashiCorp Vault

Choosing the right tool depends on compliance requirements, scale, and operational overhead. Below is a practical comparison based on production deployments across Nepal-based startups and global enterprises.

CriteriaAWS Secrets ManagerSSM Parameter StoreHashiCorp Vault
Automatic RotationNative for RDS, Redshift, DocumentDBNo native rotationFull dynamic secrets engine
EncryptionKMS mandatory (per-secret or shared)KMS optional (Advanced tier)Transit + storage encryption
Cross-Account AccessNative resource policiesLimited, requires RAMMulti-cluster federation
Cost Model$0.40/secret/month + API callsFree (Standard) / $0.05 (Advanced)Self-hosted or HCP ($$$)
SOC 2 / ISO 27001Audit logs via CloudTrail nativeRequires Advanced tier for loggingFull audit backend, enterprise features
Best ForAWS-native apps needing rotationConfig values, non-sensitive paramsMulti-cloud, dynamic creds, PKI
Need Secret Storage?No rotation neededAWS + rotationMulti-cloud/dynamicParameter StoreConfig & non-sensitiveSecrets ManagerRDS rotation + SOC 2HashiCorp VaultDynamic secrets + PKICompliance Required?Yes → Secrets Manager
Decision framework: choose Parameter Store for config, Secrets Manager for AWS-native rotation and compliance, Vault for multi-cloud dynamic credentials.

Conclusion

To manage secrets with AWS Secrets Manager in production, combine Terraform-provisioned metadata, scoped IAM policies, managed rotation Lambdas, and runtime SDK retrieval with caching. This stack delivers security, compliance, and operational reliability without slowing development velocity. If your team needs help designing an audit-ready secrets architecture or migrating legacy credential stores, reach out to discuss your infrastructure requirements.

Frequently Asked Questions

Each secret costs $0.40 per month in 2026. API calls are $0.05 per 10,000 requests. Automatic rotation adds no extra charge beyond standard API usage fees for retrieval and update operations during the rotation cycle.

Yes, use managed rotation templates for RDS, DocumentDB, and Redshift. These built-in strategies handle credential updates natively without writing or maintaining custom Lambda code, reducing operational overhead significantly compared to self-managed rotation scripts.

Secrets Manager supports automatic rotation and cross-account sharing natively. Parameter Store lacks built-in rotation but offers free standard parameters. Choose Secrets Manager for database credentials requiring rotation; use Parameter Store for static configuration values that rarely change.

Run aws secretsmanager get-secret-value with the secret-id flag. Parse the SecretString field from the JSON response. Use version-stage AWSCURRENT for production values or specify version-id to fetch previous secret versions during rollback scenarios.

Yes, all secrets are encrypted using AWS KMS automatically. You can use the default aws/secretsmanager key or specify a customer-managed KMS key for stricter access control, audit logging via CloudTrail, and compliance requirements needing explicit key ownership.

Attach a resource-based policy to the secret allowing specific external account principals. The receiving account must also have IAM permissions to call GetSecretValue. This avoids duplicating secrets and maintains a single source of truth across environments.

Verify your IAM policy includes secretsmanager:GetSecretValue for the specific secret ARN. Check if a resource-based policy restricts access or if VPC endpoints lack the correct policy. Ensure KMS decrypt permissions exist if using customer-managed keys.

Yes, use SecretBinary instead of SecretString when creating or updating secrets. Encode binary content as base64 before storing via CLI or SDK. Retrieve and decode the base64 payload back to raw bytes when consuming the secret in applications.

Rotation is eventually consistent. New credentials typically become available within seconds, but cached values in application runtimes may persist until refresh intervals expire. Always implement retry logic with exponential backoff when fetching rotated secrets programmatically.

Deletion schedules a mandatory recovery window between 7 and 30 days. During this period, you can restore the secret. After the window expires, permanent deletion occurs. Immediate deletion is not possible to prevent accidental credential loss in production systems.

Use AWS Secrets and Configuration Provider with EKS CSI driver. Mount secrets as volumes or environment variables directly into pods. This avoids storing credentials in ConfigMaps or Helm charts and syncs automatically when underlying secrets rotate.

Yes, configure the AWS Secrets Manager cache client as an environment variable resolver. Map secret names to env keys in .env files. Laravel reads resolved values transparently without changing config files or adding SDK dependencies to your application layer.

Maximum secret size is 65,536 bytes including metadata. For larger payloads, store references to S3 objects encrypted with SSE-KMS instead. Splitting oversized secrets into multiple entries complicates rotation and increases API costs unnecessarily.

Enable CloudTrail data events for Secrets Manager. Filter logs by eventName GetSecretValue and resources.ARN matching your secret. Data events incur additional charges but provide granular visibility required for security investigations and compliance audits in 2026.

Bundle related credentials like username, password, host, and port into a single JSON-formatted secret. This reduces API call volume and simplifies rotation atomicity. Avoid bundling unrelated secrets together as it prevents independent lifecycle management and least-privilege access.