AWS Security

AWS Secrets Manager vs SSM Parameter Store, In Depth: Secrets, Rotation & Config

Every application needs two things it must not hard-code: secrets (database passwords, API keys, TLS private keys, OAuth client secrets) and configuration (feature flags, endpoint URLs, log levels, queue names). Put either in source code and you have a problem — a credential in a git history is a credential in every fork, every laptop, and every build log forever; a config value baked into an image means a rebuild for every environment. AWS gives you two managed services for this, and the single most common interview question on the topic is which one do I use? — because they overlap, they are priced very differently, and choosing wrong costs either money or capability. AWS Secrets Manager is a purpose-built secrets store with automatic rotation, versioning, and cross-account sharing — it costs per secret per month. AWS Systems Manager Parameter Store is a general-purpose configuration store that also stores encrypted secrets (SecureString), and its standard tier is free.

This lesson is the exhaustive tour of both. We will go through every Secrets Manager feature — the secret types, the four-step automatic rotation model with Lambda, staging labels (AWSCURRENT, AWSPENDING, AWSPREVIOUS), resource policies and cross-account access, multi-Region replication, and the real cost — and every Parameter Store featureString, StringList, and KMS-backed SecureString; standard vs advanced tiers; hierarchies and recursive lookups; versions; and parameter policies (expiration and change notification). Then comes the part that matters most in practice: a clear when-to-use-which comparison table, every retrieval pattern (SDK, CLI, the Lambda extension and caching, the ECS/EKS integrations), how to lock both down with IAM, and how to audit every read and write with CloudTrail. By the end you will never again hard-code a secret, and you will pick the right store every time.

Learning objectives

By the end of this lesson you will be able to:

Prerequisites & where this fits

You need an AWS account, basic comfort with the AWS CLI (aws configure), and a working mental model of IAM (identities, policies, the difference between an identity policy and a resource policy) and AWS KMS (keys and envelope encryption) — both services encrypt with KMS, and access to both is governed by IAM, so the earlier IAM and KMS lessons are the foundation this one builds on. No prior secrets-management background is assumed; every term is defined as it appears. This is a lesson in the Security module of the AWS Zero-to-Hero course, following AWS KMS & Encryption, In Depth (which explains the envelope encryption both services rely on). After this, the course moves on to messaging with AWS Messaging Fundamentals: SQS, SNS & EventBridge. Once you have the fundamentals here, the advanced companion Secrets Manager Rotation at Scale: Custom Rotation Lambdas, RDS Credentials, and Cross-Account Sharing takes rotation and cross-account sharing to production depth.

Core concepts: secrets vs configuration, and the two stores

Before the options, fix two distinctions that the whole lesson hangs on.

Secret vs configuration. A secret is a value whose disclosure causes harm: a password, an API key, a private key, a connection string with a password in it. Configuration is a value that shapes behaviour but is not itself sensitive: a feature flag, a region name, a bucket name, a timeout. The line blurs (a database hostname is config; the password is a secret), and the right answer is often to store the password as a secret and the rest as config. The reason the distinction matters: secrets need rotation, tight auditing, and short blast radius; configuration mostly needs versioning and easy retrieval.

The two stores. AWS gives you two managed key-value stores for these values, both encrypted, both reached by IAM-controlled API calls — never a file on disk, never an environment variable baked into an image.

AWS Secrets Manager SSM Parameter Store
Built for Secrets Configuration (and secrets, via SecureString)
Encryption Always KMS-encrypted Plaintext (String/StringList) or KMS-encrypted (SecureString)
Automatic rotation Yes (native, with Lambda) No (you build it yourself)
Versioning Yes, with staging labels Yes, numeric versions (+ labels on advanced)
Cross-account sharing Yes (resource policy) No native resource policy (share via other means)
Multi-Region Replica secrets Per-Region (no built-in replication)
Cost ~$0.40 per secret/month + $0.05 per 10k API calls Standard tier: free; advanced ~$0.05/param/month
Size limit 64 KB per secret 4 KB (standard) / 8 KB (advanced)
API secretsmanager:* ssm:*Parameter*

