DEV Community

Cover image for Shhh, It's a Secret! Using Pulumi ESC & AWS Lambda for Secure Secrets Management
Emidowojo
Emidowojo

Posted on

15 9 9 9 9

Shhh, It's a Secret! Using Pulumi ESC & AWS Lambda for Secure Secrets Management

This is a submission for the Pulumi Deploy and Document Challenge: Shhh, It's a Secret!

Image description: Lambda and ESC in Action

What I Built

Hey there! I'm excited to share Shhh, It's a Secret!, a beginner-friendly project where I built an AWS Lambda function using Pulumi. This Lambda grabs a secret (in my case, fake-api-key-xyz123) from Pulumi ESC (Environments, Secrets, and Configuration)—a super cool tool for keeping sensitive stuff safe. While it's not a static website itself, imagine it as a helper for one: it could securely pass secrets to a fast static site without exposing them in the code. I used Pulumi to set up everything, ESC to manage the secret, and Node.js to make the Lambda work—all wrapped up in a GitHub repo with a clear README for anyone to follow along.

Here's what's inside:

  • Pulumi Stack: Sets up an IAM role and the Lambda function.
  • ESC Environment: Holds my secret, myApiKey.
  • Lambda Function: Pulls the secret using the @pulumi/esc-sdk and logs it.

Don't worry if this sounds complex—I'll walk you through every step so you can build it too.

Live Demo Link

Since this is a Lambda (a serverless function) and not a website with a URL, there's no "live demo" to click. But you can try it yourself in your AWS account! Here's how:

  1. Grab the code from my repo (see below).
  2. Run pulumi up to deploy it.
  3. Head to AWS Console > Lambda > secretFetcher > Test, and run a test event (just use {}).
  4. Check CloudWatch logs—you should see "Secret fetched: fake-api-key-xyz123".

Project Repo

Check out my GitHub repo: Emidowojo/pulumi-secret-demo. It's got all the code and a README.md. Here's a peek:

# Pulumi Secret Demo
This project shows you how to fetch a secret from Pulumi ESC with an AWS Lambda.
## Setup
1. Install Pulumi and AWS CLI (don't worry, I'll explain how below!).
2. Run `pulumi login` and `aws configure`.
3. Clone this: `git clone https://github.com/Emidowojo/pulumi-secret-demo`.
4. Go in: `cd pulumi-secret-demo && npm install`.
5. Set up ESC: Make an environment `Emidowojo/pulumi-secret-demo/my-secrets` with `myApiKey: fake-api-key-xyz123`.
6. Add your token: `pulumi config set pulumiAccessToken <your-token> --secret`.
7. Deploy: `pulumi up`.
## Testing
- Go to AWS Console > Lambda > `secretFetcher` > Test.
- Look in CloudWatch logs for "Secret fetched: fake-api-key-xyz123".
Enter fullscreen mode Exit fullscreen mode

My Journey

While building it, I hit bumps, learned so much, and came out with a working project. Let me take you through it step-by-step, so you can follow along (and avoid my mistakes).

Day 1: Getting Started with Pulumi and ESC

