You’re going to store a secret in Parameter Store, write a Lambda function that reads it at startup, grant the function’s execution role the right permissions, and verify the whole chain works through API Gateway.
This is the same workflow you’d use in production to keep API keys out of your environment variables and source code.
If you want AWS’s version of the secret-storage workflow open while you work, keep the AWS Secrets Manager overview and the Parameter Store documentation nearby.
Why It Matters
On Vercel, secrets are environment variables—you set them in the dashboard and trust the platform. On AWS, you choose where secrets live and who can access them. That choice has consequences: a secret in a Lambda environment variable is visible to anyone who can describe the function. A secret in Parameter Store is encrypted with KMS, scoped by IAM, and audited in CloudTrail. This exercise makes that difference real.
Your Task
- Store a third-party API key in Parameter Store as a SecureString
- Write a Lambda function that reads the secret at init time and uses it in responses
- Grant the function’s execution role permission to read the parameter and decrypt it
- Invoke the function and verify it can access the secret
- Call the function through API Gateway from a browser or
curl
Use the account ID 123456789012, region us-east-1, and the nodejs20.x runtime.
Store the Secret in Parameter Store
Create a SecureString parameter at the path /my-frontend-app/production/third-party-api-key. Use any value you want for the secret—something like sk_test_exercise_abc123 is fine.
Use aws ssm put-parameter with:
--nameset to the full path--valueset to your test API key--typeset toSecureString--region us-east-1
After creating it, verify the parameter exists by retrieving it with aws ssm get-parameter. Make sure to include the flag that decrypts the value—without it, you’ll see ciphertext instead of your key.
Checkpoint
aws ssm get-parameter --name "/my-frontend-app/production/third-party-api-key" --with-decryption --region us-east-1 --output json returns your parameter with the decrypted value.
Write the Lambda Handler
Create a Lambda project (or reuse the one from the Lambda Function Exercise) with the following dependencies:
npm install @aws-sdk/client-ssm
npm install -D typescript @types/aws-lambda @types/nodeWrite a handler in src/handler.ts that:
- Imports
SSMClientandGetParameterCommandfrom@aws-sdk/client-ssm - Creates the SSM client at module level
- Declares a module-level variable for the cached API key
- Defines an async
loadConfigfunction that fetches the parameter (with decryption) and caches it—but skips the fetch if the value is already cached - Calls
loadConfig()at the top of the handler - Returns a JSON response with a
messageand the first 7 characters of the API key (to prove the function can read it without exposing the full value)
Use the APIGatewayProxyHandlerV2 type for the handler.
Build the project and verify it compiles without errors.
Checkpoint
Running npm run build produces dist/handler.js with no TypeScript errors.
Update the Execution Role
Your Lambda execution role (my-frontend-app-lambda-role) needs two new permissions:
ssm:GetParameteron the specific parameter ARN. Remember: the ARN for a parameter named/my-frontend-app/production/third-party-api-keyisarn:aws:ssm:us-east-1:123456789012:parameter/my-frontend-app/production/third-party-api-key. Note the path in the ARN doesn’t double the leading slash.kms:Decrypton the AWS-managed SSM KMS key, so the function can decrypt the SecureString value.
Write a policy JSON file and attach it to the execution role using aws iam put-role-policy (inline policy) or aws iam create-policy followed by aws iam attach-role-policy (managed policy).
Verify the policy is attached by listing the role’s policies.
Checkpoint
The execution role has a policy that grants ssm:GetParameter on the specific parameter ARN and kms:Decrypt on the KMS key.
Deploy and Invoke
Package and deploy the Lambda function:
- Build the TypeScript project
- Zip the contents of the
dist/directory (plusnode_modules/—you need the@aws-sdk/client-ssmpackage in the deployment) - Create or update the Lambda function
If you already have a function named my-frontend-app-api from a previous exercise, use aws lambda update-function-code instead of create-function. You can also create a new function with a different name like my-frontend-app-secrets-demo.
Invoke the function with a test event:
aws lambda invoke \
--function-name my-frontend-app-api \
--payload '{"requestContext":{"http":{"method":"GET","path":"/secret"}},"queryStringParameters":{}}' \
--cli-binary-format raw-in-base64-out \
--region us-east-1 \
--output json \
response.jsonRead the response file and verify the function returned the first 7 characters of your API key.
Checkpoint
The response file contains a 200 status code and a body with a message confirming the secret was loaded. The key prefix matches the first 7 characters of the value you stored.
Test Through API Gateway
If you have an API Gateway HTTP API from Connecting API Gateway to Lambda, add a route that points to this function—or invoke the existing endpoint if the function is already wired up.
If you don’t have an API Gateway set up, you can test with the CLI invocation from the previous section. The API Gateway integration is a stretch goal.
Checkpoint
A curl request to your API Gateway endpoint returns the JSON response with the secret loaded successfully.
Verify the Secret Isn’t in Environment Variables
Run get-function-configuration and confirm the API key is not in the environment variables:
aws lambda get-function-configuration \
--function-name my-frontend-app-api \
--query 'Environment.Variables' \
--region us-east-1 \
--output jsonThe output should show your non-sensitive configuration (like TABLE_NAME) but not the API key. The API key lives in Parameter Store, accessible only through the SDK with proper IAM permissions.
Checkpoint
The function’s environment variables don’t contain the API key. The key exists only in Parameter Store.
Checkpoints Summary
- SecureString parameter exists at
/my-frontend-app/production/third-party-api-key -
aws ssm get-parameterwith--with-decryptionreturns the correct value - Lambda handler compiles and reads the parameter using
SSMClient - Execution role has
ssm:GetParameterandkms:Decryptpermissions - Function invocation returns the first 7 characters of the API key
- The API key does not appear in the function’s environment variables
Failure Diagnosis
- The handler throws an access error when reading the parameter: The execution role is missing either
ssm:GetParameterfor the parameter path orkms:Decryptfor the key protecting the SecureString. - The deployment succeeds but the function returns
undefinedor a stale value: The parameter name in code does not match the stored path, or you updated the parameter but never redeployed the code that references it. - The secret still appears in the function configuration: You solved the fetch path but kept the old environment variable in place. Remove the secret from Lambda configuration entirely so Parameter Store is the only source of truth.
Stretch Goals
Fetch multiple parameters with
GetParametersByPath. Store a second parameter at/my-frontend-app/production/api-endpointas a plainString. Modify the handler to useGetParametersByPathCommandto load all parameters under/my-frontend-app/production/in a single call. Verify both values appear in the response.Add a cache TTL. Modify the
loadConfigfunction to re-fetch the parameter after 5 minutes instead of caching indefinitely. Test by updating the parameter value in Parameter Store and invoking the function after the TTL expires.Try the Lambda extension. Add the AWS Parameters and Secrets Lambda Extension layer to your function. Modify the handler to fetch the parameter via
http://localhost:2773instead of the SDK. Compare the cold start duration with and without the extension.
When you’re ready, check your work against the Solution: Store and Retrieve a Secret in Lambda.