Secrets the Right Way: Secrets Manager & SSM

Inject sensitive values at runtime from Secrets Manager or SSM Parameter Store instead of baking them into environment variables.

If there is one thing that separates a hardened ECS setup from a fragile one, it is how secrets are handled. Plain environment entries feel convenient, but they show up in describe-task-definition, in the console, in CloudTrail, and in anyone's terminal history. Treat them as public.

Use the secrets field

The secrets block maps an environment variable name to a value stored in AWS Secrets Manager or SSM Parameter Store. ECS resolves it at task start and injects it into the container — the actual value never appears in the task definition.

  • Secrets Manager when you want rotation, cross-account sharing, or JSON secrets. You can even reference a single key with arn:...:secret:name:json-key::.
  • SSM Parameter Store (SecureString) when you want something lighter and cheaper for config-like secrets.

The permission that trips everyone up

The execution role — not the task role — is what pulls secrets at launch. If the value is encrypted with a customer-managed KMS key, that role also needs kms:Decrypt. A task stuck in PENDING with a ResourceInitializationError is almost always a missing secrets or KMS permission on the execution role.

Rule I never break: no secret ever lands in environment or a Docker image. If it is sensitive, it comes from secrets at runtime.

Example

Example · json
{
  "containerDefinitions": [
    {
      "name": "api",
      "image": "api:42",
      "essential": true,
      "secrets": [
        {
          "name": "DB_PASSWORD",
          "valueFrom": "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/db-AbCdEf:password::"
        },
        {
          "name": "STRIPE_KEY",
          "valueFrom": "arn:aws:ssm:us-east-1:123456789012:parameter/prod/stripe-key"
        }
      ]
    }
  ]
}

When to use it

  • A team injects database credentials from Secrets Manager at task launch so rotating the secret takes effect on the next deploy with no code change.
  • A security audit requires that no plaintext credentials appear in task definition JSON, so all API keys are stored in SSM Parameter Store SecureString parameters.
  • A developer tests secret injection locally by storing a fake credential in SSM and running a local container that reads it via the AWS SDK.

More examples

Store Secret in Secrets Manager

Creates a Secrets Manager secret containing a JSON object with database credentials that can be injected into ECS tasks.

Example · bash
aws secretsmanager create-secret \
  --name prod/api/db-credentials \
  --secret-string '{"username":"dbuser","password":"s3cr3t"}' \
  --description 'Production API database credentials'

Reference Secret in Container Definition

Uses JSON key selectors to inject individual fields from a single Secrets Manager secret as separate environment variables.

Example · json
{
  "name": "api",
  "image": "my-api:latest",
  "secrets": [
    {
      "name": "DB_USERNAME",
      "valueFrom": "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/api/db-credentials:username::"
    },
    {
      "name": "DB_PASSWORD",
      "valueFrom": "arn:aws:secretsmanager:us-east-1:123456789012:secret:prod/api/db-credentials:password::"
    }
  ]
}

Rotate Secret and Redeploy Service

Rotates the Secrets Manager secret and forces an ECS rolling deployment so all new tasks launch with the fresh credentials.

Example · bash
# Rotate the secret
aws secretsmanager rotate-secret \
  --secret-id prod/api/db-credentials

# Force new tasks to pick up the rotated credentials
aws ecs update-service \
  --cluster prod \
  --service api \
  --force-new-deployment

Discussion

  • Be the first to comment on this lesson.