# Pulumi vs Terraform in 2026: Infrastructure as Code with TypeScript and Interview Questions > A detailed comparison of Pulumi and Terraform for infrastructure as code in 2026. Covers TypeScript support, state management, provider ecosystems, and common DevOps interview questions. - Published: 2026-09-21 - Updated: 2026-09-21 - Author: Anthony Fillion-Maillet - Tags: pulumi, terraform, infrastructure-as-code, typescript, devops, interview - Reading time: 12 min --- Pulumi vs Terraform represents the central infrastructure as code decision for DevOps teams in 2026. Both tools provision cloud resources declaratively, but they differ in language support, state management, and ecosystem maturity. This comparison covers the technical tradeoffs, code examples in TypeScript and HCL, and the interview questions that surface when candidates discuss IaC choices. > **Version Context: September 2026** > > This comparison uses Pulumi v3.263.0 (released September 15, 2026) and Terraform 1.16.3 (released August 26, 2026). CDKTF, HashiCorp's TypeScript layer for Terraform, was deprecated in December 2025 and is archived. ## Core Architecture: Pulumi's Engine vs Terraform's Plan-Apply Cycle Terraform uses a domain-specific language (HCL) and a two-phase workflow: `terraform plan` generates an execution plan, `terraform apply` executes it. The state file tracks resource metadata and is stored locally or in remote backends like S3 or Terraform Cloud. Pulumi embeds its engine in general-purpose runtimes. Programs written in TypeScript, Python, Go, or C# execute directly against cloud APIs through Pulumi's providers. State is stored in Pulumi Cloud by default, with options for self-managed backends including S3, Azure Blob Storage, and local files. The architectural difference shows up immediately in how each tool handles loops, conditionals, and abstractions. Terraform's `for_each` and `count` work within HCL's constraints. Pulumi programs use native language constructs: `for...of` in TypeScript, list comprehensions in Python, `range` in Go. ```typescript // infra/index.ts - Pulumi TypeScript example import * as aws from "@pulumi/aws"; import * as pulumi from "@pulumi/pulumi"; // Native TypeScript array methods work directly const environments = ["dev", "staging", "prod"]; const buckets = environments.map(env => new aws.s3.BucketV2(`data-${env}`, { bucket: `myapp-data-${env}-${pulumi.getStack()}`, tags: { Environment: env, ManagedBy: "pulumi", }, }) ); // Export bucket ARNs as stack outputs export const bucketArns = buckets.map(b => b.arn); ``` The equivalent Terraform configuration requires HCL syntax for iteration: ```hcl # main.tf - Terraform HCL example variable "environments" { type = list(string) default = ["dev", "staging", "prod"] } resource "aws_s3_bucket" "data" { for_each = toset(var.environments) bucket = "myapp-data-${each.key}-${terraform.workspace}" tags = { Environment = each.key ManagedBy = "terraform" } } output "bucket_arns" { value = [for b in aws_s3_bucket.data : b.arn] } ``` Both snippets create three S3 buckets. The Terraform version is concise and readable for anyone familiar with HCL. The Pulumi version uses standard TypeScript, which means IDE autocompletion, type checking, and integration with existing codebases without learning a new language. ## TypeScript Support: Native Language vs CDKTF (Deprecated) Pulumi supported TypeScript from its first public release. The SDK exposes typed resource classes, and `pulumi.Output` wraps asynchronous values that resolve during deployment. Refactoring tools, linters, and test frameworks from the TypeScript ecosystem apply directly. Terraform's answer to general-purpose language support was [CDKTF (Cloud Development Kit for Terraform)](https://developer.hashicorp.com/terraform/cdktf). CDKTF let teams write TypeScript or Python that compiled to Terraform JSON. HashiCorp deprecated CDKTF in December 2025 and archived the repository. Existing CDKTF projects still run, but no updates ship for new Terraform versions or provider changes. For teams that want TypeScript IaC in 2026, Pulumi is the actively maintained option. Teams already invested in CDKTF can continue running their stacks, but migration to HCL or Pulumi is the path forward for new development. ```typescript // infra/vpc.ts - Pulumi component for reusable VPC abstraction import * as aws from "@pulumi/aws"; import * as pulumi from "@pulumi/pulumi"; // Custom component encapsulating VPC, subnets, and routing export class StandardVpc extends pulumi.ComponentResource { public readonly vpcId: pulumi.Output; public readonly publicSubnetIds: pulumi.Output[]; constructor(name: string, args: { cidr: string; azCount: number }, opts?: pulumi.ComponentResourceOptions) { super("mycompany:network:StandardVpc", name, {}, opts); const vpc = new aws.ec2.Vpc(`${name}-vpc`, { cidrBlock: args.cidr, enableDnsHostnames: true, tags: { Name: name }, }, { parent: this }); this.vpcId = vpc.id; // Create public subnets across availability zones const azs = aws.getAvailabilityZones({ state: "available" }); this.publicSubnetIds = []; for (let i = 0; i < args.azCount; i++) { const subnet = new aws.ec2.Subnet(`${name}-public-${i}`, { vpcId: vpc.id, cidrBlock: `10.0.${i}.0/24`, availabilityZone: azs.then(az => az.names[i]), mapPublicIpOnLaunch: true, tags: { Name: `${name}-public-${i}` }, }, { parent: this }); this.publicSubnetIds.push(subnet.id); } this.registerOutputs({ vpcId: this.vpcId }); } } ``` This Pulumi component creates a reusable VPC abstraction. Consumers instantiate it with `new StandardVpc("prod", { cidr: "10.0.0.0/16", azCount: 3 })`. The equivalent in Terraform is a module directory with variables, outputs, and HCL files. Both approaches enable reuse; the Pulumi version inherits TypeScript's tooling for documentation, testing, and versioning. ## State Management and Backend Options State management is where operational differences appear. Terraform state contains resource IDs, provider metadata, and sensitive values. Teams must configure remote backends to enable collaboration and implement locking to prevent concurrent modifications. [Terraform backend configuration](https://developer.hashicorp.com/terraform/language/settings/backends/configuration) supports S3, Azure Blob, Google Cloud Storage, Terraform Cloud, and others. Each backend requires its own authentication setup. State locking uses DynamoDB for S3 backends, blob leases for Azure, or native locking in Terraform Cloud. Pulumi Cloud handles state storage, locking, and history by default. Teams create a free account, run `pulumi login`, and state management is configured. For teams that require self-managed state, Pulumi supports S3, Azure Blob, GCS, and local file backends with the `pulumi login` command. ```bash # Pulumi: login to self-managed S3 backend pulumi login s3://my-pulumi-state-bucket # Terraform: configure S3 backend in HCL # backend.tf terraform { backend "s3" { bucket = "my-terraform-state" key = "prod/terraform.tfstate" region = "us-east-1" dynamodb_table = "terraform-locks" encrypt = true } } ``` Pulumi Cloud provides a web UI for stack history, resource visualization, and team permissions. Terraform Cloud offers similar features with policy enforcement through Sentinel. Self-hosted options exist for both: Pulumi's self-hosted backend and Terraform Enterprise. ## Provider Ecosystem and Multi-Cloud Support Terraform's provider ecosystem is the largest in IaC. The [Terraform Registry](https://registry.terraform.io/) lists over 4,000 providers covering AWS, Azure, GCP, Kubernetes, and hundreds of SaaS platforms. Provider quality varies: official HashiCorp and cloud vendor providers receive regular updates, while community providers may lag behind API changes. Pulumi providers wrap Terraform providers using a bridge that generates typed SDKs. The [Pulumi Registry](https://www.pulumi.com/registry/) exposes providers for major clouds, Kubernetes, databases, and monitoring services. Because Pulumi bridges Terraform providers, ecosystem coverage is comparable, though new Terraform provider versions require bridge updates before Pulumi SDKs reflect changes. Pulumi also offers native providers written directly against cloud APIs. Native providers for AWS, Azure, and Kubernetes ship same-day support for new API features. The tradeoff is that native providers exist only for major clouds; less common services use bridged providers. ```typescript // Using Pulumi's native AWS provider for Lambda import * as aws from "@pulumi/aws"; import * as pulumi from "@pulumi/pulumi"; const role = new aws.iam.Role("lambda-role", { assumeRolePolicy: aws.iam.assumeRolePolicyForPrincipal({ Service: "lambda.amazonaws.com", }), }); const lambdaFunction = new aws.lambda.Function("api-handler", { runtime: aws.lambda.Runtime.NodeJS20dX, handler: "index.handler", role: role.arn, code: new pulumi.asset.AssetArchive({ "index.js": new pulumi.asset.StringAsset( `exports.handler = async () => ({ statusCode: 200, body: "OK" });` ), }), }); export const functionArn = lambdaFunction.arn; ``` ## Testing Infrastructure Code Testing is where language choice creates divergence. Pulumi programs are standard code, so unit tests use familiar frameworks. The `@pulumi/pulumi/runtime` module provides mocking for resource creation, letting tests verify configuration without deploying resources. ```typescript // __tests__/infra.test.ts - Unit testing Pulumi resources with Vitest import { describe, it, expect, beforeAll } from "vitest"; import * as pulumi from "@pulumi/pulumi"; // Mock Pulumi runtime pulumi.runtime.setMocks({ newResource: (args: pulumi.runtime.MockResourceArgs) => { return { id: `${args.name}-id`, state: args.inputs }; }, call: (args: pulumi.runtime.MockCallArgs) => { return args.inputs; }, }); describe("S3 Bucket Configuration", () => { let bucketTags: Record; beforeAll(async () => { // Import the Pulumi program after mocks are set const infra = await import("../infra/index"); // Extract outputs for testing bucketTags = await new Promise(resolve => { infra.buckets[0].tags.apply(tags => resolve(tags as Record)); }); }); it("should tag buckets with Environment", () => { expect(bucketTags).toHaveProperty("Environment"); }); it("should tag buckets with ManagedBy", () => { expect(bucketTags.ManagedBy).toBe("pulumi"); }); }); ``` Terraform testing uses `terraform test` (introduced in Terraform 1.6) or external tools like Terratest. `terraform test` runs HCL test files that create real resources in isolated configurations. Terratest is a Go library that wraps Terraform commands and provides assertions. ```hcl # tests/bucket.tftest.hcl - Terraform native test run "bucket_tags" { command = plan assert { condition = aws_s3_bucket.data["dev"].tags["Environment"] == "dev" error_message = "Bucket must be tagged with Environment" } assert { condition = aws_s3_bucket.data["dev"].tags["ManagedBy"] == "terraform" error_message = "Bucket must be tagged with ManagedBy" } } ``` Both approaches validate infrastructure configuration. Pulumi's advantage is integration with TypeScript test runners and coverage tools. Terraform's native tests run without external dependencies but require HCL syntax. ## Interview Questions: Pulumi vs Terraform DevOps interviews in 2026 frequently include IaC tool comparisons. Here are questions that distinguish candidates who have used both tools in production. **"What happens when Terraform state drifts from actual infrastructure?"** Expected answer: `terraform plan` detects drift by comparing state with the real infrastructure. The plan shows resources to update, create, or destroy. Drift occurs when changes are made outside Terraform (console, CLI, other tools). Options include `terraform refresh` to update state, `terraform import` to bring resources under management, or accepting the plan to restore desired state. **"How does Pulumi handle secrets differently than Terraform?"** Expected answer: Pulumi encrypts secrets in state by default using a passphrase or cloud KMS. The `pulumi.secret()` function marks values as sensitive, and they remain encrypted at rest. Terraform stores secrets in plaintext in state files; teams rely on backend encryption (S3 server-side encryption) and access controls. Terraform 1.4 added `sensitive` variable marking, but values still appear unencrypted in state. **"Why did HashiCorp deprecate CDKTF, and what are the migration options?"** Expected answer: CDKTF added maintenance overhead without matching Terraform's update cadence. New provider versions and Terraform features required manual updates to the CDK bindings. HashiCorp chose to focus on HCL and Terraform Cloud. Migration options are: convert to HCL using `cdktf convert`, rewrite in Pulumi if TypeScript is required, or continue running existing CDKTF stacks without updates. **"When would you choose Pulumi over Terraform for a new project?"** Expected answer: Choose Pulumi when the team already uses TypeScript/Python/Go and wants IaC in the same language as application code, when complex abstractions require real programming constructs (generics, interfaces, testing frameworks), or when built-in secret encryption is a requirement. Choose Terraform when the team knows HCL, when the provider ecosystem must include niche or community providers, or when organizational tooling (Atlantis, Spacelift) integrates with Terraform. For more infrastructure as code interview preparation, see [Terraform Interview Questions: Infrastructure as Code Complete Guide](/blog/devops/terraform-interview-questions-infrastructure-as-code) and the [Terraform Advanced module](/technologies/devops/interview-questions/terraform-advanced). ## Migration Strategies: Terraform to Pulumi Pulumi provides `pulumi import` to bring existing cloud resources under management without recreating them. For teams with Terraform state, `pulumi convert` translates HCL to Pulumi programs. The conversion is not perfect: complex modules and dynamic blocks require manual adjustment. ```bash # Convert Terraform HCL to Pulumi TypeScript pulumi convert --from terraform --language typescript # Import existing AWS resources into Pulumi state pulumi import aws:s3/bucketV2:BucketV2 my-bucket my-existing-bucket-name ``` A phased migration lets teams run both tools during transition. Terraform manages existing stacks while Pulumi provisions new infrastructure. Once teams gain Pulumi experience, they convert Terraform stacks incrementally. State is not shared between tools, so resource management must be clearly divided to avoid conflicts. ## Decision Framework for 2026 | Factor | Terraform | Pulumi | |--------|-----------|--------| | Language | HCL (DSL) | TypeScript, Python, Go, C#, Java, YAML | | State management | Self-managed or Terraform Cloud | Pulumi Cloud (default) or self-managed | | Secret handling | Plaintext in state, backend encryption | Encrypted by default | | Testing | `terraform test`, Terratest | Native language test frameworks | | Provider coverage | 4,000+ in registry | Bridges Terraform providers + native SDKs | | TypeScript support | CDKTF (deprecated December 2025) | First-class, actively maintained | | Learning curve | HCL is purpose-built, quick for IaC | Requires existing language knowledge | | Enterprise features | Terraform Cloud/Enterprise | Pulumi Cloud/self-hosted | > **Interview Tip** > > When asked about Pulumi vs Terraform, avoid stating one is "better." Articulate tradeoffs: Terraform has a larger ecosystem and HCL's constraints prevent runtime errors; Pulumi offers real language features and typed SDKs. Show that the choice depends on team skills, existing tooling, and project requirements. ## Key Takeaways for Infrastructure as Code in 2026 - Terraform 1.16 is the stable choice for HCL-based IaC with the largest provider ecosystem and mature tooling (Atlantis, Spacelift, env0). - Pulumi v3.263 offers TypeScript, Python, Go, and C# with native language testing, IDE integration, and built-in secret encryption. - CDKTF is deprecated since December 2025. Teams wanting TypeScript IaC should evaluate Pulumi rather than starting new CDKTF projects. - Both tools support multi-cloud deployments, remote state backends, and team collaboration. The choice depends on language preference and existing team expertise. - Interview discussions should demonstrate understanding of state management, drift detection, provider ecosystems, and the tradeoffs between DSL simplicity and general-purpose language power. - Migration from Terraform to Pulumi is possible using `pulumi convert` and `pulumi import`, but requires careful planning and phased rollout. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/en/blog/devops/pulumi-vs-terraform-2026-infrastructure-as-code-typescript-interview-questions