Pulumi: IaC in Real Programming Languages

Khimananda Oli 8 min read Database
Pulumi: IaC in Real Programming Languages

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.

Pulumi (Real Language)TypeScript / PythonLanguage RuntimePulumi EngineCloud Provider APIUnit Tests / LintingTraditional DSL (HCL)Config Files (.tf)Custom ParserCore EngineCloud Provider APIValidate / Plan Only
Pulumi executes real language runtimes before reaching the engine, enabling native testing and IDE features unavailable in DSL parsers

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

  1. Install the Pulumi CLI via your package manager or download from official releases.
  2. Create a new directory and initialize a project:
    mkdir my-infra && cd my-infra
    pulumi new aws-typescript
  3. Follow the prompts to set your stack name and AWS region. The CLI creates Pulumi.yaml, package.json, and index.ts.
  4. Install dependencies:
    npm install
  5. 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.

Git PushPR / MainUnit TestsMock Providers< 30 secondspulumi previewDrift DetectionPolicy Checkspulumi upDeploy StackState UpdateFail FastBlock Deploy
Pulumi CI pipeline executes fast unit tests with mocked providers before expensive preview and deploy operations

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.

CriteriaPulumiTerraformAWS CDK
Language SupportTypeScript, Python, Go, C#, Java, YAMLHCL onlyTypeScript, Python, Java, C#, Go
Cloud CoverageAWS, Azure, GCP, Kubernetes, 150+ providersAWS, Azure, GCP, 3000+ providersAWS primary, limited others
State ManagementPulumi Cloud, S3, Azure Blob, GCS, self-hostedTerraform Cloud, S3, Consul, localCloudFormation (AWS managed)
Testing CapabilityNative unit + integration testsPlan validation, terratest (external)Snapshots, assertions library
Learning CurveLow for developers, medium for opsMedium for all rolesLow for AWS developers
Ecosystem MaturityGrowing rapidly since 2018Industry standard since 2014Strong 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.

External VaultHashiCorp / AWS KMSEncrypted at RestTLS OnlyPulumi EngineDecrypt in MemoryRedact Logs / OutputAWS ProviderAzure ProviderGCP ProviderState Backend (Encrypted)
Pulumi decrypts secrets only in memory and redacts all outputs, ensuring plaintext never persists in state or logs

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.

Frequently Asked Questions

Pulumi is an infrastructure as code tool that uses general-purpose languages like TypeScript, Python, or Go instead of domain-specific HCL. This allows developers to use familiar IDEs, testing frameworks, and package managers for cloud resource management in 2026.

TypeScript, JavaScript, Python, Go, C#, Java, and YAML are fully supported. Each language has native SDKs with type safety and autocomplete for AWS, Azure, GCP, and Kubernetes resources.

Yes, the Individual tier is free forever for personal projects. It includes unlimited stacks and resources but restricts team collaboration features available in paid Team and Enterprise tiers.

Pulumi defaults to its managed SaaS backend for encrypted state storage. You can also self-host state in S3, Azure Blob, or GCS using the pulumi login command with a storage backend URL.

Yes, use pulumi import to adopt existing resources without recreation. The CLI generates the required code definition and updates state automatically, preventing drift during migration from manual setups or other IaC tools.

Pulumi encrypts secrets at rest using unique per-stack keys. Use pulumi config set --secret to store sensitive values. Integration with HashiCorp Vault, AWS Secrets Manager, or Azure Key Vault is also natively supported.

Yes, Pulumi provides first-class Kubernetes providers and Helm chart support. You can deploy manifests, manage CRDs, and orchestrate multi-cluster deployments using standard programming constructs alongside cloud infrastructure definitions.

Developers already proficient in TypeScript or Python face minimal ramp-up time. Understanding cloud APIs and Pulumi’s async resource model takes roughly one week of hands-on practice for competent production usage.

Pulumi automatically infers dependencies through language-native references and outputs. Explicit dependsOn options exist for edge cases, but most ordering is resolved implicitly via the programming language’s type system and variable assignments.

Yes, use standard unit testing frameworks like Jest, pytest, or Go testing. Pulumi provides mocking libraries to simulate cloud provider responses, enabling fast local validation without provisioning real resources or incurring costs.

Use pulumi convert to translate HCL configurations to your target language. Validate converted code with pulumi preview, then import existing resources to match state. Always test migrations in non-production environments first.

Pulumi performs automatic rollback by default for failed operations. Partially created resources are cleaned up, and state remains consistent. Use --target flags to retry specific resources without reprocessing the entire stack.

Team plans cost $30 per user monthly with RBAC and audit logs. Enterprise adds SSO, policy enforcement, and dedicated support. Pricing is based on active users, not resource count or deployment frequency.

Yes, official GitHub Actions, GitLab CI, and CircleCI integrations exist. Use pulumi up --yes --skip-preview for automated deployments and pulumi preview for pull request checks with diff comments.

Use standard language debuggers with breakpoints since Pulumi runs native code. The pulumi watch command enables hot-reload feedback loops, while detailed logging via PULUMI_DEBUG_GRPC helps diagnose provider-level issues.