Ravindra BagaleCourses & study guides मराठी Track your progress

AWS SDK Secrets Manager and SSM Parameter Store: Read Config Safely (Python, PHP, Node.js)

Passwords and API keys do not belong in code or .env files in git. We store a database password in Secrets Manager and app settings in SSM Parameter Store, read them from Python, PHP and Node.js, rotate a secret, read a whole path of parameters and cache values so you do not call AWS on every request. The example: a payments app for Lucas in São Paulo and Sneha in Chennai.

What you'll learn in this chapter

  • Create and read a secret
  • Update and rotate
  • Store SecureString parameters
  • Read parameters by path
  • Cache values safely

1. The big picture: the hotel safe

Why do we need this?

You do not tape your passport to the hotel door. You lock it in the room safe and open it when needed. Secrets Manager is the safe for passwords; Parameter Store is the notice board for settings, with a locked section for sensitive ones.

What is it?

GetSecretValue returns SecretString. GetParameter with WithDecryption reads a SecureString. Both are encrypted with KMS; IAM decides who can read.

How to do it (step by step)

From safe to appCreateSecret / PutParameterIAM allows the app role to readApp reads at start-upCache for a few minutesRotate without redeploy
Secrets Manager costs per secret; Parameter Store standard parameters are free.

2. Create and read a secret

Why do we need this?

Lucas needs the Postgres password for the Brazil database without anyone seeing it in code.

What is it?

Store JSON in SecretString so one secret holds username, password, host. Read it and parse the JSON.

How to do it (step by step)

  1. Python: create the secret.
import boto3, json
sm = boto3.client("secretsmanager", region_name="sa-east-1")
r = sm.create_secret(Name="payments/db",
    SecretString=json.dumps({"user": "pay_app", "password": "Xy7!q2-Lm9", "host": "db.internal", "port": 5432}))
print(r["ARN"])

Expected output:

arn:aws:secretsmanager:sa-east-1:123456789012:secret:payments/db-AbCdEf
  1. Node.js: read and parse it.
import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager";
const sm = new SecretsManagerClient({ region: "sa-east-1" });
const { SecretString } = await sm.send(new GetSecretValueCommand({ SecretId: "payments/db" }));
const db = JSON.parse(SecretString);
console.log(db.user, db.host, db.port);

Expected output:

pay_app db.internal 5432
  1. PHP: read it and build a PDO DSN.
<?php
require "vendor/autoload.php";
$sm = new Aws\SecretsManager\SecretsManagerClient(["region" => "sa-east-1"]);
$db = json_decode($sm->getSecretValue(["SecretId" => "payments/db"])["SecretString"], true);
$dsn = "pgsql:host={$db["host"]};port={$db["port"]};dbname=payments";
echo $dsn, PHP_EOL;

Expected output:

pgsql:host=db.internal;port=5432;dbname=payments
  1. Verify: describe the secret (never prints the value).
aws secretsmanager describe-secret --secret-id payments/db --region sa-east-1 --query "[Name,LastChangedDate]" --output text

Expected output:

payments/db  2026-10-24T12:10:00+05:30

3. Update and rotate

Why do we need this?

The security team in Chennai rotates every database password each 30 days.

What is it?

PutSecretValue writes a new version labelled AWSCURRENT; the old one becomes AWSPREVIOUS. RotateSecret runs a rotation Lambda on a schedule.

How to do it (step by step)

  1. Python: generate and store a new password.
pw = sm.get_random_password(PasswordLength=24, ExcludePunctuation=True)["RandomPassword"]
cur = json.loads(sm.get_secret_value(SecretId="payments/db")["SecretString"])
cur["password"] = pw
v = sm.put_secret_value(SecretId="payments/db", SecretString=json.dumps(cur))
print(v["VersionStages"])

Expected output:

['AWSCURRENT']
  1. Node.js: turn on automatic rotation.