The decisive question, in one line: does the value need automatic rotation or cross-account sharing? If yes → Secrets Manager. If it is plain configuration, or a secret you will rotate yourself and you want it free → Parameter Store (SecureString for secrets). We will sharpen this into a full decision table later; keep this mental model as you read the details.

One crucial bridge between them: Parameter Store can reference a Secrets Manager secret. If you read the parameter name /aws/reference/secretsmanager/<secret-name> through the SSM API, Parameter Store fetches the value from Secrets Manager for you. This lets tools that only speak “Parameter Store” (some older integrations, certain IaC patterns) consume Secrets Manager secrets without code changes.

AWS Secrets Manager, in depth

AWS Secrets Manager stores, encrypts, rotates, and serves secrets through an API. Every secret is always encrypted with a KMS key — either the AWS-managed key aws/secretsmanager (free, zero setup) or a customer managed key (CMK) you control (required for cross-account sharing and key separation). You never store a secret in plaintext here; that is the whole point.

What a secret looks like

A secret has a name (often a path-like string such as prod/payments/db), an optional description, a KMS key, optional tags, an optional resource policy, an optional rotation configuration, and one or more versions, each holding a secret value. The secret value is up to 64 KB and is stored as either:

Secret types (what the console offers)

The API stores any string or blob, but the console offers typed wizards that pre-fill the JSON shape and wire up the right rotation Lambda:

Secret type What it stores Rotation support
Amazon RDS credentials username/password (+ host, port, engine, dbname) for an RDS DB Native — AWS provides the rotation Lambda
Amazon Redshift credentials Same shape for a Redshift cluster Native rotation Lambda
Amazon DocumentDB credentials Same shape for DocumentDB Native rotation Lambda
Other database Generic DB credentials (MySQL/PostgreSQL/etc. not on RDS) Native templates for some engines
Credentials for other service Any username/password pair Custom rotation Lambda (you write it)
Other type of secret Free-form key/value or plaintext (API keys, tokens) Custom rotation Lambda (you write it)

The type is essentially a convenience: it determines the JSON template and which rotation Lambda template AWS offers. Under the hood every secret is just a name plus versions of SecretString/SecretBinary.

Versioning and staging labels — the heart of Secrets Manager

This is the concept interviewers probe and the one beginners get wrong, so go slowly. Secrets Manager never overwrites a secret value in place. Every change creates a new version, identified by a VersionId (a UUID). To track which version is the live one, Secrets Manager attaches staging labels — named pointers that move between versions:

Staging label Meaning
AWSCURRENT The current, live version. This is what GetSecretValue returns when you don’t specify a version. Your applications use this.
AWSPENDING A new version being created/tested during rotation, not yet promoted. Only present mid-rotation.
AWSPREVIOUS The immediately prior version (the one AWSCURRENT used to point at). Your rollback safety net.

A label points to exactly one version, and a version can carry multiple labels. Rotation is, mechanically, the act of creating a new version labelled AWSPENDING and then moving AWSCURRENT to it (which automatically demotes the old current to AWSPREVIOUS). Understanding that “rotation = moving the AWSCURRENT label” demystifies the whole rotation flow below.

You can also create your own custom staging labels (e.g. to pin a version), but AWSCURRENT/AWSPENDING/AWSPREVIOUS are reserved and managed by rotation.

Key version facts:

Automatic rotation — the four-step model

Rotation means periodically replacing a secret’s value with a new one and updating the system that uses it, without downtime. Secrets Manager orchestrates this by invoking a rotation Lambda that implements a four-step contract. The Lambda is called once per step, with an event telling it the SecretId, the Token (the VersionId of the AWSPENDING version), and the Step:

Step Name What the Lambda must do
1 createSecret Generate a new secret value (e.g. GetRandomPassword) and store it as a new version labelled AWSPENDING. Idempotent — if AWSPENDING already exists, do nothing.
2 setSecret Apply the pending value to the target service (e.g. ALTER USER … PASSWORD on the database) so the new credential actually works.
3 testSecret Verify the pending value works — connect to the DB with it and run a trivial query. If this fails, rotation aborts and AWSCURRENT is untouched.
4 finishSecret Move the AWSCURRENT label onto the AWSPENDING version (demoting the old one to AWSPREVIOUS). Rotation complete.

