AWS SSM Parameter Store

5 min read index source

AWS SSM Parameter Store

Hierarchical config + secret storage. Part of AWS Systems Manager. The cheaper, simpler sibling of Secrets Manager — and “which one do I use?” is a guaranteed interview question.

Parameter types

Type Encrypted? Use for
String no plain config — feature flags, URLs, log levels
StringList no comma-separated lists
SecureString yes (KMS) secrets — passwords, API keys, tokens

SecureString encrypts the value with KMS (default aws/ssm key or a customer-managed key). Reading it requires both ssm:GetParameter and kms:Decrypt on the key.

Standard vs Advanced tier

Standard Advanced
Cost free ~$0.05 per parameter / month
Max value size 4 KB 8 KB
Parameters per account 10,000 100,000
Parameter policies (expiration, notifications) no yes
Higher throughput shared optional higher-throughput setting (paid)

Default to Standard. Go Advanced only when you need >4 KB values, >10k parameters, or parameter policies (e.g., “expire this param in 30 days”, “notify if not changed in 90 days”).

Hierarchical organization

Parameters are paths. Organize by environment / service / key:

/myapp/prod/database/url
/myapp/prod/database/password      (SecureString)
/myapp/prod/feature-flags/new-checkout
/myapp/staging/database/url

GetParametersByPath fetches a whole subtree in one call — load all of /myapp/prod/ at startup:

import boto3
ssm = boto3.client("ssm")

resp = ssm.get_parameters_by_path(
    Path="/myapp/prod/",
    Recursive=True,
    WithDecryption=True,   # decrypt SecureStrings
)
config = {p["Name"]: p["Value"] for p in resp["Parameters"]}
# paginate if > 10 params — use the paginator

IAM can be scoped to a path prefix — prod role can read /myapp/prod/*, staging role only /myapp/staging/*.

Parameter Store vs Secrets Manager

The decision matrix:

Parameter Store Secrets Manager
Cost free (Standard tier) ~$0.40 per secret / month + per-API-call
Automatic rotation no (you’d build it with EventBridge + Lambda) yes — built-in, RDS-native rotation, custom Lambda rotation
Max value size 4 KB (8 KB Advanced) 64 KB
Cross-region replication no (manual) yes (built-in)
Versioning yes (numeric versions) yes (version stages: AWSCURRENT/PENDING/PREVIOUS)
Resource policies (cross-account) no yes
Generates random secrets no yes (get_random_password)
Hierarchical paths yes no (flat names, slashes are just naming)
Plain (non-secret) config yes — this is its sweet spot overkill

Decision shortcut:

  • Plain config (URLs, flags, tuning params) → Parameter Store String. Always. Secrets Manager would be wasteful.
  • Secrets that don’t need rotation (third-party API keys you set once) → Parameter Store SecureString — free, encrypted, fine.
  • Secrets that need automatic rotation (DB passwords, especially RDS) → Secrets Manager — the built-in rotation is the whole value proposition.
  • Cross-account secret sharing, >4 KB secrets, version-staged rotation testing → Secrets Manager.

A common pattern: most config + static secrets in Parameter Store; only the rotation-critical credentials (RDS password) in Secrets Manager. You pay $0.40/mo per rotating secret instead of per every config value.

Note: Secrets Manager can reference Parameter Store and vice-versa in some integrations, but treat them as separate stores with the decision above.

Reading parameters in an app

import boto3
ssm = boto3.client("ssm")

# Single parameter
db_url = ssm.get_parameter(Name="/myapp/prod/database/url")["Parameter"]["Value"]

# Single SecureString — decrypt it
db_pw = ssm.get_parameter(
    Name="/myapp/prod/database/password", WithDecryption=True
)["Parameter"]["Value"]

# Many at once
resp = ssm.get_parameters(
    Names=["/myapp/prod/database/url", "/myapp/prod/database/password"],
    WithDecryption=True,
)

Don’t call get_parameter on every request. Like Secrets Manager, fetch once at startup / cold start and cache. For Lambda, the AWS Parameters and Secrets Lambda Extension provides a local cache + HTTP endpoint so you avoid hitting the SSM API on every invocation:

GET http://localhost:2773/systemsmanager/parameters/get?name=/myapp/prod/database/url

Versioning

Every update creates a new numeric version. You can read a specific version (Name:3) or use parameter labels for named versions. Useful for “roll back this config value” without keeping a separate history.

EnvVar injection in ECS / Lambda

Both ECS task definitions and Lambda can pull Parameter Store values at launch and inject them as environment variables — no SDK call in your code:

// ECS task definition
"secrets": [
  {"name": "DATABASE_URL", "valueFrom": "arn:aws:ssm:us-east-1:123:parameter/myapp/prod/database/url"},
  {"name": "DB_PASSWORD", "valueFrom": "arn:aws:ssm:us-east-1:123:parameter/myapp/prod/database/password"}
]

ECS resolves these at task start (the execution role needs ssm:GetParameters + kms:Decrypt). The container sees plain env vars. Same idea works with Secrets Manager ARNs.

Common gotchas

  • Calling get_parameter per request — API rate limits + latency. Cache at startup; use the Lambda extension.
  • Forgetting WithDecryption=TrueSecureString returns the ciphertext, not the value.
  • Missing kms:Decrypt — the role needs both ssm:GetParameter and kms:Decrypt on the encryption key.
  • Standard-tier throughput — shared account-wide throughput; a chatty app can throttle. Cache, or enable the Advanced higher-throughput setting.
  • Using Secrets Manager for plain config — wasteful at $0.40/secret/mo. Parameter Store String is free.
  • Using Parameter Store for rotating DB credentials — you’d have to build the rotation yourself; Secrets Manager does it natively. Use the right tool.
  • Path-based IAM mistakesarn:.../parameter/myapp/prod/* scopes correctly; forgetting the leading / in the parameter name breaks the ARN match.

Interview angle

  • “Parameter Store vs Secrets Manager — when each?” — Parameter Store for plain config (free) and static secrets (SecureString, free, encrypted). Secrets Manager when you need automatic rotation (especially RDS-native), cross-account sharing, >4 KB values, or version-staged rotation. Common pattern: config + static secrets in Parameter Store, only rotation-critical credentials in Secrets Manager.
  • “How do you give a service its config without baking it into the image?” — store under a path like /myapp/prod/*; either inject into env vars via the ECS task definition / Lambda config at launch, or fetch with get_parameters_by_path at startup. IAM scoped to the path prefix.
  • “SecureString — how does it work?” — value encrypted with KMS. WithDecryption=True on read returns the plaintext; the caller needs ssm:GetParameter and kms:Decrypt on the key.
  • “How do you avoid hitting the SSM API on every request?” — fetch + cache at startup/cold start; for Lambda use the AWS Parameters and Secrets Lambda Extension (local cache + localhost HTTP endpoint).
  • “Standard vs Advanced tier?” — Standard is free, 4 KB values, 10k params. Advanced is paid, 8 KB values, 100k params, plus parameter policies (expiration, change notifications). Default to Standard.