Hey Reader ππ½
We've been talking a lot about how great SST's switch to Pulumi was, and many of you have asked us how to use plain Pulumi directly.
So today, we're sharing our quick guide to Pulumi - a tool we're really excited about since it lets us build infrastructure with languages we already know and love. No more learning weird syntax - just TypeScript, Python, or whatever we're comfortable with!
We spent the last few days playing with it, and here's what we've learned...
AWS Community Day
Prague 2025
π¨πΏ
|
Join us at AWS Community Day Prague 2025, a friendly gathering of AWS enthusiasts and experts for a day of learning and fun.
|
We are both presenting two different talks on that day!
|
Don't miss our talks:
β’ |
π Sandro: "EDA for the real world"
Learn best practices from building an event-driven architecture in a startup. From 0 to 1 million events a month
|
β’ |
π Tobias: "Considerations for AWS-native observability before jumping into a >$100k/yr 3rd party tool"
Discover cost-effective observability strategies using AWS native solutions
|
|
Why come?
β’ |
π₯ Meet other AWS fans in a relaxed atmosphere |
β’ |
π‘ Learn practical tips you can use right away |
β’ |
π» Enjoy Prague and hang out with the community |
|
"Come for the AWS knowledge, stay for the community!" β¨
|
β€οΈ Not sponsored β we're sharing this to support the amazing organizers of AWS Community Day Prague.
|
Want to sponsor our newsletter? Contact us for sponsorship opportunities!
|
|
Introduction
Pulumi is a modern Infrastructure as Code platform. Like other tools (e.g., Terraform, CloudFormation, CDK), you can define your whole infrastructure in code.
One of the main differences is that Pulumi is not bound to AWS only. You can also deploy infrastructure to Azure, GCP, Cloudflare, etc.
β
Furthermore, Pulumi allows you to use your favorite programming language. This means you can use Python, TypeScript, Go, JavaScript, and more.
In this article, we will look at how to get started with Pulumi. At the end, you'll know everything you're set up to start building your own infrastructure.
Key Concepts
Like Terraform, Pulumi is a declarative tool. This means we'll describe our desired state of our infrastructure. Pulumi will then take care of creating this state through actions.
β
On the host, we'll write our code in a programming language of our choice. Pulumi will then compute this into a plan, which is a list of actions that need to be taken to reach the desired state.
This means it will calculate which resources need to be
- created,
- updated,
- or deleted.
The state is later stored in a stack. This stack can live in a remote or local directory.
For serious projects, it's a requirement to use a remote stack, as otherwise, collaboration is not possible.
Projects
For Pulumi, every folder that contains a Pulumi.yaml
file is considered a project. If there's no Pulumi project yet, you can create one by running pulumi new
.
As we've learned, the supported runtimes are nodejs
, python
, dotnet
, go
, java
and yaml
.
The project file itself is a simple, short YAML file:
When using NodeJS, by default, Pulumi will use TypeScript. You can change this by setting options.typescript
to false
.
With NodeJS-based projects, we'll also need a package.json
file. This way, we can install dependencies that are needed for our project.
Also worth noticing, everything you refer to on the local filesystem needs to be relative to the project's root.
Stacks
Stacks are a way to manage different environments. For example, we can have a dev
and a prod
stack.
Each stack can have its own configuration. When we create a new stack, Pulumi will also create a Pulumi.$STACK_NAME.yaml
file.
This file can contain any configuration that we want to use for this stack.
You can set the configuration properties via the CLI or directly in the YAML file.
Inside your infrastructure file, e.g., your index.ts.
In the case of a NodeJS project, you can access your configuration properties and your stack name at any time.
The config object also has other methods, like requireObject
which allow us to require a configuration object. If it's not set, it will throw an error.
Resources
Resources are the main building blocks in Pulumi. They make up your infrastructure.
In case of AWS, a resource is a single AWS resource, like a VPC, a security group, an EC2 instance, etc.
There are two subclasses of resources:
- CustomResource: a cloud resource managed by a resource provider, e.g., AWS.
- ComponentResource: a resource that is composed of other resources, e.g., a VPC and an EC2 instance. These are the higher building blocks that are used to create more complex infrastructure via composition.
Higher building blocks are also called components. They are very useful when we want to create reusable infrastructure.
In the example above, we're having a component that is composed of resources needed to run a containerized application in Fargate.
Inputs and Outputs
Now to one of the most important concepts in Pulumi: inputs and outputs.
All resources that Pulumi accepts are called inputs. For example, the aws.lambda.Function
resource accepts inputs like name
, handler
, runtime
, etc.
Pulumi will later translate this into a plan and map the inputs to the API call's parameters.
Outputs are the other way around. They are the values that are returned by a resource.
For example, the aws.lambda.Function
resource returns an arn
or name
output.
The output is calculated when the resource is created. Pulumi can't know it beforehand, as it's a runtime value.
Nevertheless, we can still access it via the arn
output property.
Now, here comes the magic of Pulumi: We can also use this output as an input for other resources.
This way, we can create a log group for our Lambda function. We don't need to hardcode the name of the log group, as it's dynamically created by Pulumi.
If we change the name of the Lambda function, the log group would automatically be renamed to match the new Lambda function's name.
If you've ever worked with CDK or CloudFormation you know how big of a hassle this can be. Pulumi makes this one super easy!
Secrets
Secrets are a way to manage sensitive information, like passwords, API keys, etc. This way, you don't need to work with plain text secrets in your Pulumi configuration files.
With the CLI, we can easily encrypt secrets and push them to our configuration files.
This will encrypt the secret and save it to the configuration file.
By default, Pulumi will use a key that is managed by the Pulumi Cloud. Alternatively, you can also use other key management systems, like AWS KMS, Azure Key Vault, and Google Cloud KMS. This way, the keys are managed by you or your organization.
Inside our code, we can access the secret via the Config
object.
The great thing here: Pulumi will never print the secret value to the console. Even if we decide to explicitly print the secret value, it will be masked.
State Management and Backends
When Pulumi creates a resource for you, it needs to store metadata so it knows what it created. This metadata is stored in the state file.
This state file should live in a remote backend; otherwise, collaboration will be a pain.
Luckily, Pulumi is very flexible when it comes to state management.
We can choose where to store the state file. Options include but are not limited to:
- AWS S3
- Microsoft Azure Blob Storage
- Google Cloud Storage
Alternatively, you can also use the Pulumi Cloud as a backend.
When you decide to use a remote backend, you always need to log in before using the CLI.
Hands-On
Now that we know the key concepts, let's bring them to life.
What we'll do:
- Configuring Pulumi so that it's able to deploy to AWS.
- Creating a new project and stack.
- Provisioning a simple S3 bucket via the Pulumi starter template.
- Deploying everything.
- Destroying the stack and cleaning up.
Prerequisites and Installation
Before we start, we need to install Pulumi.
- π macOS: We can simply install Pulumi via Homebrew.
- π§ Linux: We can use curl to run the installation script for the latest version via
curl -fsSL https://get.pulumi.com | sh
.
- πͺ Windows: by using PowerShell, we can use chocolatey to install Pulumi via
choco install pulumi
.
Choosing our Programming Language
As mentioned earlier, Pulumi doesn't come with its own language. Instead, we can use our favorite programming language.
Supported languages are:
- Python
- TypeScript
- Go
- JavaScript
- C#
- Java
You can also make use of YAML, but I don't recommend it.
In this guide, we'll use TypeScript.
Setting up our AWS Access
We'll now install the AWS CLI. When using macOS, you can use homebrew via brew install awscli
to get the latest version. For Linux and Windows, there's a similar process.
Configuration of our Credentials
You need to be authenticated and authorized to access your account via the AWS CLI. This requires you to have an Access Key ID and Secret Access Key.
Let's head over to your user's security credentials settings so we can create a pair of those if you haven't done this already.
Security Best Practice: While using long-term access keys is convenient for this tutorial, it's recommended to use temporary session tokens with Multi-Factor Authentication (MFA) for enhanced security in production environments. Learn more in our guide on
AWS Authentication Best Practices.
We'll select the Command Line Interface (CLI). The AWS console may note that it's recommended to make use of the IAM Identity Center to access the AWS CLI.
Make sure to save your Secret Access Key, as you can't display it again.
Now we can jump back into our terminal and run aws configure
, which will prompt us for our previously created Access Key and Secret Access Key. It will also ask for a default region, e.g., us-east-1, and the default output format, e.g. json
.
If you've set up everything properly, you should be able to get a few details of your account and user by running aws sts get-caller-identity
.
Now we're ready to start building!
Creating a New Project and Stack
We'll store our state in an Amazon S3 bucket. Let's create that bucket first
Now, we can already log in to Pulumi.
Now, we can create a new project by using the pulumi new
command and using the aws-typescript
template.
β
You'll be asked a lot of questions while going through the process, including:
- The name of the project
- Project description
- Stack name
- Phrase to protect the secrets
- Your desired package manager
- The AWS region to deploy to
Afterward, you'll see a success message like this:
β
Our project is now ready to go! π
Let's have a look at our index.ts
file.
It's a very minimalistic example, but it's enough to get us started.
Now, we can already perform an initial deployment with pulumi up.
Pulumi will now translate our TypeScript code into a plan.
As we can see, Pulumi detected that we want to create a new S3 bucket. We can now decide whether we want to apply this plan or not (this can also be skipped by using the --yes
flag).
Let's confirm the plan and apply it.
Now, we can head over to our S3 bucket and see that it's been created.
Now, we can destroy the stack and clean up with pulumi destroy.
β
That's it already! π
You've learned the key concepts of Pulumi and how to create a new project and stack.
Summary
That's it for today! We hope this quick intro to Pulumi gives you a taste of how simple infrastructure-as-code can be when you use your favorite programming language.
What do you think about Pulumi? Hit reply and let us know if you're already using it or planning to try it out. We'd love to hear your experiences!
And if you're anywhere near Prague this week, come hang out with us at AWS Community Day - first round's on us! π»
Happy building,
Sandro & Tobias
β
β