The brilliance of this design: until step 4, AWSCURRENT still points at the old, working credential, so applications keep running. Only when the new credential has been proven (step 3) does it become current. If any step throws, the secret stays on the old value and CloudWatch logs the failure.

Single-user vs alternating-users rotation (the classic RDS choice):

Rotation schedule — you set it as a rotation interval in days (e.g. every 30 days) or a cron/rate schedule (e.g. cron(0 8 1 * ? *) for the 1st of each month at 08:00 UTC). You can also choose to rotate immediately on save (runs once now to validate the Lambda) or defer the first rotation to the next window. A rotation window duration bounds how long a rotation may take.

The deep mechanics of writing custom rotators, the master-secret pattern, and alternating-users in full are covered in the advanced companion lesson linked at the end — here you need to know the four-step contract and the two strategies cold, because that is what gets asked.

Resource policies and cross-account access

A resource policy (a JSON policy attached to the secret itself) controls who can access it, complementing the IAM identity policies attached to principals. The headline use is cross-account sharing: to let account B read a secret owned by account A, you need both:

  1. A resource policy on the secret (account A) that Allows the account-B principal secretsmanager:GetSecretValue.
  2. A key policy / grant on the KMS key (account A) that lets the account-B principal kms:Decryptand the secret must use a CMK, not the AWS-managed aws/secretsmanager key, because the managed key cannot be shared cross-account. This is the single most common cross-account “why is it AccessDenied?” cause.

You can also use a resource policy within one account to deny everything except a specific role (a belt-and-braces guardrail), or to block access (an explicit Deny). Use aws secretsmanager validate-resource-policy to catch broad/public policies before applying them.

Replication (multi-Region secrets)

Secrets Manager can replicate a secret to other Regions. You designate a primary secret and add replica Regions; Secrets Manager keeps the replicas in sync (value and metadata) and re-encrypts each replica with a KMS key in that Region (you choose which). Why it matters:

Replicas are billed as separate secrets. You cannot replicate into a Region that already has a secret of the same name (resolve the conflict first).

Other Secrets Manager features

Secrets Manager cost

The pricing model is the reason people reach for Parameter Store instead: ~$0.40 per secret per month (prorated) plus $0.05 per 10,000 API calls. A replica counts as another secret. Ten thousand secrets is ~$4,000/month before API calls — so don’t store configuration here, and cache reads (the Lambda extension does this for you) so you aren’t billed per request in a hot loop.

AWS Systems Manager Parameter Store, in depth

Parameter Store (part of AWS Systems Manager) is a hierarchical key-value store for configuration data and secrets. Its standard tier is free, which makes it the default home for the dozens of non-rotating config values and many secrets a typical application needs. A parameter has a name (a path like /myapp/prod/db/host), a type, a value, a tier, an optional description, a KMS key (for SecureString), optional policies, and numeric versions.

Parameter types

Type What it stores Encryption Notes
String A single plaintext string None For non-sensitive config: hostnames, flags, URLs, ARNs.
StringList A comma-separated list None Returned as a comma-separated string; split client-side. For lists of subnets, AZs, etc.
SecureString A single string encrypted with KMS KMS For secrets: passwords, API keys. Choose the AWS-managed aws/ssm key or a CMK.

A few important subtleties:

Standard vs advanced tier

Each parameter is standard or advanced, and the tier changes limits, cost, and capabilities:

Standard Advanced
Value size up to 4 KB up to 8 KB
Parameters per account/Region 10,000 100,000
Parameter policies (expire, notify) No Yes
Higher-throughput limit option No Yes (with throughput setting)
Cost Free ~$0.05 per parameter/month + per-API charges for higher throughput

You set the tier explicitly, or use --tier Intelligent-Tiering, which automatically promotes a parameter to advanced only if it needs to (value > 4 KB or a policy is attached) — so you pay for advanced only when required. Default to standard; let intelligent tiering handle the exceptions.

Hierarchies and paths

Parameter names are paths separated by /, which lets you organise configuration as a tree and fetch whole subtrees in one call:

/myapp/prod/db/host
/myapp/prod/db/port
/myapp/prod/db/password      (SecureString)
/myapp/staging/db/host

