Pulumi is a modern Infrastructure as Code platform that lets you define, deploy, and manage cloud resources using general-purpose programming languages — Python, TypeScript, Go, C#, and Java — instead of a domain-specific language like Terraform’s HCL. This means you get real loops, conditionals, functions, classes, and access to the full ecosystem of your chosen language’s packages when describing infrastructure. Pulumi supports over 100 cloud and SaaS providers including AWS, Azure, GCP, and Kubernetes. This guide covers installing the Pulumi CLI on Red Hat Enterprise Linux 9, creating a new Python project, and provisioning an EC2 instance and S3 bucket to demonstrate the core Pulumi workflow.
Prerequisites
- RHEL 9 server with
sudoor root access and internet connectivity - Python 3.9 or later installed (
python3 --version) - AWS CLI configured with credentials (
aws configure) for the AWS provider examples - A Pulumi account at app.pulumi.com (free tier) for state backend, or familiarity with local state
- Basic familiarity with Python and cloud concepts (VPCs, instances, buckets)
Step 1 — Install the Pulumi CLI
Pulumi provides an official installation script that downloads a pre-built binary and places it in ~/.pulumi/bin. Add this directory to your PATH to make the pulumi command available in all sessions.
curl -fsSL https://get.pulumi.com | sh
# Add Pulumi to PATH for the current session
export PATH="$HOME/.pulumi/bin:$PATH"
# Make it permanent
echo 'export PATH="$HOME/.pulumi/bin:$PATH"' >> ~/.bashrc
pulumi version
Step 2 — Create a New Pulumi Project
Log in to the Pulumi state backend, then scaffold a new AWS Python project. The pulumi new command creates a project directory, a virtual environment, and a starter __main__.py file. Accept the defaults or customize the project name, description, and AWS region when prompted.
pulumi login
mkdir ~/pulumi-demo && cd ~/pulumi-demo
pulumi new aws-python
# Pulumi creates:
# Pulumi.yaml — project manifest
# Pulumi.dev.yaml — stack configuration (dev stack)
# __main__.py — your Python IaC program
# requirements.txt — Python dependencies
# .venv/ — isolated virtual environment
Step 3 — Write the Infrastructure Program
Replace the contents of __main__.py with a program that creates an EC2 instance and a private S3 bucket. Pulumi resolves resource dependencies automatically when you reference one resource’s output as an input to another.
# __main__.py
import pulumi
import pulumi_aws as aws
# Create an S3 bucket for application artifacts
bucket = aws.s3.Bucket(
"app-artifacts",
acl="private",
tags={"Environment": "dev", "Project": "pulumi-demo"},
)
# Reference the default VPC for simplicity
default_vpc = aws.ec2.get_vpc(default=True)
default_subnet = aws.ec2.get_subnet(
filters=[aws.ec2.GetSubnetFilterArgs(name="vpc-id", values=[default_vpc.id])],
default_for_az=True,
)
# Security group allowing SSH inbound
sg = aws.ec2.SecurityGroup(
"web-sg",
vpc_id=default_vpc.id,
ingress=[aws.ec2.SecurityGroupIngressArgs(
protocol="tcp", from_port=22, to_port=22, cidr_blocks=["0.0.0.0/0"]
)],
egress=[aws.ec2.SecurityGroupEgressArgs(
protocol="-1", from_port=0, to_port=0, cidr_blocks=["0.0.0.0/0"]
)],
)
# Launch an EC2 instance
instance = aws.ec2.Instance(
"web-server",
instance_type="t3.micro",
ami="ami-0c55b159cbfafe1f0", # RHEL 9 us-east-1 — update as needed
subnet_id=default_subnet.id,
vpc_security_group_ids=[sg.id],
tags={"Name": "pulumi-demo-server"},
)
# Export values for inspection after deployment
pulumi.export("instance_public_ip", instance.public_ip)
pulumi.export("bucket_name", bucket.id)
Step 4 — Preview and Deploy the Stack
Run pulumi preview to see a dry-run diff of all resources that will be created, modified, or deleted. Then run pulumi up to execute the deployment. Pulumi prompts for confirmation before making any changes.
# Install Python dependencies into the virtual environment
source .venv/bin/activate
pip install -r requirements.txt
# Dry-run: see what Pulumi would create
pulumi preview
# Deploy — review the plan and type "yes" to confirm
pulumi up
# View exported outputs
pulumi stack output instance_public_ip
pulumi stack output bucket_name
Step 5 — Manage Stacks and Destroy Resources
Pulumi uses stacks to separate environments (dev, staging, production). Each stack has its own state and configuration. When you are finished, pulumi destroy tears down all resources in the current stack without deleting the project.
# List all stacks in this project
pulumi stack ls
# Create a new stack for staging
pulumi stack init staging
pulumi config set aws:region us-west-2
# Switch back to dev
pulumi stack select dev
# Tear down all resources (prompts for confirmation)
pulumi destroy
# Remove the stack record from state (optional cleanup)
pulumi stack rm dev
Conclusion
You have installed Pulumi on RHEL 9, written a Python IaC program that provisions AWS resources, and used the preview → up → destroy workflow to manage infrastructure lifecycle. The key advantage over Terraform is expressive power: instead of workarounds like count meta-arguments or for_each maps, you write ordinary Python loops and functions. Pulumi’s state model is equivalent to Terraform’s — both track deployed resources and compute diffs — but Pulumi’s state can live in its cloud backend, an S3 bucket, or local disk, giving you flexibility without vendor lock-in. For team workflows, store per-environment configuration in Pulumi.<stack>.yaml and use Pulumi ESC (Environments, Secrets, and Configuration) to manage secrets securely without embedding credentials in code.
Next steps: How to Deploy Kubernetes Resources with Pulumi on RHEL 9, Getting Started with Terraform on RHEL 9, and How to Manage Secrets with HashiCorp Vault on RHEL 9.