import { RotateSecretCommand } from "@aws-sdk/client-secrets-manager";
await sm.send(new RotateSecretCommand({
  SecretId: "payments/db",
  RotationLambdaARN: "arn:aws:lambda:sa-east-1:123456789012:function:pg-rotator",
  RotationRules: { ScheduleExpression: "rate(30 days)" },
}));
console.log("rotation on");

Expected output:

rotation on
  1. PHP: read the previous version (for a rollback).
<?php
$old = $sm->getSecretValue(["SecretId" => "payments/db", "VersionStage" => "AWSPREVIOUS"]);
echo json_decode($old["SecretString"])->user, " previous version ok", PHP_EOL;

Expected output:

pay_app previous version ok
  1. Verify: list versions.
aws secretsmanager list-secret-version-ids --secret-id payments/db --region sa-east-1 --query "Versions[].VersionStages" --output text

Expected output:

AWSPREVIOUS
AWSCURRENT

4. Parameter Store settings

Why do we need this?

Sneha keeps feature flags, the UPI gateway URL and a Stripe key per environment.

What is it?

Name parameters as paths like /payments/prod/... . String for plain values, SecureString for secrets. GetParametersByPath reads the whole tree (10 per page, paginate).

How to do it (step by step)

  1. Python: put three parameters.
ssm = boto3.client("ssm", region_name="ap-south-2")
ssm.put_parameter(Name="/payments/prod/upi_url", Value="https://upi.example.in/v2", Type="String", Overwrite=True)
ssm.put_parameter(Name="/payments/prod/max_txn_inr", Value="200000", Type="String", Overwrite=True)
ssm.put_parameter(Name="/payments/prod/stripe_key", Value="sk_live_51abc", Type="SecureString", Overwrite=True)
print("3 saved")

Expected output:

3 saved
  1. Node.js: read one SecureString.
import { SSMClient, GetParameterCommand } from "@aws-sdk/client-ssm";
const ssm = new SSMClient({ region: "ap-south-2" });
const r = await ssm.send(new GetParameterCommand({ Name: "/payments/prod/stripe_key", WithDecryption: true }));
console.log(r.Parameter.Type, r.Parameter.Value.slice(0, 8) + "…");

Expected output:

SecureString sk_live_…
  1. PHP: read the whole path with a paginator.
<?php
$ssm = new Aws\Ssm\SsmClient(["region" => "ap-south-2"]);
$cfg = [];
foreach ($ssm->getPaginator("GetParametersByPath", ["Path" => "/payments/prod/", "WithDecryption" => true]) as $page)
    foreach ($page["Parameters"] as $p) $cfg[basename($p["Name"])] = $p["Value"];
ksort($cfg); echo implode(",", array_keys($cfg)), PHP_EOL;

Expected output:

max_txn_inr,stripe_key,upi_url
  1. Verify: describe parameters.
aws ssm describe-parameters --parameter-filters Key=Path,Values=/payments/prod --region ap-south-2 --query "Parameters[].[Name,Type]" --output text

Expected output:

/payments/prod/max_txn_inr  String
/payments/prod/stripe_key   SecureString
/payments/prod/upi_url      String

5. Cache values

Why do we need this?

At 500 requests a second, calling AWS every time is slow, costly and hits throttling.

What is it?

Read once and keep it in memory for a few minutes. Python and others have official caching libraries; a small TTL cache works everywhere. On Lambda, the Parameters and Secrets extension does it for you on localhost:2773.

How to do it (step by step)

  1. Python: official cache.
# pip install aws-secretsmanager-caching
from aws_secretsmanager_caching import SecretCache, SecretCacheConfig
cache = SecretCache(config=SecretCacheConfig(secret_refresh_interval=300), client=sm)
for _ in range(3):
    print(json.loads(cache.get_secret_string("payments/db"))["user"])

Expected output:

pay_app
pay_app
pay_app   (one API call)
  1. Node.js: a tiny TTL cache.
