The world of Infrastructure-as-Code (IaC) has evolved by leaps and bounds. With a growing number of IaC tools available, DevOps teams often find themselves weighing the pros and cons of popular choices that pits Pulumi vs Terraform and others.
In this blog, I’ll take a deep dive into the features, similarities, differences, and real-world use cases of Terraform and Pulumi. Specifically, I’m going to focus on the following:
Comparison of Pulumi and Terraform: I’ll cover features, language flexibility, and community support.
Languages: Terraform utilizes its own HCL while Pulumi supports multiple common programming languages. I will review how these differences in approach to state management can make one more suitable than the other.
Real-world examples: I’ll demonstrate how both tools can be used for managing cloud infrastructure across multiple platforms securely.
So, buckle up as we explore the fascinating world of Pulumi and Terraform.
My Setup
A GitHub account: I’ll use GitHub Codespaces with all necessary tools installed for you.
For posterity, remember that Infrastructure-as-Code provisions infra resources using preset configuration files instead of manually.
The beauty of IaC lies in its ability to treat infrastructure like code, allowing us to manage it with the same processes, tools, and programming languages as application code.
This enables us to leverage software development practices like version control, testing, and automation throughout the infrastructure provisioning lifecycle, making it easier to allocate infrastructure resources efficiently.
Pulumi vs. Terraform
Terraform and Pulumi are key players in the IaC arena, each offering unique features for infrastructure automation. Terraform shines in its broad compatibility with cloud providers such as AWS, Azure, and GCP.
The use of its domain-specific language, Hashicorp Configuration Language (HCL), lets you adopt software engineering practices in managing infrastructure with Terraform. It operates on a declarative model, focusing on what the end state of the infrastructure should look like.
Pulumi, on the other hand, aims to deliver a developer-centric experience by offering support for multiple languages including TypeScript, Go, .NET, Python, and Java. Its architecture is equipped with essentials such as a language host, command-line interface, and state management capabilities.
While Terraform specializes in a focused, declarative approach with its HCL, Pulumi offers broader language support and flexibility, making each tool uniquely suited for various cloud computing tasks and environments.
Key Features Comparison
Terraform stands out with a broad set of features such as its declarative approach, extensive platform support, and a wealth of community modules.
It allows users to express infrastructure requirements in a simple yet powerful way and integrates with a diverse selection of plugins and tools created by the community.
Meanwhile, with its user-friendly interface, extensive integrations, and native provider support, Pulumi becomes a compelling choice for developers working with Infrastructure-as-Code.
One of Pulumi’s standout features is its Dynamic Provider Support, which enables the tool to create Terraform providers and support new resources and features at a much faster pace than Terraform.
This allows Pulumi to stay up-to-date with the latest cloud or SaaS features and resources, ensuring developers can access cutting-edge tools and technologies.
Compatible with Terraform providers and has its own Pulumi providers
Large ecosystem of Terraform providers
State Management
Pulumi Cloud hosts state by default, with option to move hosting to another cloud service or manage manually
State is manually managed manually JSON state files (terraform.tfstate)
State Encryption
Encrypted by default
Unencrypted by default (premium feature)
Testing
Unit, property, and integration testing, also compatible with external testing framework
Integration testing, new testing feature as of v1.6.0
Integration
Native integration for the available config languages
Third-party scripts
Pros & Cons
Another way to look at the two tools is to pit their pros and cons against each other. I do that here in this chart, that dives a little deeper into the contrasts of the previous section.
Pulumi
Terraform
Pros
Multilingual: Can you standard programming language without learning a new DSL
Strong Typing: Fewer mistakes, better IDE support
Dynamic Logic: Loops, conditionals in config
Rich Outputs: Detailed CLI diffs
HCL: Dynamically typed DSL that is well known among domain-specific languages
Well-Established: Larger community and more tools
State Management: Multiple backends
Extensible: Provider-based
Cons
Learning Curve: Tough if used to HCL
State Management: SaaS backend limitations
Community: Smaller but growing
Language: HCL is less expressive
Complexity: Advanced features tricky
Error Messages: Can be cryptic
Concurrency: Locking not flexible
When to Choose Terraform
Well-Established Infrastructure: When you're working with traditional VMs, networking configurations, and databases, Terraform has a strong track record here.
Declarative Code: If you prefer infrastructure to be declared in a domain-specific language designed solely for that purpose, Terraform's HCL is your friend.
Multi-Cloud & Provider Ecosystem: Massive selection of providers and modules. Great if you have a heterogeneous environment with multiple clouds or SaaS services.
Community and Resources: Abundance of tutorials, courses, and third-party tools. Also, a very active community.
When to Choose Pulumi
Full Programming Languages: If you want the expressiveness and power of full-fledged languages like Python, TypeScript, or Go, Pulumi lets you code away.
Application-Oriented: Better suited for modern, container-based, or serverless architectures. You can even deploy your app code along with your infrastructure.
Dynamic Configuration: Need complex logic, loops, or conditionals? Pulumi's programming language support makes this a breeze.
Strong Typing and IDE Support: With general-purpose languages, you get the benefits of strong typing and excellent IDE support for auto-completion, error-checking, etc.
Integrated Config Management: Manage your secrets and configs in the same language as your infrastructure code, without requiring separate tools.
Open-Source: If your organization prefers open-source tools for compliance or philosophy, HashiCorp's latest decision to change Terraform's license to BSL might deter you. Pulumi would be a better option.
In specific scenarios, such as multi-cloud infrastructure management, both Terraform and Pulumi offer valuable solutions for uniformly managing resources across different environments.
Licensing and How It May Affect Your Terraform vs. Pulumi Decision
License Flexibility: If you're comfortable with Apache 2.0, Pulumi SDK offers a pretty flexible license for most use cases.
Commercial Features: For enterprise-grade features, you'd likely end up in a paid plan for both Terraform and Pulumi, depending on your needs.
Open-Source Commitment: If an OSI-approved license is a strict requirement, Pulumi SDK meets that criterion.
Let's now compare Pulumi and Terraform with this simple example to create an S3 bucket in AWS.
Now all I need to do is run the following commands:
# Set AWS Access Key ID as an environment variable to authenticate with AWS.# Replace 'your-access-key-id' with your actual AWS Access Key ID.export AWS_ACCESS_KEY_ID=your-access-key-id
# Set AWS Secret Access Key as an environment variable for AWS authentication.# Replace 'your-secret-access-key' with your actual AWS Secret Access Key.export AWS_SECRET_ACCESS_KEY=your-secret-access-key
# Initialize Terraform project. This sets up the backend, downloads necessary providers, etc.
terraform init
# Run a Terraform plan to preview the changes that will be made to the infrastructure.# This step is a dry-run that shows what will happen when you actually apply the changes.
terraform plan
# Apply the planned changes to the infrastructure.# This step will actually create, update, or delete resources to match the configuration.# It will show a preview of these actions and ask for confirmation before proceeding.
terraform apply
and here is the output of running Terraform:
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_s3_bucket.terraform-bucket will be created
+ resource "aws_s3_bucket""terraform-bucket" {
+ acceleration_status = (known after apply)
+ acl = (known after apply)
+ arn = (known after apply)
+ bucket = "env0-terraform-example"
+ bucket_domain_name = (known after apply)
+ bucket_prefix = (known after apply)
+ bucket_regional_domain_name = (known after apply)
+ force_destroy = false
+ hosted_zone_id = (known after apply)
+ id = (known after apply)
+ object_lock_enabled = (known after apply)
+ policy = (known after apply)
+ region = (known after apply)
+ request_payer = (known after apply)
+ tags = {
+ "Environment" = "Dev"
+ "Name" = "My env0 Terraform Example Bucket"
}
+ tags_all = {
+ "Environment" = "Dev"
+ "Name" = "My env0 Terraform Example Bucket"
}
+ website_domain = (known after apply)
+ website_endpoint = (known after apply)
}
# aws_s3_bucket_acl.terraform-bucket will be created
+ resource "aws_s3_bucket_acl""terraform-bucket" {
+ acl = "private"
+ bucket = (known after apply)
+ id = (known after apply)
}
# aws_s3_bucket_ownership_controls.terraform-bucket will be created
+ resource "aws_s3_bucket_ownership_controls""terraform-bucket" {
+ bucket = (known after apply)
+ id = (known after apply)
+ rule {
+ object_ownership = "BucketOwnerPreferred"
}
}
Plan: 3 to add, 0 to change, 0 to destroy.aws_s3_bucket.terraform-bucket: Creating...aws_s3_bucket.terraform-bucket: Still creating... [10s elapsed]aws_s3_bucket.terraform-bucket: Creation complete after 16s [id=env0-terraform-example]aws_s3_bucket_ownership_controls.terraform-bucket: Creating...aws_s3_bucket_ownership_controls.terraform-bucket: Creation complete after 1s [id=env0-terraform-example]aws_s3_bucket_acl.terraform-bucket: Creating...aws_s3_bucket_acl.terraform-bucket: Creation complete after 0s [id=env0-terraform-example,private]
Cleanup with Terraform
To go ahead and clean up, run terraform destroy.
Pulumi AWS S3 Bucket Example
Since you can use different programming languages with Pulumi, I'm most comfortable with Python, so we'll go with that.
To get started with Pulumi, I can use the command pulumi new. Here's what happens when you run pulumi new:
Template Selection: Pulumi will first ask you to pick a template – AWS Python stack, an Azure TypeScript stack, or one of a number of others. You can even use your own custom template if you have one. For this tutorial, I’m using AWS Python.
Project Metadata: You'll be prompted to fill in some metadata for your new project, like the project name, stack name, and sometimes config values that the template requires. This sets up the pulumi.yaml and pulumi.[stack-name].yaml files.
File Generation: Pulumi will generate a set of files based on the template you chose. These usually include a pulumi.yaml file for the project metadata and source files in the language of your choice (e.g., __main__.py for Python and for our example).
Install Dependencies: If the template has any dependencies (like AWS SDK for a Python AWS template), Pulumi will install them for you.
Ready to Go: After all this, you'll have a new Pulumi project directory all set up and ready for you to start coding your infrastructure.
I can then cd into our project directory and run pulumi up to deploy our new stack, but first, I have to edit the generated code in __main__.py to tailor it to our specific needs.
I've taken a few screenshots of this process below.
Pulumi Files
These are the files that were created in the previous process.
"""An AWS Python Pulumi program"""import pulumi
from pulumi_aws import s3
# Create an AWS resource (S3 Bucket)
bucket = s3.Bucket('my-bucket')
# Export the name of the bucket
pulumi.export('bucket_name', bucket.id)
And below is our updated __main__.py file
import pulumi
from pulumi_aws import s3
# Create an AWS resource (S3 Bucket)
bucket = s3.Bucket("env0-pulumi-example",
bucket="env0-pulumi-example",
acl="private",
tags={
"Name": "My env0 Pulumi Example Bucket",
"Environment": "Dev",
})
ownership = s3.BucketOwnershipControls(
"bucket-controls",
bucket=bucket.id,
rule={"object_ownership": "BucketOwnerPreferred"}
)
# Export the name of the bucket
pulumi.export("bucketName", bucket.id)
Run Pulumi
Let's now run Pulumi, but first, you'll need to log in to Pulumi since we're going to use the Pulumi SaaS to store our stack. Make sure you have a Pulumi account then run pulumi login. Create a token and feed it in the prompt. Now you're ready to run the following commands:
pulumi preview
Previewing update (s3-pulumi-test)
View in Browser (Ctrl+O): https://app.pulumi.com/samgabrail/PulumiTest/s3-pulumi-test/previews/bc3ff0f6-4e84-4a80-9d64-aed495c97f4f
Type Name Plan
+ pulumi:pulumi:Stack PulumiTest-s3-pulumi-test create
+ ├─ aws:s3:Bucket env0-pulumi-example create
+ └─ aws:s3:BucketOwnershipControls bucket-controls create
Outputs:
bucketName: output[string]
Resources:
+ 3 to create
and when satisfied run pulumi up and choose 'yes'when prompted.
pulumi up
Previewing update(s3-pulumi-test)
View in Browser(Ctrl+O): https://app.pulumi.com/samgabrail/PulumiTest/s3-pulumi-test/previews/68f6a538-b4e8-419f-95bf-6b5198a2706e
Type Name Plan
+ pulumi:pulumi:Stack PulumiTest-s3-pulumi-test create
+ ├─ aws:s3:Bucket env0-pulumi-example create
+ └─ aws:s3:BucketOwnershipControls bucket-controls create
Outputs:
bucketName: output[string]
Resources:
+ 3 to create
Do you want to perform this update? yes
Updating(s3-pulumi-test)
View in Browser(Ctrl+O): https://app.pulumi.com/samgabrail/PulumiTest/s3-pulumi-test/updates/1
Type Name Status
+ pulumi:pulumi:Stack PulumiTest-s3-pulumi-test created(3s)
+ ├─ aws:s3:Bucket env0-pulumi-example created(0.83s)
+ └─ aws:s3:BucketOwnershipControls bucket-controls created(0.66s)
Outputs:
bucketName: "env0-pulumi-example"
Resources:
+ 3 created
Duration: 5s
Cleanup with Pulumi
Now go ahead and run pulumi destroy to clean up and delete the bucket.
Summary
In conclusion, both Terraform and Pulumi offer powerful features and benefits for managing and deploying infrastructure. While Terraform’s maturity and extensive community support make it a popular choice, Pulumi’s developer-friendly approach and growing ecosystem position it as an increasingly attractive alternative.
By considering factors such as language support, state management, community resources, and specific use cases, organizations can make an informed decision between these two powerful IaC tools to best meet their infrastructure management needs. Better yet, use both with env0 since it's a framework-agnostic platform.