Declarative HCL vs Imperative Code
Terraform uses HCL (HashiCorp Configuration Language): a declarative DSL for defining infrastructure. resource "aws_s3_bucket" "my_bucket" { bucket = "my-app-assets" tags = { Environment = "production" } }. You declare: what the infrastructure should look like. Terraform figures out: how to make it so (create, update, or destroy resources to match the declaration). HCL is: not a programming language — no loops, no conditionals (except count and for_each), no functions beyond built-in ones.
Pulumi uses real programming languages: TypeScript, Python, Go, or C#. const bucket = new aws.s3.Bucket('my-bucket', { tags: { Environment: 'production' } }). You write: imperative code that creates resources. Pulumi executes: the code to determine the desired state, then applies changes. TypeScript is: a real language — loops, conditionals, functions, classes, imports, type safety, and the full npm ecosystem. You can: write abstractions, compose components, and use any TypeScript pattern.
Without IaC tool rules: the AI generates Terraform HCL in a Pulumi project (HCL syntax is invalid TypeScript), Pulumi TypeScript in a Terraform project (TypeScript is not HCL), terraform plan commands in a Pulumi environment (use pulumi preview), or HCL module patterns in Pulumi (use TypeScript imports and classes). The IaC tool determines: the language, the resource syntax, the CLI commands, and the state management approach. One rule prevents: every IaC syntax error.
Resource Definition: HCL Blocks vs Constructor Calls
Terraform resource definition: resource blocks in .tf files. resource "aws_lambda_function" "api" { function_name = "my-api" runtime = "nodejs20.x" handler = "index.handler" filename = "lambda.zip" role = aws_iam_role.lambda.arn }. References: other resources by type.name (aws_iam_role.lambda.arn). Dependencies: implicit from references (Terraform builds the dependency graph automatically). AI rule: 'Terraform: resource blocks in .tf files. Reference: type.name.attribute. Dependencies: implicit from references. Variables: var.name. Outputs: output blocks.'
Pulumi resource definition: constructor calls in TypeScript. const api = new aws.lambda.Function('api', { functionName: 'my-api', runtime: 'nodejs20.x', handler: 'index.handler', code: new pulumi.asset.FileArchive('./lambda.zip'), role: lambdaRole.arn }). References: TypeScript variables (lambdaRole.arn is a Pulumi Output<string>). Dependencies: implicit from variable references (Pulumi tracks Output dependencies). AI rule: 'Pulumi: new aws.service.Resource() constructors. Reference: TypeScript variables. Dependencies: implicit from Output references. Config: pulumi.Config().'
The resource rule prevents: the AI generating resource "aws_lambda_function" HCL in a Pulumi .ts file (syntax error), new aws.lambda.Function() in a .tf file (syntax error), using var.name in Pulumi (Terraform syntax — use pulumi.Config().require()), or referencing aws_iam_role.lambda.arn in Pulumi (Terraform syntax — use the TypeScript variable). The resource definition syntax is: completely different despite creating the same infrastructure.
- Terraform: resource 'type' 'name' { } blocks in .tf files. Reference: type.name.attribute
- Pulumi: new aws.service.Resource('name', { }) constructors in .ts files. Reference: variable.property
- Terraform: HCL syntax (= for assignment, no semicolons). Pulumi: TypeScript syntax (: for types, , for props)
- Variables: Terraform var.name. Pulumi: pulumi.Config().require('name')
- AI error: HCL blocks in .ts file = syntax error. TypeScript in .tf file = syntax error
S3 bucket in Terraform: resource 'aws_s3_bucket' 'name' { bucket = '...' }. S3 bucket in Pulumi: new aws.s3.Bucket('name', { ... }). Same cloud resource. Incompatible syntax. One rule about which IaC tool: prevents every resource definition from being in the wrong language.
State Management: terraform.tfstate vs Pulumi State
Terraform state: terraform.tfstate file (JSON) tracks every resource Terraform manages. State backends: local file (default, not recommended for teams), S3 + DynamoDB (AWS remote state with locking), or Terraform Cloud (managed state). State locking: prevents concurrent modifications. terraform plan: compares desired state (HCL) with current state (tfstate) to determine changes. AI rule: 'Terraform: state in S3 backend with DynamoDB locking for teams. Never commit terraform.tfstate to git. terraform plan before apply. terraform import for existing resources.'
Pulumi state: managed by Pulumi (Pulumi Cloud by default, or self-managed S3/Azure Blob/GCS backend). State stores: every resource Pulumi manages, similar to Terraform state. Pulumi Cloud: provides state management, history, RBAC, and drift detection. pulumi preview: compares desired state (your code) with current state (Pulumi state). AI rule: 'Pulumi: state in Pulumi Cloud (default) or self-managed backend. pulumi preview before up. pulumi stack for environment management. pulumi import for existing resources.'
The state rule prevents: the AI running terraform apply without a remote backend (state stored locally, team cannot collaborate), committing tfstate to git (contains secrets and infrastructure details), running pulumi up without preview (apply without seeing changes), or mixing state management patterns (Terraform state commands in Pulumi, Pulumi stack commands in Terraform). State management is: the most critical IaC operations concern. Wrong state handling: can destroy infrastructure.
- Terraform: terraform.tfstate in S3 + DynamoDB for teams. Never commit to git
- Pulumi: Pulumi Cloud (default) or self-managed backend. Managed history and RBAC
- Preview: terraform plan vs pulumi preview — always preview before applying changes
- Locking: Terraform DynamoDB lock. Pulumi Cloud handles locking automatically
- State contains secrets: both tools store sensitive values in state — encrypt and protect
terraform apply without remote backend: state stored locally, team cannot collaborate, state lost = Terraform does not know what it manages. Never commit tfstate to git (contains secrets). Always remote backend + locking. State is the most critical IaC concern — get it wrong and infrastructure is unrecoverable.
Modules vs Components: Reuse Patterns
Terraform modules: directories with .tf files that accept variables and produce outputs. module "vpc" { source = "./modules/vpc" cidr_block = "10.0.0.0/16" }. Modules are: directories referenced by path or registry URL. Module registry: Terraform Registry has thousands of community modules. Versioning: source = "hashicorp/consul/aws" version = "~> 0.1". AI rule: 'Terraform: modules in modules/ directory. Source: local path or registry. Variables: variable blocks. Outputs: output blocks. Use community modules from registry when available.'
Pulumi components: TypeScript classes that extend pulumi.ComponentResource. class Vpc extends pulumi.ComponentResource { constructor(name: string, args: VpcArgs, opts?: pulumi.ComponentResourceOpts) { super('custom:Vpc', name, {}, opts); const vpc = new aws.ec2.Vpc(...); } }. Components are: TypeScript classes with full language features (generics, inheritance, composition). Package registry: npm packages for reusable components. AI rule: 'Pulumi: components as TypeScript classes extending ComponentResource. Publish as npm packages for reuse. Full TypeScript: generics, inheritance, composition for complex abstractions.'
The reuse rule prevents: the AI generating module {} blocks in Pulumi (Terraform syntax), creating TypeScript classes in Terraform (HCL does not have classes), using the Terraform Registry in Pulumi (use npm packages), or applying HCL variable/output patterns in Pulumi (use TypeScript function parameters and return types). The reuse mechanism is: language-determined. HCL = directories with variables. TypeScript = classes with constructors.
When to Choose Each Tool
Choose Terraform when: your team has Terraform experience (the most widely adopted IaC tool), your infrastructure is straightforward (standard AWS/GCP/Azure resources without complex logic), you want the largest module ecosystem (Terraform Registry has more modules than any alternative), your organization uses Terraform Cloud (managed state, policies, cost estimation), or your team prefers declarative configuration (describe what, not how). Terraform is: the industry standard for IaC, supported by every cloud provider.
Choose Pulumi when: your team prefers real programming languages (TypeScript developers write infrastructure in TypeScript — no new language to learn), your infrastructure requires complex logic (conditionals, loops, abstractions that HCL cannot express cleanly), you want type safety for infrastructure (TypeScript catches misconfigured resources at compile time), or you want to share code between application and infrastructure (same language, same packages, same patterns). Pulumi is: the developer-first IaC tool for teams that find HCL limiting.
For AI rule generation: Terraform HCL is a more constrained language — the AI has fewer ways to express the same infrastructure (less room for style variation, more consistent output). Pulumi TypeScript is more flexible — the AI has many ways to express the same infrastructure (more room for style variation, may need more rules to ensure consistency). Terraform projects: need fewer AI rules (HCL constrains the output). Pulumi projects: need more AI rules (TypeScript flexibility requires convention enforcement).
Terraform HCL: limited syntax means fewer ways to express infrastructure (less style variation, more consistent AI output). Pulumi TypeScript: full language flexibility means many ways to express the same thing (more style variation, needs more rules for consistency). Terraform: fewer AI rules suffice. Pulumi: needs convention rules to constrain the flexibility.
IaC Tool Rule Summary
Summary of Terraform vs Pulumi AI rules.
- Language: Terraform = HCL (declarative DSL). Pulumi = TypeScript/Python/Go (imperative code)
- Resources: Terraform resource blocks. Pulumi constructor calls (new aws.service.Resource())
- State: Terraform tfstate in S3 + DynamoDB. Pulumi Cloud or self-managed backend
- Preview: terraform plan vs pulumi preview — always before apply/up
- Reuse: Terraform modules (directories). Pulumi components (TypeScript classes, npm packages)
- Terraform: larger ecosystem, more modules, industry standard, constrained language (fewer rules needed)
- Pulumi: real languages, type safety, complex logic, developer-first (more rules needed for consistency)
- AI rule: IaC tool determines every resource syntax, CLI command, and state operation