The power move is get-parameters-by-path with --recursive, which returns every parameter under a prefix — so one call fetches all of /myapp/prod/ for an environment. This is the idiomatic way to load configuration: name parameters by /<app>/<env>/<group>/<key> and pull the env subtree at startup. Paths also make IAM scoping clean — grant ssm:GetParametersByPath on arn:aws:ssm:…:parameter/myapp/prod/* and a role can read exactly its environment and nothing else.

Versions, labels, and history

Parameter policies (advanced tier only)

Advanced parameters support policies — JSON rules that automate lifecycle, attached at creation or update:

These policies are the closest Parameter Store gets to Secrets Manager’s lifecycle automation — they notify, but they do not rotate; you still build the rotation yourself (e.g. an EventBridge rule triggering a Lambda).

Parameter Store cost

Standard tier is free for storage and standard-throughput API calls — which is the whole appeal. Advanced parameters are ~$0.05 per parameter per month, and high-throughput API interactions are billed per call when you opt into the higher-throughput setting. For the vast majority of configuration, Parameter Store costs nothing.

When to use which: the decision table

This is the question, so here is the clear answer. Both can store a secret; the differences decide it.

Need Use Secrets Manager Use Parameter Store
Automatic rotation (RDS, Redshift, custom) ✅ Native, built-in ❌ Build it yourself (EventBridge + Lambda)
Cross-account sharing of the value ✅ Resource policy + CMK ❌ No native resource policy
Multi-Region replication of the value ✅ Replica secrets ❌ Per-Region only
Plain configuration (flags, URLs, ARNs) ⚠️ Works but wasteful ($/secret) ✅ Free String/StringList
Secrets, but free and you’ll rotate yourself ⚠️ Costs $0.40/secret/mo SecureString (free, standard tier)
Large value (up to 64 KB) ✅ 64 KB ❌ 4 KB / 8 KB only
Built-in random-password generation GetRandomPassword + rotation ❌ Generate it yourself
Hierarchical config, fetch a subtree at once ⚠️ No path-tree fetch get-parameters-by-path --recursive
Tightest cost at thousands of values ❌ $0.40 each adds up fast ✅ Standard tier free

The rules I actually apply:

Retrieval patterns: getting secrets into your app safely

Storing a secret is half the job; reading it without leaking it (into logs, env vars, or images) is the other half. Here is every common pattern.

AWS CLI

# Secrets Manager — returns the JSON value of AWSCURRENT
aws secretsmanager get-secret-value --secret-id prod/payments/db \
  --query SecretString --output text

# A specific stage (e.g. roll back to previous)
aws secretsmanager get-secret-value --secret-id prod/payments/db \
  --version-stage AWSPREVIOUS --query SecretString --output text

# Parameter Store — a SecureString MUST pass --with-decryption
aws ssm get-parameter --name /myapp/prod/db/password \
  --with-decryption --query Parameter.Value --output text

# Fetch a whole environment subtree in one call
aws ssm get-parameters-by-path --path /myapp/prod/ --recursive \
  --with-decryption --query 'Parameters[].[Name,Value]' --output text

SDK (application code)

In application code you call the same APIs through the SDK and hold the value in memory only — never write it to disk or an env var. Pseudocode (any language SDK):

client = SecretsManager()
resp   = client.get_secret_value(SecretId="prod/payments/db")
creds  = json.parse(resp.SecretString)   # {username, password, host, port}
db.connect(creds.host, creds.username, creds.password)
# cache `creds` in memory; refresh on a timer or on auth failure

The single most important rule: cache the value in memory and reuse it, refreshing periodically (or when a DB connection fails auth, which signals a rotation). Calling GetSecretValue on every request is both slow and, on Secrets Manager, billed per call.

The Lambda extension (caching) — the recommended Lambda pattern

For AWS Lambda, AWS publishes the AWS Parameters and Secrets Lambda Extension (a layer). It runs a tiny local HTTP cache alongside your function; your code fetches from http://localhost:2773/… instead of calling the AWS API directly. Benefits:

# Inside a Lambda with the extension layer attached:
# Secrets Manager
curl "http://localhost:2773/secretsmanager/get?secretId=prod/payments/db" \
  -H "X-Aws-Parameters-Secrets-Token: $AWS_SESSION_TOKEN"
# Parameter Store (SecureString decrypted)
curl "http://localhost:2773/systemsmanager/parameters/get?name=/myapp/prod/db/password&withDecryption=true" \
  -H "X-Aws-Parameters-Secrets-Token: $AWS_SESSION_TOKEN"

This is the default I recommend for Lambda — it removes both the cost and the cold-start latency of naïve per-invocation fetches. (For non-Lambda apps, AWS also ships language-specific caching libraries, e.g. the AWS Secrets Manager caching client.)

ECS and EKS integration (inject at startup, no code change)

A note on the env-var pattern: injecting a secret as an environment variable is convenient but the value is then visible to anything that can read the process environment (and may surface in crash dumps). Mounting as a file (EKS CSI) or fetching via the extension is tighter; choose based on your threat model.

CloudFormation / IaC dynamic references

Infrastructure-as-code can pull a secret at deploy time without putting it in the template, using dynamic references:

'{{resolve:secretsmanager:prod/payments/db:SecretString:password}}'
'{{resolve:ssm-secure:/myapp/prod/db/password}}'
'{{resolve:ssm:/myapp/prod/db/host}}'

Use these so the value never lands in the template, parameters file, or state — only the reference does.

IAM access control

Access to both services is IAM-governed — by default no principal can read a secret or parameter; you grant least privilege explicitly.

Secrets Manager — scope to specific secret ARNs (or by tag), and remember the KMS half:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ReadPaymentsDbSecret",
      "Effect": "Allow",
      "Action": ["secretsmanager:GetSecretValue", "secretsmanager:DescribeSecret"],
      "Resource": "arn:aws:secretsmanager:eu-west-2:111122223333:secret:prod/payments/db-*"
    },
    {
      "Sid": "DecryptWithCmk",
      "Effect": "Allow",
      "Action": "kms:Decrypt",
      "Resource": "arn:aws:kms:eu-west-2:111122223333:key/<cmk-id>"
    }
  ]
}

Two gotchas: secret ARNs end in a random 6-character suffix, so use a trailing -* (or -??????) when you write the ARN by name; and if the secret uses a CMK, the principal needs kms:Decrypt on that key (the AWS-managed key grants this implicitly within the account).

Parameter Store — scope by path prefix, and include KMS for SecureString:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["ssm:GetParameter", "ssm:GetParameters", "ssm:GetParametersByPath"],
      "Resource": "arn:aws:ssm:eu-west-2:111122223333:parameter/myapp/prod/*"
    },
    {
      "Effect": "Allow",
      "Action": "kms:Decrypt",
      "Resource": "arn:aws:kms:eu-west-2:111122223333:key/<cmk-id>"
    }
  ]
}

Path-based scoping is the big win for Parameter Store: one statement on /myapp/prod/* grants exactly the prod environment. For both services, prefer tag-based (ABAC) policies at scale so you don’t maintain ARN lists, and grant write actions (PutSecretValue, PutParameter) only to deployment roles, never to running apps.

Auditing with CloudTrail

Every API call to both services is recorded by AWS CloudTrail — this is how you answer “who read this secret, and when?” in an audit or incident.

AWS Secrets Manager vs Parameter Store

The diagram contrasts the two stores side by side — Secrets Manager with its staging-label versioning and four-step rotation Lambda on the left, SSM Parameter Store with its String/SecureString hierarchy and tiers on the right — and shows the shared retrieval paths (SDK, CLI, the Lambda extension cache, ECS/EKS injection) and the KMS-plus-CloudTrail layer that both rely on for encryption and auditing.

Hands-on lab: store, retrieve, and rotate a secret

This lab uses the AWS CLI and is effectively free: Parameter Store standard tier is free, and you will create a single Secrets Manager secret and delete it immediately (cost is prorated to fractions of a cent). You will store config in Parameter Store, store a secret in Secrets Manager, retrieve both correctly, enable rotation, and clean up. Use a non-production account/Region (examples use eu-west-2).

Prerequisites: the CLI configured (aws configure) with permissions for ssm:*Parameter*, secretsmanager:*, and kms:* on the AWS-managed keys.

Part A — configuration in Parameter Store

# Plain String config (free, not encrypted)
aws ssm put-parameter --name /lab/app/db/host \
  --type String --value "db.internal.example.com"
aws ssm put-parameter --name /lab/app/db/port \
  --type String --value "5432"

# A SecureString secret (KMS-encrypted with the aws/ssm managed key)
aws ssm put-parameter --name /lab/app/db/password \
  --type SecureString --value "S3cr3t-do-not-commit"

# Read the whole environment subtree in ONE call, decrypting secrets
aws ssm get-parameters-by-path --path /lab/app/ --recursive \
  --with-decryption \
  --query 'Parameters[].[Name,Value]' --output table
# validation: shows host, port, and the DECRYPTED password

# Prove --with-decryption is required: omit it and the password is ciphertext
aws ssm get-parameter --name /lab/app/db/password \
  --query Parameter.Value --output text
# -> AQICAHh... (ciphertext, because we did NOT pass --with-decryption)

Part B — a secret in Secrets Manager

# Create a JSON secret (the shape rotation Lambdas expect)
aws secretsmanager create-secret --name lab/app/api \
  --description "Lab third-party API credentials" \
  --secret-string '{"api_key":"AKIA-EXAMPLE","api_secret":"shhh"}'
# note the returned ARN (ends in a random 6-char suffix)

# Retrieve the current value (AWSCURRENT)
aws secretsmanager get-secret-value --secret-id lab/app/api \
  --query SecretString --output text
# -> {"api_key":"AKIA-EXAMPLE","api_secret":"shhh"}

# Update it -> creates a NEW version, moves AWSCURRENT, old -> AWSPREVIOUS
aws secretsmanager put-secret-value --secret-id lab/app/api \
  --secret-string '{"api_key":"AKIA-EXAMPLE-2","api_secret":"shhh2"}'

# Roll back by reading the PREVIOUS stage
aws secretsmanager get-secret-value --secret-id lab/app/api \
  --version-stage AWSPREVIOUS --query SecretString --output text
# -> the original value (validation that staging labels moved)

# Inspect the version-to-label mapping
aws secretsmanager describe-secret --secret-id lab/app/api \
  --query VersionIdsToStages
# -> shows which VersionId carries AWSCURRENT vs AWSPREVIOUS

Part C — generate a strong password (the rotation helper)

aws secretsmanager get-random-password \
  --password-length 24 --exclude-punctuation \
  --query RandomPassword --output text
# validation: a 24-char random password (what rotation step 1 uses)

Part D — the cross-service bridge

# Read the Secrets Manager secret THROUGH the Parameter Store API
aws ssm get-parameter \
  --name /aws/reference/secretsmanager/lab/app/api \
  --with-decryption --query Parameter.Value --output text
# -> the same JSON value, fetched via the SSM API (no Secrets Manager call in your code)

Cleanup (avoid charges)

# Parameter Store (free, but tidy up)
aws ssm delete-parameters --names \
  /lab/app/db/host /lab/app/db/port /lab/app/db/password

# Secrets Manager — force-delete now (skip the recovery window) since this is a lab
aws secretsmanager delete-secret --secret-id lab/app/api \
  --force-delete-without-recovery

Cost note: Parameter Store standard tier is free. The one Secrets Manager secret is billed at ~$0.40/month prorated — held for a few minutes it is a fraction of a cent, and --force-delete-without-recovery stops billing immediately. In production you would omit --force-delete-without-recovery and let the 7–30 day recovery window protect you. The real-world cost levers are: number of secrets in Secrets Manager (store config in Parameter Store instead), API call volume (cache aggressively — use the Lambda extension), replica secrets (each replica is billed), and advanced-tier parameters (use Intelligent-Tiering so you only pay when a parameter actually needs advanced).

Common mistakes & troubleshooting

Symptom Likely cause Fix
get-parameter returns ciphertext (AQICAH…) not the value Forgot --with-decryption on a SecureString Pass --with-decryption (and ensure the role has kms:Decrypt)
AccessDeniedException reading a secret despite a Secrets Manager Allow The secret uses a CMK and the principal lacks kms:Decrypt on it Add kms:Decrypt for the key (or use the AWS-managed key, which grants it in-account)
Cross-account read is AccessDenied Only the IAM policy was set, not the resource policy, or the secret uses the managed KMS key Add a resource policy on the secret and use a CMK with cross-account kms:Decrypt
ARN-scoped IAM policy never matches the secret Secret ARNs end in a random 6-char suffix; the policy used the bare name End the resource with -* (e.g. …:secret:prod/db-*)
App breaks right after a rotation App cached the old password forever / no retry on auth failure Cache with a TTL and refresh on auth failure; prefer the Lambda extension’s caching
Rotation Lambda fails at setSecret/testSecret Lambda can’t reach the DB (no VPC config, SG, or it lacks the master/superuser secret) Put the Lambda in the DB’s VPC/subnets, open the SG, grant it the master secret
put-parameter fails with “already exists” Overwrite is not implicit Add --overwrite (only deployment roles should have PutParameter)
Secrets Manager bill is unexpectedly high Per-call billing from fetching on every request, or many config values stored as secrets Cache reads (Lambda extension), move plain config to Parameter Store
Parameter rejected: value too large String/standard exceeds 4 KB Use the advanced tier (8 KB) via --tier Advanced/Intelligent-Tiering, or split the value

Best practices

Security notes

Interview & exam questions

  1. When do you use Secrets Manager vs SSM Parameter Store? Secrets Manager when you need automatic rotation, cross-account sharing, multi-Region replication, large (≤64 KB) values, or built-in password generation. Parameter Store for plain configuration and for secrets you’ll rotate yourself or that don’t rotate — because its standard tier is free and it has hierarchical paths. Both can store secrets; rotation and sharing are what tip the choice to Secrets Manager.

  2. Explain Secrets Manager staging labels. AWSCURRENT points to the live version (what GetSecretValue returns by default); AWSPENDING is a new version created during rotation but not yet promoted; AWSPREVIOUS is the prior version (rollback). Rotation is literally moving AWSCURRENT onto the tested AWSPENDING version.

  3. Walk through the four-step rotation model. createSecret (generate a new value, store as AWSPENDING), setSecret (apply it to the target service), testSecret (verify it works), finishSecret (move AWSCURRENT to the pending version). Until step 4, AWSCURRENT still points to the working credential, so there’s no downtime.

  4. Single-user vs alternating-users rotation — when each? Single-user changes the one user’s password (simplest, brief reconnection window). Alternating-users keeps two users and flips between them so the old credential stays valid until clients move (zero-downtime, needs a master secret). Use alternating-users for databases that can’t tolerate any auth blip.

  5. How do you share a secret with another AWS account? Attach a resource policy on the secret allowing the other account’s principal GetSecretValue, and use a customer managed KMS key with a key policy/grant allowing that principal kms:Decrypt (the AWS-managed aws/secretsmanager key can’t be shared cross-account).

  6. What’s the difference between String, StringList, and SecureString? String is one plaintext value; StringList is a comma-separated list (plaintext); SecureString is a single value encrypted with KMS. Reading a SecureString requires --with-decryption and kms:Decrypt.

  7. Standard vs advanced Parameter Store tier? Standard: up to 4 KB, 10,000 params/Region, free, no policies. Advanced: up to 8 KB, 100,000 params, ~$0.05/param/month, supports parameter policies and a higher-throughput option. Intelligent-Tiering auto-promotes only when needed.

  8. How do you fetch all of an environment’s configuration in one call? Name parameters under a path (/app/prod/...) and call get-parameters-by-path --recursive --with-decryption on the prefix — one call returns the whole subtree.

  9. What’s the recommended way to read secrets in a Lambda? The AWS Parameters and Secrets Lambda Extension — a layer running a local HTTP cache that serves both Secrets Manager and Parameter Store, caching across warm invocations to cut API cost and latency, with a configurable TTL so values refresh after rotation.

  10. How do ECS and EKS get secrets without code changes? ECS injects them as environment variables via the task definition secrets/valueFrom (resolved by the task execution role at start). EKS uses the Secrets Store CSI Driver with the AWS provider (ASCP) to mount secrets as files, governed by IRSA/Pod Identity.

  11. You’re getting AccessDenied reading a secret even though the IAM policy allows GetSecretValue. Why? The secret is encrypted with a CMK and the principal lacks kms:Decrypt on that key — both permissions are required. (Also check the ARN’s random suffix and, for cross-account, the resource policy.)

  12. How do you audit who read a secret? CloudTrail records GetSecretValue/GetParameter as management events (principal, time, IP, ARN), and the KMS Decrypt call is logged separately — two independent trails. Send CloudTrail to CloudWatch and alarm on unexpected reads or deletes.

  13. What happens when you delete a secret? By default deletion is scheduled after a 7–30 day recovery window (default 30), during which restore-secret undoes it; --force-delete-without-recovery deletes immediately (and stops billing). Parameter Store deletes are immediate (no recovery window).

  14. How can Parameter Store consume a Secrets Manager secret? Read the parameter name /aws/reference/secretsmanager/<secret-name> through the SSM API — Parameter Store fetches the value from Secrets Manager — so tools that only speak Parameter Store can use Secrets Manager secrets unchanged.

Quick check

  1. Which staging label does GetSecretValue return when you don’t specify a version?
  2. Name the four rotation steps in order.
  3. What must you pass to get-parameter to read a SecureString value (not ciphertext)?
  4. Which Parameter Store tier is free, and what is its per-parameter size limit?
  5. What two permissions does a principal need to read a CMK-encrypted secret?

Answers

  1. AWSCURRENT — the current, live version.
  2. createSecret → setSecret → testSecret → finishSecret.
  3. --with-decryption (and the role needs kms:Decrypt on the key).
  4. The standard tier is free, with a 4 KB per-parameter value limit (advanced is 8 KB and billed per parameter).
  5. secretsmanager:GetSecretValue on the secret and kms:Decrypt on the customer managed key.

Exercise

Take an application you know that currently keeps a database password and a few config values in environment variables or a checked-in .env file. Produce a one-page secrets-and-config design that: (a) puts the non-sensitive config (host, port, flags) in Parameter Store under a /<app>/<env>/... hierarchy and lists the single get-parameters-by-path call the app makes at startup; (b) decides where the database password lives — Secrets Manager with automatic rotation (state single- or alternating-users and the schedule) or Parameter Store SecureString if it won’t auto-rotate — with a one-line justification; © writes the least-privilege IAM policy the running app needs (path/ARN scope plus the KMS Decrypt); (d) chooses the retrieval mechanism (Lambda extension, ECS secrets injection, or EKS CSI) for the app’s runtime; and (e) names the CloudTrail alarm you’d set to detect an unexpected read. Keep it to choices and one-line justifications — the goal is to practise the decision framework end to end.

Certification mapping

This lesson maps to the security and developer domains of the AWS certification path. For AWS Certified Security – Specialty (SCS-C02), expect questions on choosing Secrets Manager vs Parameter Store, the rotation model and staging labels, cross-account secret sharing (resource policy plus CMK kms:Decrypt), and auditing reads via CloudTrail and KMS — the “both permissions are required” and “managed key can’t be shared cross-account” points are near-guaranteed. For AWS Certified Developer – Associate (DVA-C02), the emphasis is on retrieving secrets/parameters in code, the Lambda extension caching, ECS secrets/valueFrom injection, dynamic references in CloudFormation, and SecureString with --with-decryption. For Solutions Architect – Associate (SAA-C03), expect the high-level “which service” decision and the multi-Region replica concept. Across all of them, the cost contrast (Parameter Store standard free vs Secrets Manager per-secret) is a recurring distractor-buster.

Glossary

Next steps

You now know both stores end to end — what each is for, every option they expose, how to retrieve values safely, and how to lock them down and audit them. The natural next move is to take rotation and cross-account sharing to production depth: read Secrets Manager Rotation at Scale: Custom Rotation Lambdas, RDS Credentials, and Cross-Account Sharing, which implements the four-step rotation Lambda in full, contrasts single- and alternating-user RDS strategies, shows how to write a custom rotator for third-party credentials, and covers cross-account sharing via resource policies and KMS grants. After that, the course continues with decoupling and messaging in AWS Messaging Fundamentals: SQS, SNS & EventBridge — When to Use Which, where the IAM and resource-policy patterns you learned here reappear on queues and topics.

AWSSecrets ManagerParameter StoreSSMSecurityRotation
Need this built for real?

Vinod is a Senior Cloud Architect (22+ yrs) — available for Azure / AWS / GCP architecture, landing zones, and migrations.

Work with me

Comments

Keep Reading