const cache = new Map();
async function param(name, ttl = 300_000) {
  const hit = cache.get(name);
  if (hit && hit.exp > Date.now()) return hit.v;
  const r = await ssm.send(new GetParameterCommand({ Name: name, WithDecryption: true }));
  cache.set(name, { v: r.Parameter.Value, exp: Date.now() + ttl });
  return r.Parameter.Value;
}
console.log(await param("/payments/prod/max_txn_inr"), await param("/payments/prod/max_txn_inr"));

Expected output:

200000 200000   (second read from memory)
  1. PHP: APCu cache around the SDK call.
<?php
function param(Aws\Ssm\SsmClient $ssm, string $n): string {
    $v = apcu_fetch($n, $ok);
    if ($ok) return $v;
    $v = $ssm->getParameter(["Name" => $n, "WithDecryption" => true])["Parameter"]["Value"];
    apcu_store($n, $v, 300);
    return $v;
}
echo param($ssm, "/payments/prod/upi_url"), PHP_EOL;

Expected output:

https://upi.example.in/v2
  1. Verify: on Lambda, read via the extension.
curl -s -H "X-Aws-Parameters-Secrets-Token: $AWS_SESSION_TOKEN" "localhost:2773/systemsmanager/parameters/get?name=/payments/prod/upi_url" | jq -r .Parameter.Value

Expected output:

https://upi.example.in/v2

6. The same call in Java, Go, .NET and the CLI

Why do we need this?

Teams often mix languages. Once you know the call in Python, PHP and Node.js, the other SDKs are the same API with different syntax.

What is it?

All of them call the GetSecretValue API. Use the CLI line to test it quickly from a terminal.

How to do it (step by step)

StackCode
AWS CLIaws secretsmanager get-secret-value --secret-id payments/db --query SecretString --output text
Java v2String s = sm.getSecretValue(b -> b.secretId("payments/db")).secretString();
Go v2out, _ := client.GetSecretValue(ctx, &secretsmanager.GetSecretValueInput{SecretId: aws.String("payments/db")})
fmt.Println(*out.SecretString)
.NETvar r = await sm.GetSecretValueAsync(new GetSecretValueRequest { SecretId = "payments/db" });
Console.WriteLine(r.SecretString);

7. Clean up so nothing keeps costing

Why do we need this?

Lab resources left running cost money and can be a security risk.

What is it?

Delete in reverse order: children first, then parents.

How to do it (step by step)

  1. Delete the secret and parameters.
aws secretsmanager delete-secret --secret-id payments/db --force-delete-without-recovery --region sa-east-1
aws ssm delete-parameters --names /payments/prod/upi_url /payments/prod/max_txn_inr /payments/prod/stripe_key --region ap-south-2

Expected output:

(no output means success)

Ravindra Bagale's Tip

Never print or log a secret value, even in debug. Log the secret name and version id instead.

8. Interview questions and answers

Q. Secrets Manager vs Parameter Store?

Secrets Manager adds rotation and cross-account sharing at a per-secret price; Parameter Store is free for standard parameters and good for config.

Q. What does WithDecryption do?

Returns the plain value of a SecureString parameter using KMS.

Q. What are AWSCURRENT and AWSPREVIOUS?

Staging labels on secret versions; reads default to AWSCURRENT.

Q. Why cache secrets?

To cut latency, cost and throttling; refresh every few minutes to pick up rotation.

Q. How many parameters per GetParametersByPath page?

Up to 10; use the paginator.

