This is a submission for the Pulumi Deploy and Document Challenge: Shhh, It's a Secret!
What I Built
This project demonstrates a comprehensive approach to secure cloud infrastructure deployment using Pulumi's Environments, Secrets, and Configuration (ESC) platform. I've built a mini Reddit-style application (based on Meteor framework) that showcases how to implement infrastructure as code with proper secret management when deploying to Google Cloud Run.
By leveraging Pulumi ESC's unified secrets management capabilities, this project illustrates how development and operations teams can collaborate seamlessly while maintaining security best practices. The application connects to a MongoDB Atlas database with sensitive connection strings and credentials securely stored, accessed, and rotated through Pulumi ESC's SDK.
This implementation demonstrates how organizations can:
- Manage sensitive configuration across different environments (development, staging, production)
- Securely handle database credentials and API keys without exposing them in code
- Implement infrastructure as code using familiar programming languages
- Automate deployment workflows while maintaining security controls
- Establish a foundation for scalable, secure cloud infrastructure management
The documentation provides a step-by-step guide for implementing similar solutions, making it accessible for developers, infrastructure teams, and security professionals looking to enhance their cloud security posture while accelerating deployment cycles.
This project showcases:
- Secure Secret Management: Using Pulumi ESC to securely store and access the MongoDB connection string
- Containerization: Packaging a Meteor 3 application in a Docker container
- Cloud Deployment: Deploying the containerized app to Google Cloud Run
- Infrastructure as Code: Defining all infrastructure using Pulumi
- Meteor Application: A simple Meteor 3 app with BlazeJS
- MongoDB Atlas: A managed MongoDB database
Live Demo Link
Live Demo: Mini Reddit: https://meteor-app-service-b378141-szyom6qpxq-uc.a.run.app
Project Repo
Mooptcom
/
meteor-pulumi-esc
Meteor app deployed to Cloud Run with Pulumi ESC
Meteor App Deployment with Pulumi ESC
This repository demonstrates how to securely deploy a Meteor 3 application to Google Cloud Run using Pulumi ESC for secret management. The application connects to a MongoDB Atlas database, with the connection string securely stored and accessed using Pulumi ESC.
This project was created for the Pulumi Deploy and Document Challenge: Shhh, It's a Secret!
-
Real-time Demo: https://meteor-app-service-b378141-szyom6qpxq-uc.a.run.app or https://meteor-app-service-b378141-1099397202050.us-central1.run.app/
-
Demo video: https://youtu.be/V2-wESswkV8 and https://www.youtube.com/watch?v=O5kcxLiycJM
🌟 Features
- Secure Secret Management: Using Pulumi ESC to securely store and access MongoDB connection strings
- Containerized Meteor App: Multi-stage Docker build optimized for Meteor 3 applications
- Serverless Deployment: Automatic deployment to Google Cloud Run
- Infrastructure as Code: Complete infrastructure defined using Pulumi
- Voting Application: Simple Meteor application with MongoDB integration
🏗️ Architecture
The architecture consists of:
- Meteor Application: A Meteor 3 app with BlazeJS and a todos feature
- MongoDB Atlas…
My Journey
I started with a basic Meteor 3 application using BlazeJS and wanted to deploy it to Google Cloud Run while securely managing the MongoDB connection string. Here's how I approached it:
Setting up the Meteor app: I created a standard Meteor 3 app with BlazeJS and containerized it using a multi-stage Dockerfile.
Managing secrets: I used Pulumi ESC to securely store and access the MongoDB connection string, ensuring it's never exposed in plaintext.
Infrastructure as code: I defined all the infrastructure (Artifact Registry, Cloud Run service, IAM policies), which later to be Pulumi-ed, making the deployment reproducible and version-controlled.
Challenges
I encountered issues with Google IAM permissions, which I resolved by properly configuring and granting the necessary roles to my service account (refer detailed step-by-step guide below).
I was confused between the
pulumi
andesc
commands for environments, secrets and configuration purpose. At first, I had deployed a version based onpulumi config set --secret
at Git Repo: Pulumi2, it was working great! After I read more about Pulumi ESC, I found the difference (refer Pulumi Copilot Prompts below). Given the challenge "Shhh, It's a Secret!" specifically mentions using the Pulumi ESC SDK to qualify for the prompt, I did the second version, meteor-pulumi-esc, that accesses secrets and configurations from Pulumi ESC using the Pulumi ESC SDK, i.e. theesc secret set
approach, for this submission.
Using Pulumi ESC
Pulumi ESC (Environments, Secrets, and Configuration) provides a secure way to manage sensitive configuration data. In this project, I used it to:
Store the MongoDB connection string securely: The connection string is encrypted at rest and never exposed in plaintext.
Access the secret programmatically: I created a bridge between ESC and Pulumi to access the secret at runtime without hardcoding it.
Separate infrastructure from configuration: This separation makes it safer to share and collaborate on the infrastructure code.
Environment-specific configuration: ESC makes it easy to manage different configurations for different environments (dev, staging, prod).
Quick Preview only
PS: Refer detailed step-by-step guide below.
Install the Pulumi ESC CLI
curl -fsSL https://get.pulumi.com/esc/install.sh | sh
First, make sure you're logged in to Pulumi:
esc login
log
Logged in to pulumi.com as kafechew (https://app.pulumi.com/kafechew)
Initialize Pulumi ESC
esc env init meteor-pulumi/dev
log
Environment created: kafechew/meteor-pulumi/dev
Set the mongodb_uri secret:
esc env set meteor-pulumi/dev mongodb_uri 'mongodb+srv://USERNAME:[email protected]/?retryWrites=true&w=majority&appName=Cluster0' --secret
Let's set your GCP project and region in the ESC environment
# Set your GCP project
esc env set meteor-pulumi/dev gcp_project 'meteor-pulumi-esc-demo'
# Set your GCP region
esc env set meteor-pulumi/dev gcp_region 'us-central1'
You can verify your environment configuration
esc env get meteor-pulumi/dev
log
Value
{
"gcp_project": "meteor-pulumi-esc-demo",
"gcp_region": "us-central1",
"mongodb_uri": "[secret]"
}
Definition
values:
mongodb_uri:
fn::secret:
ciphertext: ZXNjeAAAAAEAAAEAA9JEMGnQuWoTJsX9+pBIXo94YgEBV+ZVv+SsQsRiROyHDjNTLKeB50BkrRKAY51z8XLIr1/WDHIb3rxI3R0oF5QvVwtxzRdXVRJEAZg8HR2/+UWzeX7mPunes4kIVcLE3Wbs8+sVNibrGg2uoBShN5w0C/xn5C+aqaEEMFUpZ+Ksk
gcp_project: meteor-pulumi-esc-demo
gcp_region: us-central1
Preview the deployment
pulumi preview
log
Previewing update (dev)
View in Browser (Ctrl+O): https://app.pulumi.com/kafechew/meteor-pulumi-esc/dev/previews/d026ba11-fdc3-421a-914f-246887355da9
Type Name Plan Info
+ pulumi:pulumi:Stack meteor-pulumi-esc-dev create 4 messages
+ ├─ gcp:artifactregistry:Repository meteor-app-repo create
+ ├─ docker:index:Image meteor-app-image create
+ ├─ gcp:cloudrun:Service meteor-app-service create
+ └─ gcp:cloudrun:IamMember meteor-app-everyone create
Diagnostics:
pulumi:pulumi:Stack (meteor-pulumi-esc-dev):
Using values from ESC environment
GCP Project: meteor-pulumi-esc-demo
GCP Region: us-central1
MongoDB URI: [secret]
Outputs:
url: output
Resources:
+ 5 to create
Deploy
If everything looks good, deploy
pulumi up
log
ask@cs-278579150805-default:~/meteor-pulumi-esc/pulumi-infra$ pulumi up
Previewing update (dev)
View in Browser (Ctrl+O): https://app.pulumi.com/kafechew/meteor-pulumi-esc/dev/previews/2d8aefe9-94a5-406d-817f-31625
url: output
Resources:
+ 5 to create
Do you want to perform this update? yes
Updating (dev)
View in Browser (Ctrl+O): https://app.pulumi.com/kafechew/meteor-pulumi-esc/dev/updates/1
Type Name Status Info
+ pulumi:pulumi:Stack meteor-pulumi-esc-dev created (392s) 4 messages
+ ├─ gcp:artifactregistry:Repository meteor-app-repo created (15s)
+ ├─ docker:index:Image meteor-app-image created (254s)
+ ├─ gcp:cloudrun:Service meteor-app-service created (107s)
+ └─ gcp:cloudrun:IamMember meteor-app-everyone created (7s)
Diagnostics:
pulumi:pulumi:Stack (meteor-pulumi-esc-dev):
Using values from ESC environment
GCP Project: meteor-pulumi-esc-demo
GCP Region: us-central1
MongoDB URI: [secret]
Outputs:
url: "https://meteor-app-service-b378141-szyom6qpxq-uc.a.run.app"
Resources:
+ 5 created
Duration: 6m35s
This approach demonstrates how to use Pulumi ESC for secret management by:
- Storing secrets in Pulumi ESC
- Retrieving them using the ESC CLI
- Using them in our Pulumi program
Key Benefits of Using Pulumi ESC
- Security: Secrets are encrypted at rest and in transit
- Separation of concerns: Infrastructure code is separated from application secrets
- Programmatic access: Secrets can be accessed securely at runtime
- Environment management: Different environments can have different configurations
Pulumi Copilot
config set
vs esc set
Here's a comparison of using pulumi config set --secret
versus esc secret set
in a table format:
Feature/Aspect | pulumi config set --secret |
esc secret set |
---|---|---|
Scope | Specific to a Pulumi stack | Shared across multiple stacks and projects |
Command | pulumi config set |
esc secret set |
Encryption | Yes, encrypted and stored securely within the stack | Yes, encrypted and stored securely within the ESC |
Access | Accessed within the context of the stack | Accessed within the context of the ESC environment |
Management | Managed via Pulumi CLI | Managed via ESC CLI |
Visibility | Visible only to the specific stack | Visible to all stacks and projects within the ESC |
Use Case | Ideal for stack-specific secrets | Ideal for secrets that need to be shared across stacks |
Configuration | Stored in the stack's configuration | Stored in the ESC environment |
Integration | Directly integrated with Pulumi stack configurations | Requires ESC CLI for management |
Complexity | Simple and straightforward for stack-specific secrets | More complex, but offers broader scope and sharing |
Security Benefits
This approach provides several security benefits:
- Encryption: Secrets are encrypted at rest and in transit
- Masking: Secrets are masked in logs and command output
- Separation: Configuration is separated from code
- Access Control: Access to secrets can be controlled and audited
- No Hardcoding: Secrets are never hardcoded in our code
Best Practices
Here are some best practices we follow for secure configuration management with Pulumi ESC:
-
Use
--secret
for Sensitive Data: Always use the--secret
flag when storing sensitive data - Create Environment-Specific Environments: Use different environments for different deployment contexts
- Rotate Secrets Regularly: Regularly update secrets to minimize the impact of potential breaches
- Limit Access to Environments: Only grant access to environments to those who need it
- Document Your Configuration: Keep a record of what configuration values are needed and what they're for
Managing Environments
You can manage your environments with these commands:
- List environments:
esc env ls
- Get all values:
esc env get meteor-pulumi/dev
- Get a specific value:
esc env get meteor-pulumi/dev mongodb_uri
- Update a value:
esc env set meteor-pulumi/dev mongodb_uri 'new-value' --secret
- Delete a value:
esc env unset meteor-pulumi/dev mongodb_uri
Step-by-step Tutorial
This step-by-step tutorial will guide you through deploying a Meteor 3 app with BlazeJS to Google Cloud Run using Pulumi ESC for secret management (and Pulumi IAC in general). We'll build this project from scratch and publish it to GitHub.
Prerequisites
- A Google Cloud account
- A MongoDB Atlas account
- A GitHub account
- Basic familiarity with command line
Step 1: Create a Basic Meteor 3 App in Google Cloud Shell
Set Up Google Cloud Shell (Free!)
- Open Google Cloud Console
- Click on the Cloud Shell icon (>_) in the top right corner
- Wait for the shell to initialize
Create a New Project
# Create a new directory for your project
mkdir meteor-pulumi-esc
cd meteor-pulumi-esc
# Initialize git repository
git init
log
ask@cloudshell:~/meteor-pulumi-esc (meteor-pulumi-demo)$ git init
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint:
hint: git config --global init.defaultBranch
hint:
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint:
hint: git branch -m
Initialized empty Git repository in /home/ask/meteor-pulumi-esc/.git/
Create a Basic Meteor 3 App
Install Meteor in Cloud Shell
curl https://install.meteor.com/ | sh
log
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 7869 0 7869 0 0 8837 0 --:--:-- --:--:-- --:--:-- 8831
Downloading Meteor distribution
######################################################################## 100.0%
Meteor 3.1.2 has been installed in your home directory (~/.meteor).
Writing a launcher script to /usr/local/bin/meteor for your convenience.
This may prompt for your password.
To get started fast:
$ meteor create ~/my_cool_app
$ cd ~/my_cool_app
$ meteor
Or see the docs at:
docs.meteor.com
Deploy and host your app with Cloud:
www.meteor.com/cloud
Create a new Meteor app with Blaze
meteor create meteor-app --blaze --prototype
The command meteor create --prototype
creates a project with the prototype purpose packages (autopublish
and insecure
). If you use them, you can change your collections (MongoDB) quickly and create prototype apps very quickly.
log
Using blaze skeleton
Created a new Meteor app in 'meteor-app'.
To run your new app:
cd meteor-app
meteor --port 3001
If you are new to Meteor, try some of the learning resources here:
https://www.meteor.com/tutorials
When you’re ready to deploy and host your new Meteor application, check out Cloud:
https://www.meteor.com/cloud
Test the app locally
cd meteor-app
meteor --port 3001
log
[[[[[ ~/meteor-pulumi-esc/meteor-app ]]]]]
=> Started proxy.
=> Started HMR server.
=> Started MongoDB.
=> Started your app.
=> App running at: http://localhost:3001/
Near the Web Preview
, change port and preview, you will be redirected to a new tab
https://XXXXXXX.cs-asia-southeast1-ajrg.cloudshell.dev/?authuser=3
Press Ctrl+C to stop the app after verifying it works
Visit Creating a Minimal Reddit App with Meteor 3 and Blaze for step-by-step guide creating a simplified version of Reddit as a single-page application using Meteor 3 and Blaze.
Step 2: Create a Dockerfile
Go back to meteor-pulumi-esc
cd ..
Create a file named meteor-app/Dockerfile
cat > meteor-app/Dockerfile << 'EOF'
# Build stage
FROM geoffreybooth/meteor-base:3.1.2 as builder
# Copy package files first to leverage Docker cache
COPY package*.json /app/
RUN cd /app && meteor npm install
# Copy the rest of the app
COPY . /app
# Build the app
RUN cd /app && \
meteor build --directory /opt/bundle --server-only
# Production stage
FROM node:18-slim
# Copy the built app
COPY --from=builder /opt/bundle/bundle /app
# Install production dependencies
WORKDIR /app/programs/server
RUN npm install
# Set up environment
WORKDIR /app
# Note: We don't set PORT here as it's provided by Cloud Run
# ENV PORT=8080
ENV ROOT_URL=http://localhost:8080
ENV MONGO_URL=mongodb+srv://placeholder
# Expose the application port
EXPOSE 8080
# Start the app
CMD ["node", "main.js"]
EOF
Create a .dockerignore
file:
cat > meteor-app/.dockerignore << 'EOF'
.meteor/local
node_modules
EOF
Step 3: Set Up MongoDB Atlas
- Log in to MongoDB Atlas
- Create a new project (or use an existing one)
- Build a new cluster (you can use the free tier)
- Create a database user with read/write permissions
- Configure network access to allow connections from anywhere (for development)
- Get your connection string by clicking "Connect" > "Connect your application"
- Save this connection string; you'll need it later
mongodb URI: mongodb+srv://USERNAME:[email protected]/?retryWrites=true&w=majority&appName=Cluster0
Step 4: Setup Google Cloud Credentials
Create a new Google Cloud project
gcloud projects create meteor-pulumi-esc-demo --name="Meteor Pulumi ESC Demo"
log
Create in progress for [https://cloudresourcemanager.googleapis.com/v1/projects/meteor-pulumi-esc-demo].
Waiting for [operations/create_project.global.89599016493] to finish...done.
Enabling service [cloudapis.googleapis.com] on project [meteor-pulumi-esc-demo]...
Operation "operations/acat.p2-1099397202050-13791814-ea9b-4654-a8ce-6c0d" finished successfully.
Set it as your active project
gcloud config set project meteor-pulumi-esc-demo
log
Updated property [core/project].
list your billing accounts
gcloud billing accounts list
log
ACCOUNT_ID: XXXXXXXXXX
NAME: My Billing Account
OPEN: True
MASTER_ACCOUNT_ID:
Link a Billing Account to Your Project
gcloud billing projects link meteor-pulumi-esc-demo --billing-account=XXXXXXXXXX
log
billingAccountName: billingAccounts/XXXXXXXXXX
billingEnabled: true
name: projects/meteor-pulumi-esc-demo/billingInfo
projectId: meteor-pulumi-esc-demo
Enable required APIs
gcloud services enable cloudbuild.googleapis.com
gcloud services enable run.googleapis.com
gcloud services enable artifactregistry.googleapis.com
gcloud services enable containerregistry.googleapis.com
gcloud services enable compute.googleapis.com
log
Operation "operations/acf.p2-1099397202050-c6a722e0-3a46-48ab-9313-5f8" finished successfully.
Operation "operations/acf.p2-1099397202050-54fd31e7-9d94-496b-9a41-9b" finished successfully.
Operation "operations/acf.p2-1099397202050-cf7ac311-97f1-40d5-aa33-5a" finished successfully.
Create a service account
gcloud iam service-accounts create pulumi-deployer \
--description="Service account for Pulumi deployments" \
--display-name="Pulumi Deployer"
log
Created service account [pulumi-deployer].
Grant necessary permissions
gcloud projects add-iam-policy-binding meteor-pulumi-esc-demo \
--member="serviceAccount:[email protected]" \
--role="roles/editor"
gcloud projects add-iam-policy-binding meteor-pulumi-esc-demo \
--member="serviceAccount:[email protected]" \
--role="roles/run.admin"
gcloud projects add-iam-policy-binding meteor-pulumi-esc-demo \
--member="serviceAccount:[email protected]" \
--role="roles/iam.serviceAccountUser"
Create and download a key file
gcloud iam service-accounts keys create ~/pulumi-deployer-key.json \
--iam-account=pulumi-deployer@meteor-pulumi-esc-demo.iam.gserviceaccount.com
log
created key [e3d13cd] of type [json] as [/home/ask/pulumi-deployer-key.json] for [[email protected]]
Set the environment variable for Pulumi to use
export GOOGLE_APPLICATION_CREDENTIALS=~/pulumi-deployer-key.json
Step 5: Setup Pulumi Infrastructure Project and ESC
Install Pulumi CLI in Cloud Shell
# Install Pulumi
curl -fsSL https://get.pulumi.com | sh
log
=== Installing Pulumi 3.159.0 ===
+ Downloading https://github.com/pulumi/pulumi/releases/download/v3.159.0/pulumi-v3.159.0-linux-x64.tar.gz...
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
100 85.9M 100 85.9M 0 0 28.5M 0 0:00:03 0:00:03 --:--:-- 40.1M
+ Extracting to /home/ask/.pulumi/bin
+ Adding /home/ask/.pulumi/bin to $PATH in /home/ask/.bashrc
=== Pulumi is now installed! 🍹 ===
+ Please restart your shell or add /home/ask/.pulumi/bin to your $PATH
+ Get started with Pulumi: https://www.pulumi.com/docs/quickstart
Add Pulumi to your PATH
source ~/.bashrc
Create Pulumi Infrastructure Project
Create a directory for Pulumi infrastructure code
mkdir pulumi-infra
cd pulumi-infra
Initialize a new Pulumi project
pulumi new javascript
log
Manage your Pulumi stacks by logging in.
Run `pulumi login --help` for alternative login options.
Enter your access token from https://app.pulumi.com/account/tokens
or hit to log in using your browser :
Go to the link, create new token.
This is your new access token. Be sure to copy it, because you won't be able to see it again!
pul-YYYYYYYYYYYYYYYY
log
Manage your Pulumi stacks by logging in.
Run `pulumi login --help` for alternative login options.
Enter your access token from https://app.pulumi.com/account/tokens
or hit to log in using your browser :
Welcome to Pulumi!
Pulumi helps you create, deploy, and manage infrastructure on any cloud using
your favorite language. You can get started today with Pulumi at:
https://www.pulumi.com/docs/get-started/
Tip: Resources you create with Pulumi are given unique names (a randomly
generated suffix) by default. To learn more about auto-naming or customizing resource
names see https://www.pulumi.com/docs/intro/concepts/resources/#autonaming.
This command will walk you through creating a new Pulumi project.
Enter a value or leave blank to accept the (default), and press .
Press ^C at any time to quit.
- Project name: meteor-pulumi-esc
- Project description: Meteor app deployed to Cloud Run with Pulumi ESC
- Stack name: dev
- package manager: npm
log
Project name (pulumi-infra): meteor-pulumi-esc
Project description (A minimal JavaScript Pulumi program): Meteor app deployed to Cloud Run with Pulumi ESC
Created project 'meteor-pulumi-esc'
Please enter your desired stack name.
To create a stack in an organization, use the format / (e.g. `acmecorp/dev`).
Stack name (dev): dev
Created stack 'dev'
The package manager to use for installing dependencies npm
Installing dependencies...
added 293 packages, and audited 294 packages in 29s
43 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
Finished installing dependencies
Your new project is ready to go!
To perform an initial deployment, run `pulumi up`
Set Up Pulumi ESC
npm install @pulumi/gcp @pulumi/docker @pulumi/esc-sdk
log
npm warn deprecated [email protected]: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
npm warn deprecated [email protected]: This package is no longer supported. Please use @npmcli/package-json instead.
npm warn deprecated [email protected]: Glob versions prior to v9 are no longer supported
added 69 packages, and audited 363 packages in 11s
49 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
Install the Pulumi ESC CLI
curl -fsSL https://get.pulumi.com/esc/install.sh | sh
Pulumi auth
Make sure you're logged in to Pulumi
esc login
log
Logged in to pulumi.com as kafechew (https://app.pulumi.com/kafechew)
Initialize Pulumi ESC
esc env init meteor-pulumi/dev
log
Environment created: kafechew/meteor-pulumi/dev
Set the mongodb_uri secret:
esc env set meteor-pulumi/dev mongodb_uri 'mongodb+srv://USERNAME:[email protected]/?retryWrites=true&w=majority&appName=Cluster0' --secret
Let's set your GCP project and region in the ESC environment
# Set your GCP project
esc env set meteor-pulumi/dev gcp_project 'meteor-pulumi-esc-demo'
# Set your GCP region
esc env set meteor-pulumi/dev gcp_region 'us-central1'
Verify ESC
You can verify your environment configuration
esc env get meteor-pulumi/dev
log
Value
{
"gcp_project": "meteor-pulumi-esc-demo",
"gcp_region": "us-central1",
"mongodb_uri": "[secret]"
}
Definition
values:
mongodb_uri:
fn::secret:
ciphertext: ZXNjeAAAAAEAAAEAA9JEMGnQuWoTJsX9+pBIXo94YgEBV+ZVv+SsQsRiROyHDjNTLKeB50BkrRKAY51z8XLIr1/WDHIb3rxI3R0oF5QvVwtxzRdXVRJEAZg8HR2/+UWzeX7mPunes4kIVcLE3Wbs8+sVNibrGg2uoBShN5w0C/xn5C+aqaEEMFUpZ+K
gcp_project: meteor-pulumi-esc-demo
gcp_region: us-central1
Step 6: Deploy Your Application with pulumi up
We use a bridge approach to access ESC values via using the esc env open
command directly, which should give us all the values
cat > get-esc-values-simple.js << 'EOF'
const { execSync } = require('child_process');
const fs = require('fs');
try {
// Get all values in JSON format
const output = execSync(`esc env open meteor-pulumi/dev`, { encoding: 'utf8' });
try {
// Parse the JSON output
const values = JSON.parse(output);
// Write values to a file that can be imported by Pulumi
fs.writeFileSync('esc-values.json', JSON.stringify(values, null, 2));
console.log('ESC values written to esc-values.json');
} catch (error) {
console.error(`Error parsing JSON output:`, error.message);
console.error(`Output:`, output);
}
} catch (error) {
console.error(`Error getting ESC values:`, error.message);
}
EOF
# Run the script to get ESC values
node get-esc-values-simple.js
We pass the secret to our Cloud Run service as an environment variable. Replace the content of index.js
to use the Pulumi ESC SDK
cat > index.js << 'EOF'
const pulumi = require("@pulumi/pulumi");
const gcp = require("@pulumi/gcp");
const docker = require("@pulumi/docker");
const fs = require("fs");
// Try to load ESC values from file
let mongodbUri, gcpProject, gcpRegion;
try {
const escValues = JSON.parse(fs.readFileSync('esc-values.json', 'utf8'));
mongodbUri = pulumi.secret(escValues.mongodb_uri);
gcpProject = escValues.gcp_project;
gcpRegion = escValues.gcp_region;
console.log("Using values from ESC environment");
} catch (error) {
console.error("Error loading ESC values:", error);
console.log("Falling back to standard Pulumi config");
// Fall back to standard Pulumi config
const config = new pulumi.Config();
mongodbUri = config.requireSecret("mongodb_uri");
const gcpConfig = new pulumi.Config("gcp");
gcpProject = gcpConfig.require("project");
gcpRegion = gcpConfig.require("region");
}
console.log(`GCP Project: ${gcpProject}`);
console.log(`GCP Region: ${gcpRegion}`);
console.log("MongoDB URI: [secret]");
// Create a GCP Artifact Registry repository
const repo = new gcp.artifactregistry.Repository("meteor-app-repo", {
format: "DOCKER",
location: gcpRegion,
repositoryId: "meteor-app-repo",
project: gcpProject,
});
// Build and push the Docker image
const imageName = pulumi.interpolate`${gcpRegion}-docker.pkg.dev/${gcpProject}/${repo.repositoryId}/meteor-app:latest`;
const image = new docker.Image("meteor-app-image", {
imageName: imageName,
build: {
context: "../meteor-app",
dockerfile: "../meteor-app/Dockerfile",
platform: "linux/amd64", // Explicitly set platform
},
registry: {
server: pulumi.interpolate`${gcpRegion}-docker.pkg.dev`,
username: "_json_key",
password: pulumi.secret(process.env.GOOGLE_APPLICATION_CREDENTIALS
? fs.readFileSync(process.env.GOOGLE_APPLICATION_CREDENTIALS).toString()
: ""),
},
});
// Define a service URL pattern
const serviceUrlPattern = pulumi.interpolate`meteor-app-${gcpProject}.${gcpRegion}.run.app`;
// Deploy the image to Cloud Run
const service = new gcp.cloudrun.Service("meteor-app-service", {
location: gcpRegion,
project: gcpProject,
template: {
spec: {
containers: [{
image: image.imageName,
resources: {
limits: {
memory: "1Gi",
cpu: "1",
},
},
envs: [
{
name: "ROOT_URL",
value: pulumi.interpolate`https://${serviceUrlPattern}`,
},
{
name: "MONGO_URL",
value: mongodbUri,
},
],
}],
containerConcurrency: 80,
},
},
});
// Make the Cloud Run service publicly accessible
const iamMember = new gcp.cloudrun.IamMember("meteor-app-everyone", {
service: service.name,
location: gcpRegion,
project: gcpProject,
role: "roles/run.invoker",
member: "allUsers",
});
// Export the service URL
exports.url = service.statuses[0].url;
EOF
Make sure again you have the GCP credentials set
export GOOGLE_APPLICATION_CREDENTIALS=~/pulumi-deployer-key.json
Set Up a Pre-Deployment Script
Let's create a script to run before each deployment to update the ESC values:
cat > pre-deploy.sh << 'EOF'
#!/bin/bash
set -e
echo "Fetching ESC values..."
node get-esc-values-simple.js
echo "ESC values updated. Ready for deployment."
EOF
chmod +x pre-deploy.sh
# Run the pre-deployment script
./pre-deploy.sh
# Make sure you have the GCP credentials set
export GOOGLE_APPLICATION_CREDENTIALS=~/pulumi-deployer-key.json
log
ask@cs-278579150805-default:~/meteor-pulumi-esc/pulumi-infra$ ./pre-deploy.sh
Fetching ESC values...
ESC values written to esc-values.json
ESC values updated. Ready for deployment.
ask@cs-278579150805-default:~/meteor-pulumi-esc/pulumi-infra$ export GOOGLE_APPLICATION_CREDENTIALS=~/pulumi-deployer-key.json
Preview the deployment
pulumi preview
log
Previewing update (dev)
View in Browser (Ctrl+O): https://app.pulumi.com/kafechew/meteor-pulumi-esc/dev/previews/d026ba11-fdc3-421a-914f-246887355da9
Type Name Plan Info
+ pulumi:pulumi:Stack meteor-pulumi-esc-dev create 4 messages
+ ├─ gcp:artifactregistry:Repository meteor-app-repo create
+ ├─ docker:index:Image meteor-app-image create
+ ├─ gcp:cloudrun:Service meteor-app-service create
+ └─ gcp:cloudrun:IamMember meteor-app-everyone create
Diagnostics:
pulumi:pulumi:Stack (meteor-pulumi-esc-dev):
Using values from ESC environment
GCP Project: meteor-pulumi-esc-demo
GCP Region: us-central1
MongoDB URI: [secret]
Outputs:
url: output
Resources:
+ 5 to create
Deploy!
If everything looks good, deploy
pulumi up
log
ask@cs-278579150805-default:~/meteor-pulumi-esc/pulumi-infra$ pulumi up
Previewing update (dev)
View in Browser (Ctrl+O): https://app.pulumi.com/kafechew/meteor-pulumi-esc/dev/previews/2d8aefe9-94a5-406d-817f-31625
url: output
Resources:
+ 5 to create
Do you want to perform this update? yes
Updating (dev)
View in Browser (Ctrl+O): https://app.pulumi.com/kafechew/meteor-pulumi-esc/dev/updates/1
Type Name Status Info
+ pulumi:pulumi:Stack meteor-pulumi-esc-dev created (392s) 4 messages
+ ├─ gcp:artifactregistry:Repository meteor-app-repo created (15s)
+ ├─ docker:index:Image meteor-app-image created (254s)
+ ├─ gcp:cloudrun:Service meteor-app-service created (107s)
+ └─ gcp:cloudrun:IamMember meteor-app-everyone created (7s)
Diagnostics:
pulumi:pulumi:Stack (meteor-pulumi-esc-dev):
Using values from ESC environment
GCP Project: meteor-pulumi-esc-demo
GCP Region: us-central1
MongoDB URI: [secret]
Outputs:
url: "https://meteor-app-service-b378141-szyom6qpxq-uc.a.run.app"
Resources:
+ 5 created
Duration: 6m35s
Live Demo: https://meteor-app-service-b378141-szyom6qpxq-uc.a.run.app
Step 7: Push to Github Repo Publicly
Go back to the project root
cd ..
Set your Git identity
git config --global user.email "[email protected]"
git config --global user.name "Kai Chew"
Verify your Git configuration
git config --global --list
Create a .gitignore file
cat > .gitignore << 'EOF'
node_modules
.pulumi
Pulumi.*.yaml
.DS_Store
meteor-app/.meteor/local
EOF
Add files to git
git add .
git commit -m "Initial commit: Meteor app deployed to Cloud Run with Pulumi ESC"
Github Auth
gh auth login
use Personal access tokens (classic), not fine-grained, repository-scoped token!
The minimum required scopes are 'repo', 'read:org', 'workflow'. Make sure to copy your personal access token now as you will not be able to see this again.
ghp_ZZZZZZZZZZZZZ
log
ask@cs-278579150805-default:~/meteor-pulumi-esc$ gh auth login
? Where do you use GitHub? GitHub.com
? What is your preferred protocol for Git operations on this host? HTTPS
? Authenticate Git with your GitHub credentials? Yes
? How would you like to authenticate GitHub CLI? Paste an authentication token
Tip: you can generate a Personal Access Token here https://github.com/settings/tokens
The minimum required scopes are 'repo', 'read:org', 'workflow'.
? Paste your authentication token: *********************************************************************************************
- gh config set -h github.com git_protocol https
✓ Configured git protocol
✓ Logged in as kafechew
Create repo & push
gh repo create Mooptcom/meteor-pulumi-esc --public --description "Meteor app deployed to Cloud Run with Pulumi ESC" --source=. --push
log
✓ Created repository Mooptcom/meteor-pulumi-esc on GitHub
https://github.com/Mooptcom/meteor-pulumi-esc
✓ Added remote https://github.com/Mooptcom/meteor-pulumi-esc.git
Enumerating objects: 33, done.
Counting objects: 100% (33/33), done.
Delta compression using up to 4 threads
Compressing objects: 100% (25/25), done.
Writing objects: 100% (33/33), 8.16 KiB | 1.02 MiB/s, done.
Total 33 (delta 2), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (2/2), done.
To https://github.com/Mooptcom/meteor-pulumi-esc.git
* [new branch] HEAD -> master
branch 'master' set up to track 'origin/master'.
✓ Pushed commits to https://github.com/Mooptcom/meteor-pulumi-esc.git
Mooptcom
/
meteor-pulumi-esc
Meteor app deployed to Cloud Run with Pulumi ESC
Meteor App Deployment with Pulumi ESC
This repository demonstrates how to securely deploy a Meteor 3 application to Google Cloud Run using Pulumi ESC for secret management. The application connects to a MongoDB Atlas database, with the connection string securely stored and accessed using Pulumi ESC.
This project was created for the Pulumi Deploy and Document Challenge: Shhh, It's a Secret!
-
Real-time Demo: https://meteor-app-service-b378141-szyom6qpxq-uc.a.run.app or https://meteor-app-service-b378141-1099397202050.us-central1.run.app/
-
Demo video: https://youtu.be/V2-wESswkV8 and https://www.youtube.com/watch?v=O5kcxLiycJM
🌟 Features
- Secure Secret Management: Using Pulumi ESC to securely store and access MongoDB connection strings
- Containerized Meteor App: Multi-stage Docker build optimized for Meteor 3 applications
- Serverless Deployment: Automatic deployment to Google Cloud Run
- Infrastructure as Code: Complete infrastructure defined using Pulumi
- Voting Application: Simple Meteor application with MongoDB integration
🏗️ Architecture
The architecture consists of:
- Meteor Application: A Meteor 3 app with BlazeJS and a todos feature
- MongoDB Atlas…
Conclusion: Revolutionizing Cloud Deployments with Pulumi ESC
The Power of Secure Configuration Management
In this comprehensive guide, we've demonstrated how Pulumi ESC transforms the deployment of cloud applications by providing a robust, secure framework for managing sensitive configuration data. Unlike traditional approaches that risk exposing credentials in code or configuration files, our implementation of Pulumi ESC for deploying a Meteor 3 application to Google Cloud Run represents a significant advancement in DevSecOps practices.
Beyond Basic Secret Management
While other solutions merely scratch the surface of secret management, our approach dives deeper by creating a complete end-to-end workflow that addresses real-world challenges. We've shown how Pulumi ESC doesn't just store secrets—it fundamentally changes how developers interact with sensitive data throughout the entire application lifecycle.
The integration between Meteor 3, MongoDB Atlas, and Google Cloud Run showcases Pulumi ESC's versatility across different cloud providers and application frameworks. This isn't just about storing a connection string; it's about establishing a secure pipeline where sensitive data never appears in plaintext at any stage of development or deployment.
Practical Implementation with Real-World Considerations
Our guide stands out by addressing the practical challenges developers face when implementing secure configuration management:
- Bridging the ESC-Pulumi Gap: We developed an innovative approach to bridge between ESC environments and Pulumi deployments, ensuring seamless integration even as the tooling evolves.
- Multi-Stage Docker Optimization: Our implementation leverages multi-stage Docker builds specifically optimized for Meteor applications, reducing attack surfaces while maintaining efficient deployments.
- Comprehensive Error Handling: Unlike simplistic examples, our solution includes robust error handling and fallback mechanisms, ensuring reliability in production environments.
- Cross-Platform Compatibility: The solution works seamlessly across different development environments—from local machines to Cloud Shell—providing consistency regardless of where deployment occurs.
Beyond Theory: A Production-Ready Solution
What truly sets our approach apart is its production-readiness. We've incorporated:
- Automated Pre-Deployment Workflows: Custom scripts that automatically synchronize ESC values before deployment, eliminating manual steps and potential errors.
- Version-Controlled Infrastructure: Complete integration with Git workflows, allowing teams to collaborate on infrastructure while keeping secrets secure.
- Transparent Debugging Paths: Clear logging and troubleshooting strategies that make it easy to identify and resolve issues without compromising security.
- Scalable Environment Management: A foundation that easily extends to multiple environments (development, staging, production) with appropriate inheritance and overrides.
The Future of Secure Cloud Deployments
This guide represents more than just a tutorial—it's a blueprint for the future of secure cloud deployments. By combining Pulumi ESC with modern application frameworks and cloud services, we've demonstrated a path forward that balances security, developer experience, and operational efficiency.
The approach we've outlined doesn't just solve today's problems; it establishes patterns that will remain relevant as cloud technologies evolve. Whether you're deploying a simple web application or a complex microservices architecture, the principles of secure configuration management using Pulumi ESC provide a foundation for building more secure, maintainable cloud infrastructure.
In an era where data breaches and security vulnerabilities make headlines daily, implementing proper secret management isn't optional—it's essential. Our guide provides not just the theory but the practical implementation details that empower development teams to embrace security as a fundamental aspect of their cloud deployment strategy.
By following this approach, organizations can significantly reduce security risks, improve compliance posture, and accelerate deployment cycles—all while giving developers the tools they need to work efficiently with sensitive configuration data.