Skip to content

Commit 7b4d05d

Browse files
Added lambda secrets example (#1290)
This pull request includes several important changes to the `aws-ts-lambda-secrets` project, aimed at adding functionality for securely managing secrets in AWS Lambda functions. The most significant changes include updates to configuration files, the addition of a README with detailed instructions, and the implementation of the Lambda function itself. ### Configuration and Setup: * [`aws-ts-lambda-secrets/.gitignore`](diffhunk://#diff-ff0c423a810cb3967f98f8292eae57875a153f3fe43dc8a1f7d873b44b4c4f22R1-R2): Added `/bin/` and `/node_modules/` to ignore list to prevent tracking build and dependency files. * [`aws-ts-lambda-secrets/Pulumi.yaml`](diffhunk://#diff-dfedf6b88d86811458ea6e89da6e96e36fd6f3e98acc2f182305510969617dcbR1-R3): Added project metadata including name, description, and runtime. * [`aws-ts-lambda-secrets/package.json`](diffhunk://#diff-714d06d25653a83a5b0550f348966f9e7d21dcd261f289c264a6a70097e5b374R1-R11): Defined project dependencies and development dependencies for Pulumi and AWS SDKs. * [`aws-ts-lambda-secrets/tsconfig.json`](diffhunk://#diff-3147e9ffca0ab0d3c507a40f39e4730fd0dcf028eef9dcee591880742102237eR1-R19): Configured TypeScript compiler options for the project. ### Documentation: * [`aws-ts-lambda-secrets/README.md`](diffhunk://#diff-3b4654cf63bd221fa0aa3a4a7e0bb5755c1424840f65d257bfed5f35cc49ac71R1-R102): Added comprehensive instructions for setting up, configuring, deploying, and cleaning up the AWS Lambda function with secrets management. ### Lambda Function Implementation: * [`aws-ts-lambda-secrets/app/index.js`](diffhunk://#diff-32a85a3f75357c8a8ffb8dd2c4315c864724440e2e4c40631dee1d0974a23f27R1-R20): Implemented the Lambda function to retrieve and use secrets from AWS Secrets Manager. * [`aws-ts-lambda-secrets/app/package.json`](diffhunk://#diff-261b8e969067ee707cd31b30a140cdde8b7d6d711627df3110aa34236bfb25efR1-R16): Added dependencies for the AWS SDK to interact with Secrets Manager. * [`aws-ts-lambda-secrets/index.ts`](diffhunk://#diff-d415fe4b5b0f46270f2119f6225cf6bb8f0bf38af9a1421e7638f29654d829a9R1-R89): Defined the Pulumi infrastructure code to create and manage AWS resources, including secrets, IAM roles, and the Lambda function.
1 parent 455c3db commit 7b4d05d

File tree

8 files changed

+262
-0
lines changed

8 files changed

+262
-0
lines changed

aws-ts-lambda-secrets/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/bin/
2+
/node_modules/

aws-ts-lambda-secrets/Pulumi.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
name: aws-ts-lambda-secrets
2+
description: A minimal AWS TypeScript Pulumi program
3+
runtime: nodejs

aws-ts-lambda-secrets/README.md

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
[![Deploy](https://get.pulumi.com/new/button.svg)](https://app.pulumi.com/new)
2+
3+
# Lambd secrets
4+
5+
Storing stack secrets securely and accessing them in a Lambda Function
6+
7+
You can find a post describing the code on the [Pulumi blog](https://pulumi.com/blog/safe-lambda-secrets/).
8+
9+
## Running the App
10+
11+
1. Create a new stack:
12+
13+
```
14+
pulumi stack init dev
15+
```
16+
17+
1. Configure Pulumi to use an AWS region of your choice, for example:
18+
19+
```
20+
pulumi config set aws:region us-west-2
21+
```
22+
23+
1. Restore NPM modules via `npm install` or `yarn install`.
24+
25+
1. Add values to your stack config, both secret and plaintext:
26+
27+
```bash
28+
pulumi config set --path 'lambdawithsecrets.envvars["envvar1"]' envvar1value
29+
pulumi config set --path 'lambdawithsecrets.envvars["envvar2"]' envvar2value
30+
pulumi config set --path 'lambdawithsecrets.secrets["secret1"]' secretvalue1 --secret
31+
pulumi config set --path 'lambdawithsecrets.secrets["secret2"]' secretvalue2 --secret
32+
```
33+
34+
1. Preview and deploy the app via `pulumi up`.
35+
36+
```
37+
$ pulumi up
38+
Previewing update (dev)
39+
40+
View Live: https://app.pulumi.com/acmecorp/lambda-secrets/dev/previews/209f07fb-190d-4596-af9f-d2d72e9c5cc9
41+
42+
Type Name Plan
43+
+ pulumi:pulumi:Stack lambda-secrets-dev create
44+
+ ├─ aws:secretsmanager:Secret secret1 create
45+
+ ├─ aws:secretsmanager:Secret secret2 create
46+
+ ├─ aws:iam:Role roleLambdaWithSecrets create
47+
+ ├─ aws:secretsmanager:SecretVersion secretversion-secret2 create
48+
+ ├─ aws:secretsmanager:SecretVersion secretversion-secret1 create
49+
+ ├─ aws:iam:Policy secretsPolicy create
50+
+ ├─ aws:iam:RolePolicyAttachment rpa-basic create
51+
+ ├─ aws:iam:RolePolicyAttachment rpa-secrets create
52+
+ └─ aws:lambda:Function lambdaWithSecrets create
53+
54+
Outputs:
55+
lambdaName: "lambdaWithSecrets-b8ac5e9"
56+
57+
Resources:
58+
+ 10 to create
59+
60+
Do you want to perform this update? yes
61+
Updating (dev)
62+
63+
View Live: https://app.pulumi.com/acmecorp/lambda-secrets/dev/updates/1
64+
65+
Type Name Status
66+
+ pulumi:pulumi:Stack lambda-secrets-dev created
67+
+ ├─ aws:secretsmanager:Secret secret1 created
68+
+ ├─ aws:secretsmanager:Secret secret2 created
69+
+ ├─ aws:iam:Role roleLambdaWithSecrets created
70+
+ ├─ aws:secretsmanager:SecretVersion secretversion-secret2 created
71+
+ ├─ aws:secretsmanager:SecretVersion secretversion-secret1 created
72+
+ ├─ aws:iam:Policy secretsPolicy created
73+
+ ├─ aws:iam:RolePolicyAttachment rpa-basic created
74+
+ ├─ aws:lambda:Function lambdaWithSecrets created
75+
+ └─ aws:iam:RolePolicyAttachment rpa-secrets created
76+
77+
Outputs:
78+
lambdaName: "lambdaWithSecrets-ca62cf8"
79+
80+
Resources:
81+
+ 10 created
82+
83+
Duration: 24s
84+
```
85+
86+
1. Invoke the Lambda Function:
87+
88+
```
89+
aws lambda invoke --function-name $(pulumi stack output lambdaName) /dev/stdout
90+
```
91+
92+
You should see the output as the secret values you added:
93+
94+
```
95+
{"secret1":"secret1value","secret2":"secret2value"}
96+
```
97+
98+
1. You can view the environment variables containing the plaintext values and the secret ARNs in the details of the Lambda Function in the AWS Console.
99+
100+
## Clean up
101+
102+
To clean up the resources, you will need to run `pulumi destroy` and answer the confirmation question at the prompt.

aws-ts-lambda-secrets/app/index.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { SecretsManagerClient, GetSecretValueCommand } from "@aws-sdk/client-secrets-manager"; // ES Modules import
2+
const client = new SecretsManagerClient();
3+
4+
const getSecretForLambda = async (secretId) => {
5+
const input = { "SecretId": secretId };
6+
const command = new GetSecretValueCommand(input);
7+
console.log('Retrieving secret during top-level await');
8+
return await client.send(command);
9+
}
10+
11+
const secret1 = getSecretForLambda(process.env.SECRET1);
12+
const secret2 = getSecretForLambda(process.env.SECRET2)
13+
14+
export async function handler() {
15+
console.log('Using secret in handler')
16+
return {
17+
secret1: (await secret1).SecretString,
18+
secret2: (await secret2).SecretString,
19+
};
20+
};
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"name": "app",
3+
"version": "1.0.0",
4+
"description": "",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "echo \"Error: no test specified\" && exit 1"
8+
},
9+
"keywords": [],
10+
"author": "",
11+
"license": "ISC",
12+
"dependencies": {
13+
"@aws-sdk/client-secrets-manager": "^3.183.0"
14+
},
15+
"type": "module"
16+
}

aws-ts-lambda-secrets/index.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Copyright 2016-2022, Pulumi Corporation. All rights reserved.
2+
3+
import * as aws from "@pulumi/aws";
4+
import * as pulumi from "@pulumi/pulumi";
5+
6+
interface LambdaConfig {
7+
envvars: string[];
8+
secrets: pulumi.Output<string>[];
9+
}
10+
11+
const config = new pulumi.Config();
12+
const lambdaconfig = config.requireObject<LambdaConfig>("lambdawithsecrets");
13+
14+
const secretsArnArray: pulumi.Output<string>[] = new Array();
15+
const secretArray: aws.secretsmanager.Secret[] = new Array();
16+
for (const key in lambdaconfig.secrets) {
17+
if (lambdaconfig.secrets.hasOwnProperty(key)) {
18+
const secret = new aws.secretsmanager.Secret(`${key}`);
19+
const secretVersion = new aws.secretsmanager.SecretVersion(`secretversion-${key}`, {
20+
secretId: secret.id,
21+
secretString: lambdaconfig.secrets[key],
22+
});
23+
secretArray.push(secret);
24+
secretsArnArray[key.toLocaleUpperCase()] = secret.id;
25+
}
26+
}
27+
28+
const role = new aws.iam.Role("roleLambdaWithSecrets", {
29+
assumeRolePolicy: aws.iam.assumeRolePolicyForPrincipal(aws.iam.Principals.LambdaPrincipal),
30+
});
31+
32+
const rpaBasic = new aws.iam.RolePolicyAttachment("rpa-basic", {
33+
role: role,
34+
policyArn: aws.iam.ManagedPolicy.AWSLambdaBasicExecutionRole,
35+
});
36+
37+
38+
39+
const secretManagerPolicyDoc = aws.iam.getPolicyDocumentOutput({
40+
statements: [
41+
{
42+
effect: "Allow",
43+
actions: [
44+
"secretsmanager:GetSecretValue",
45+
],
46+
resources: secretArray.map(x => pulumi.interpolate`${x.arn}`),
47+
},
48+
],
49+
});
50+
51+
const secretManagerPolicy = new aws.iam.Policy("secretsPolicy", {
52+
policy: secretManagerPolicyDoc.apply(doc => doc.json),
53+
});
54+
55+
const rpaSecrets = new aws.iam.RolePolicyAttachment("rpa-secrets", {
56+
role: role,
57+
policyArn: secretManagerPolicy.arn,
58+
});
59+
60+
const lambdaEnvVars: pulumi.Input<{
61+
[key: string]: pulumi.Input<string>;
62+
}> = {};
63+
64+
for (const key in secretsArnArray) {
65+
if (secretsArnArray.hasOwnProperty(key)) {
66+
lambdaEnvVars[key] = secretsArnArray[key];
67+
}
68+
}
69+
70+
for (const key in lambdaconfig.envvars) {
71+
if (lambdaconfig.envvars.hasOwnProperty(key)) {
72+
lambdaEnvVars[key.toLocaleUpperCase()] = lambdaconfig.envvars[key];
73+
}
74+
}
75+
76+
const lambda = new aws.lambda.Function("lambdaWithSecrets", {
77+
code: new pulumi.asset.AssetArchive({
78+
".": new pulumi.asset.FileArchive("./app"),
79+
}),
80+
role: role.arn,
81+
handler: "index.handler",
82+
runtime: aws.lambda.Runtime.NodeJS16dX,
83+
environment: {
84+
variables: lambdaEnvVars,
85+
},
86+
timeout: 15,
87+
});
88+
89+
export const lambdaName = lambda.name;

aws-ts-lambda-secrets/package.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"name": "aws-ts-lambda-secrets",
3+
"main": "index.ts",
4+
"devDependencies": {
5+
"@types/node": "^14"
6+
},
7+
"dependencies": {
8+
"@pulumi/pulumi": "^3.0.0",
9+
"@pulumi/aws": "^5.0.0"
10+
}
11+
}

aws-ts-lambda-secrets/tsconfig.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"compilerOptions": {
3+
"strict": true,
4+
"outDir": "bin",
5+
"target": "es2016",
6+
"module": "commonjs",
7+
"moduleResolution": "node",
8+
"sourceMap": true,
9+
"experimentalDecorators": true,
10+
"pretty": true,
11+
"noFallthroughCasesInSwitch": true,
12+
"noImplicitReturns": true,
13+
"forceConsistentCasingInFileNames": true,
14+
"suppressImplicitAnyIndexErrors": true
15+
},
16+
"files": [
17+
"index.ts"
18+
]
19+
}

0 commit comments

Comments
 (0)