Try at home

  1. Store /payments/dev/ values and load dev or prod config based on an APP_ENV variable in Node.js.
  2. Write an IAM policy that lets an app read only /payments/prod/*.

In short

  1. Secrets: Create/Get/Put.
  2. Rotate with a Lambda.
  3. SSM: SecureString + WithDecryption.
  4. GetParametersByPath + paginator.
  5. Cache with a TTL.

Practice Tasks

About this scenario

Sahyadri Clinic is a healthcare business in Satara. Dr. Anil Deshmukh looks after its IT and cloud work. They count money in INR and run their AWS resources in the ap-south-1 Region. In the tasks below you do real jobs for them using what this lesson teaches. All names, account IDs, IP addresses and numbers are made up for practice.

0 / 11

Easy Read a secret

Why: Apps read the secret at start-up instead of keeping it in a file.

What to do: Get the value of secret sahyadriclinic/prod/db.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Easy Port for SSH

Why: Security group rules need the right port.

What to do: Which TCP port must the security group open for SSH?

How:
  1. Write down the numbers the task gives you.
  2. Work it out step by step on paper.
  3. Type only the final answer in the box.
  4. Click Check answer.

Easy Port for MySQL

Why: The DB security group should open only this port, only to the app.

What to do: Sahyadri Clinic's app talks to RDS MySQL. Which port should the DB security group allow?

How:
  1. Write down the numbers the task gives you.
  2. Work it out step by step on paper.
  3. Type only the final answer in the box.
  4. Click Check answer.

Medium Read a decrypted parameter

Why: Without --with-decryption you only see the encrypted blob.

What to do: Read /sahyadriclinic/prod/api-key and decrypt it.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Medium Store an encrypted parameter

Why: SecureString parameters are encrypted with KMS and are free for standard tier.

What to do: Store /sahyadriclinic/prod/api-key as a SecureString with the value abc123.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Medium Read one secret only

Why: Each app should read only its own secret.

What to do: Allow secretsmanager:GetSecretValue on arn:aws:secretsmanager:ap-south-1:102103063049:secret:sahyadriclinic/prod/db-* only.

How:
  1. Start with { "Version": "2012-10-17", "Statement": [ ... ] }.
  2. In each statement write Effect, Action and Resource.
  3. Give only what the task needs (least privilege).
  4. Use double quotes and commas correctly. Click Check answer.

Medium Store a database password

Why: Secrets Manager encrypts secrets and can rotate them; never put them in code.

What to do: Create secret sahyadriclinic/prod/db with the value from secret.json.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Medium CloudFormation: web security group

Why: Opening only 443 keeps the server safer.

What to do: Template with an AWS::EC2::SecurityGroup that allows TCP 443 from 0.0.0.0/0. Add a GroupDescription.

How:
  1. Start from the top-level keys the task names.
  2. Indent child keys with 2 spaces (no tabs).
  3. Fill in each property with the value from the task.
  4. Click Check answer. Key order does not matter.

Hard Read all parameters under a path

Why: A path groups config, like /app/prod/, so one call loads it all.

What to do: Get all parameters under /sahyadriclinic/prod, including sub-paths, decrypted.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Hard Rotate a secret now

Why: Rotation changes the password on a schedule or on demand.

What to do: Start rotation of secret sahyadriclinic/prod/db now.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Hard Deny access from outside the office

Why: An explicit Deny with aws:SourceIp blocks keys used from anywhere else.

What to do: Write one Deny statement: deny all actions (*) on all resources (*) when the source IP is NOT in 203.0.113.0/24.

How:
  1. Start with { "Version": "2012-10-17", "Statement": [ ... ] }.
  2. In each statement write Effect, Action and Resource.
  3. Give only what the task needs (least privilege).
  4. Use double quotes and commas correctly. Click Check answer.

Revision, comparison and interview cards

Revise this lesson in a few minutes. Save the cards on your phone or download them as PNG files. The QR code on each card opens this lesson.

Secrets Manager. Store JSON in SecretString. GetSecretValue, then parse. PutSecretValue: old → AWSPREVIOUS. RotateSecret with a Lambda.
Revision card: Secrets Manager · Download PNG · Open card page
Parameter Store. Paths: /app/env/name. SecureString + WithDecryption. GetParametersByPath, paginate. Cache values for minutes.
Revision card: Parameter Store · Download PNG · Open card page
Secrets Manager vs Parameter Store. Encrypted store for secrets. / Key-value store for config.
Comparison: Secrets Manager vs Parameter Store · Download PNG · Open card page
Throttled reading secrets?. Reading on every request. Cache for 5 minutes. Lambda: use the extension.
Interview card: Throttled reading secrets? · Download PNG · Open card page

More cards: Ravi Sir's Cards.