What I Did:

  1. Tools First: I installed Pulumi with npm install -g @pulumi/pulumi (it's a command-line tool for cloud stuff) and AWS CLI via Homebrew (brew install awscli) on my MacBook.
  2. Logging In: Ran pulumi login to connect to app.pulumi.com (you'll need an account—free to sign up!). Then aws configure to add my AWS Access Key, Secret Key, and region (us-east-1).
  3. New Project: Made a folder with mkdir pulumi-secret-demo && cd pulumi-secret-demo, then started a Pulumi project with pulumi new aws-typescript. This gave me a starter index.ts file.
  4. ESC Setup: Went to app.pulumi.com, clicked ESC > New Environment, named it Emidowojo/pulumi-secret-demo/my-secrets, and added:
values:
  myApiKey: fake-api-key-xyz123
Enter fullscreen mode Exit fullscreen mode

Token Time: Got my Pulumi access token from User Settings and ran:

export PULUMI_ACCESS_TOKEN=pul-**************************************************************
Enter fullscreen mode Exit fullscreen mode

What Went Wrong:

  • I forgot the token at first—pulumi up failed with "Not authenticated." Setting the token fixed it fast.

Takeaway: ESC is awesome for secrets—it's like a safe for your API keys. If you're new, try to double-check your logins.

Day 2: Making the Lambda and Fixing Bugs

What I Did:

Pulumi Code (index.ts): Wrote this to create a Lambda and IAM role:

import * as pulumi from "@pulumi/pulumi"; // Import Pulumi for coding cloud stuff
import * as aws from "@pulumi/aws"; // Import AWS tools for Lambda and IAM
// Create an IAM role so Lambda can run
const lambdaRole = new aws.iam.Role("lambdaRole", {
    assumeRolePolicy: aws.iam.assumeRolePolicyForPrincipal({ Service: "lambda.amazonaws.com" }),
});
// Attach a basic policy to the role
new aws.iam.RolePolicyAttachment("lambdaPolicy", {
    role: lambdaRole, // Link to our role
    policyArn: aws.iam.ManagedPolicy.AWSLambdaBasicExecutionRole,// Gives Lambda permission to log
});
// Define the Lambda function
const lambda = new aws.lambda.Function("secretFetcher", {
    runtime: "nodejs18.x",     // Use Node.js 18
    handler: "index.handler", // Points to lambda/index.js’s handler function
    role: lambdaRole.arn,   // Use our role’s ARN (unique ID)
    code: new pulumi.asset.AssetArchive({ // Bundle our Lambda code
        ".": new pulumi.asset.FileArchive("./lambda"), // Grab everything in the lambda folder
    }),
    environment: {     // Set environment variables
        variables: {
            PULUMI_ACCESS_TOKEN: pulumi.output(process.env.PULUMI_ACCESS_TOKEN).apply(t => t || ""),
        },
    },
});

export const lambdaArn = lambda.arn; // Share the Lambda’s ARN for later use
Enter fullscreen mode Exit fullscreen mode

Lambda Code: Made a lambda folder:

mkdir lambda && cd lambda && npm init -y && npm install @pulumi/esc-sdk
Enter fullscreen mode Exit fullscreen mode

First try at lambda/index.js:

const { EscClient } = require("@pulumi/esc-sdk"); // Import the ESC library (first version I tried)
exports.handler = async () => {   // Lambda’s main function
    const client = new EscClient({ accessToken: process.env.PULUMI_ACCESS_TOKEN }); // Connect to ESC with token
    const env = await client.getEnvironment("my-secrets"); // Try to fetch the secret
    console.log("Secret fetched:", env.values.myApiKey); // Log the secret
    return { statusCode: 200, body: "Secret retrieved!" }; // Sends a successful response
};
Enter fullscreen mode Exit fullscreen mode

Deployed: Ran pulumi up—it showed "+ 4 to create" and set up my stack.

What Went Wrong: Read the "Challenges I Faced" section below

Takeaway: Don't panic when things break—logs and patience will save you.

Day 3: Winning and Sharing

What I Did:

  1. Final Deploy: After fixing bugs, ran pulumi up again to update the Lambda.
  2. Testing: Went to AWS Console > Lambda > secretFetcher > Test, created a New1 event ({}), and ran it. Checked CloudWatch logs—bam, "Secret fetched: fake-api-key-xyz123"!
  3. GitHub: Pushed everything:
git add . && git commit -m "Finished Lambda with ESC" && git push --force
Enter fullscreen mode Exit fullscreen mode

What Went Wrong: Just making sure the token worked after moving it to config—easy check with pulumi config get pulumiAccessToken.

Takeaway: Seeing it work feels amazing, and sharing it makes it even better!

Challenges I Faced

Here's where I stumbled—and how I got back up. If you hit these, you'll know what to do!

  1. "Cannot find name 'pulumi'" in index.ts

    • Problem: TypeScript didn't recognize pulumi.
    • Fix: Added import * as pulumi from "@pulumi/pulumi"; at the top. Simple miss!
  2. "Type 'string | undefined'" for PULUMI_ACCESS_TOKEN

    • Problem: TypeScript complained about my env var.
    • Fix: Wrapped it: pulumi.output(process.env.PULUMI_ACCESS_TOKEN).apply(t => t || "").
  3. Lambda Not in AWS

    • Problem: Deployed, but couldn't find secretFetcher in us-east-1.
    • Fix: My AWS CLI region didn't match Pulumi's—set it in Pulumi.dev.yaml or CLI.
  4. "EscClient is not a constructor"

    • Problem: Lambda failed with this error.
    • Fix: The SDK changed—updated to const { EscApi } = require("@pulumi/esc-sdk/esc"). Checked node_modules/@pulumi/esc-sdk/esc/index.js to confirm.
  5. "401 Unauthorized" Errors

    • Problem: Lambda kept failing—logs showed "undefined pul-..." in the Authorization header.
    • Fix: Added Configuration:
   const { EscApi, Configuration } = require("@pulumi/esc-sdk/esc"); // Import both classes
   const config = new Configuration({ accessToken: process.env.PULUMI_ACCESS_TOKEN }); // Set up token properly
   const client = new EscApi(config); // Create client with config
Enter fullscreen mode Exit fullscreen mode
  • Also switched to openAndReadEnvironment("Emidowojo", "pulumi-secret-demo", "my-secrets"). // Fetch secret

GitHub Push Blocked

  • Problem: Hardcoded token in index.ts—GitHub refused to.
  • Fix: Moved it to config:
   pulumi config set pulumiAccessToken pul-************************************************************** --secret
Enter fullscreen mode Exit fullscreen mode
  • Updated index.ts:
   const config = new pulumi.Config(); // Use Pulumi’s config system
   const pulumiAccessToken = config.require("pulumiAccessToken"); // Get token securely
   environment: { variables: { PULUMI_ACCESS_TOKEN: pulumiAccessToken } }
Enter fullscreen mode Exit fullscreen mode
  • Amended commit: git commit --amend --no-edit && git push --force.

Using Pulumi ESC

Pulumi made this project so much easier—here's how it helped me (and can help you too!):

  • What It Did: I used Pulumi to write index.ts, setting up my Lambda and IAM role in TypeScript. It's like coding your cloud instead of clicking around!
  • Example: new aws.lambda.Function("secretFetcher", {...}) built my Lambda in one go.
  • How It Worked: Ran pulumi up—it showed me what it'd create (4 resources) and deployed them fast. Later updates were just ~ 1 to update.
  • Why I Loved It: No confusing YAML—TypeScript felt natural. Plus, pulumi up previews caught mistakes before they happened (like missing imports).

Pulumi ESC kept my secrets safe! —here's the rundown:

  • What It Did: I stored myApiKey: fake-api-key-xyz123 in an ESC environment called Emidowojo/pulumi-secret-demo/my-secrets. My Lambda grabs it securely.
  • Setup: Added it in app.pulumi.com under ESC.
  • Code: Used openAndReadEnvironment to fetch it.
  • How I Set It Up: Kept my ESC token safe with:
pulumi config set pulumiAccessToken <your-token> --secret
Enter fullscreen mode Exit fullscreen mode
  • Passed it to Lambda in index.ts's environment.
  • Why It's Great: No secrets in my code—ESC encrypts them and ties right into Pulumi. Perfect for a static site needing secure API keys without the hassle of AWS Secrets Manager.

Conclusion

And there you have it—Shhh, It’s a Secret! is live! We’ve built an AWS Lambda that securely fetches a secret from Pulumi ESC, ready to support a static site without ever risking sensitive data in the code. From setting up Pulumi and ESC to wrestling bugs and pushing to GitHub, this journey taught me how powerful (and fun) infrastructure-as-code can be. Now that it’s working, I’m hooked on exploring more—maybe pairing this Lambda with an S3 static site next! Want to try it? Grab the repo, run pulumi up, and see the magic for yourself. Happy coding!

Quadratic AI

Quadratic AI – The Spreadsheet with AI, Code, and Connections

  • AI-Powered Insights: Ask questions in plain English and get instant visualizations
  • Multi-Language Support: Seamlessly switch between Python, SQL, and JavaScript in one workspace
  • Zero Setup Required: Connect to databases or drag-and-drop files straight from your browser
  • Live Collaboration: Work together in real-time, no matter where your team is located
  • Beyond Formulas: Tackle complex analysis that traditional spreadsheets can't handle

Get started for free.

Watch The Demo 📊✨

Top comments (2)

Collapse
 
tobialagbe profile image
Tobi Alagbe

Lovely breakdown

Collapse
 
egahiojo_opaluwa_b5ba0f7 profile image
Egahi-ojo Opaluwa

This was a very insightful read and easy to follow. Can’t wait to try out Pulumi.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.

👋 Kindness is contagious

DEV is better (more customized, reading settings like dark mode etc) when you're signed in!

Okay