
Table of Contents
By Khimananda Oli | Last reviewed: August 2026
Managing cloud infrastructure often forces engineers to learn domain-specific languages that lack the tooling and expressiveness of modern software development. Pulumi: IaC in real programming languages solves this friction by letting you define resources using TypeScript, Python, Go, or C# instead of proprietary configuration syntax. This approach enables teams to apply standard testing, refactoring, and CI/CD practices directly to their infrastructure code, bridging the gap between application and platform engineering.
How does Pulumi: IaC in real programming languages differ from Terraform?
The fundamental difference lies in the execution model. While Terraform uses HashiCorp Configuration Language (HCL) — a declarative DSL designed specifically for infrastructure — Pulumi executes actual program code. When you run pulumi up, your language runtime evaluates the program to construct a desired state graph, which the Pulumi engine then reconciles against the cloud provider API. This distinction matters because it removes the artificial ceiling on complexity that DSLs impose.
In my work auditing infrastructure across multi-cloud environments, I frequently encounter HCL modules that have grown into unmaintainable tangles of dynamic blocks and string interpolation simply to replicate basic programming constructs. With Pulumi, you use native loops, conditionals, classes, and functions. If you need to generate fifty S3 buckets with specific tagging policies based on a JSON config file, you write a standard loop in your preferred language rather than wrestling with for_each limitations.
This architectural choice also impacts team velocity. Developers already proficient in TypeScript or Python can contribute to infrastructure immediately without climbing an HCL learning curve. For organizations in Nepal building global SaaS products, this means your existing engineering team can own platform concerns without hiring dedicated specialists first. If you are evaluating broader infrastructure strategies, our guide on infrastructure as code with Terraform covers the DSL alternative in depth.
How do you set up a Pulumi project with TypeScript or Python?
Starting a new Pulumi project takes under two minutes. The CLI scaffolds a complete project structure including dependency files, stack configuration, and an entry point. Below is the exact workflow I use when bootstrapping new AWS or Azure environments.
Initialize and configure the stack
- Install the Pulumi CLI via your package manager or download from official releases.
- Create a new directory and initialize a project:
mkdir my-infra && cd my-infra pulumi new aws-typescript - Follow the prompts to set your stack name and AWS region. The CLI creates
Pulumi.yaml,package.json, andindex.ts. - Install dependencies:
npm install - Configure secrets and settings:
pulumi config set aws:region ap-south-1 pulumi config set --secret dbPassword "your-secure-password"
Define your first resources
Open index.ts and define infrastructure using familiar object-oriented patterns. This example creates an S3 bucket with versioning and exports its ARN:
import * as aws from "@pulumi/aws";
const logsBucket = new aws.s3.BucketV2("app-logs", {
tags: {
Environment: "production",
ManagedBy: "pulumi",
},
});
new aws.s3.BucketVersioningV2("app-logs-versioning", {
bucket: logsBucket.id,
versioningConfiguration: {
status: "Enabled",
},
});
export const bucketArn = logsBucket.arn; For Python teams, the equivalent uses standard Python semantics:
import pulumi_aws as aws
logs_bucket = aws.s3.BucketV2("app-logs",
tags={
"Environment": "production",
"ManagedBy": "pulumi",
}
)
aws.s3.BucketVersioningV2("app-logs-versioning",
bucket=logs_bucket.id,
versioning_configuration={
"status": "Enabled",
}
)
pulumi.export("bucket_arn", logs_bucket.arn) Note that both examples use strongly-typed SDKs. Your IDE will autocomplete properties, catch errors before deployment, and provide inline documentation. This is impossible with untyped DSL configurations.
Why should you write unit tests for infrastructure code?
Infrastructure bugs cause outages just like application bugs, yet most IaC goes untested beyond basic validation. Because Pulumi uses real languages, you can write genuine unit tests that mock cloud providers and verify logic without deploying anything. This is critical for compliance-ready environments where every change must be validated before hitting production.
I treat infrastructure tests with the same rigor as application code. Here is a practical Node.js test using Pulumi's testing framework that validates tag enforcement:
import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import { describe, it, expect } from "vitest";
describe("S3 Bucket Compliance", () => {
it("must have required tags", async () => {
const bucket = new aws.s3.BucketV2("test-bucket", {
tags: { Environment: "staging", Owner: "platform-team" }
});
const tags = await pulumi.output(bucket.tags).apply(t => t);
expect(tags).toHaveProperty("Environment");
expect(tags).toHaveProperty("Owner");
expect(tags?.["ManagedBy"]).toBeUndefined();
});
}); This test runs in milliseconds during CI, catching policy violations before they reach the plan stage. For security-focused teams, integrating these checks aligns well with practices described in our DevSecOps shift-left guide. You can also perform integration tests against ephemeral stacks using Pulumi's automation API, giving you end-to-end confidence without manual verification.
When should you choose Pulumi over Terraform or CDK?
No tool is universally superior. The decision depends on your team's existing skills, project complexity, and operational constraints. After managing infrastructure across dozens of organizations, I evaluate these trade-offs systematically.
| Criteria | Pulumi | Terraform | AWS CDK |
|---|---|---|---|
| Language Support | TypeScript, Python, Go, C#, Java, YAML | HCL only | TypeScript, Python, Java, C#, Go |
| Cloud Coverage | AWS, Azure, GCP, Kubernetes, 150+ providers | AWS, Azure, GCP, 3000+ providers | AWS primary, limited others |
| State Management | Pulumi Cloud, S3, Azure Blob, GCS, self-hosted | Terraform Cloud, S3, Consul, local | CloudFormation (AWS managed) |
| Testing Capability | Native unit + integration tests | Plan validation, terratest (external) | Snapshots, assertions library |
| Learning Curve | Low for developers, medium for ops | Medium for all roles | Low for AWS developers |
| Ecosystem Maturity | Growing rapidly since 2018 | Industry standard since 2014 | Strong within AWS ecosystem |
Choose Pulumi when your team is developer-heavy and wants to unify application and infrastructure workflows. It excels for complex architectures requiring dynamic resource generation, sophisticated abstractions, or tight integration with application code. Teams building internal developer platforms often find Pulumi's component model more natural than HCL modules.
Stick with Terraform if you have deep existing HCL investment, require obscure third-party providers not yet available in Pulumi, or operate in highly regulated environments where auditor familiarity matters. The community module ecosystem remains unmatched. For pure AWS shops already invested in the AWS ecosystem, CDK offers similar benefits with tighter CloudFormation integration, though at the cost of multi-cloud flexibility.
How do you manage secrets and state securely in Pulumi?
Security failures in IaC typically stem from leaked credentials or exposed state files. Pulumi addresses both through built-in encryption and secret management. Never store plaintext secrets in configuration files or source control — this is non-negotiable for any SOC 2 or ISO 27001 audit.
Pulumi encrypts all secret values at rest using either the Pulumi Cloud's key management or your own KMS keys when using self-managed backends. Mark values as secret during configuration:
pulumi config set --secret databaseUrl "postgresql://user:pass@host/db" In code, retrieve secrets safely. They remain encrypted in state and logs:
const dbUrl = config.requireSecret("databaseUrl");
const dbInstance = new aws.rds.Instance("app-db", {
connectionString: dbUrl,
// Value is never printed in preview or logs
}); For teams requiring external secret stores, integrate HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault directly. This pattern aligns with approaches detailed in our secrets management with Vault article. Always enable state encryption, restrict backend access via IAM policies, and audit state access logs regularly. In my compliance work, I've seen more breaches from careless state handling than from application vulnerabilities.
Start Building Infrastructure with Real Code
Pulumi: IaC in real programming languages represents a pragmatic evolution in infrastructure automation, not a revolution. It trades some ecosystem maturity for genuine software engineering practices that reduce bugs, accelerate onboarding, and improve maintainability. Start small: migrate a single non-critical stack, establish testing patterns, and measure the impact on your deployment frequency and incident rates before committing broadly.
If you need help evaluating Pulumi for your organization, designing secure state management, or migrating existing HCL modules to typed languages, reach out to discuss your infrastructure strategy. I help teams build platforms that are automated, observable, secure, and audit-ready from day one.