Skip to main content
Resources are the cloud objects declared inside a stack block. Each resource maps to a Pulumi resource class and becomes a provider API call when the stack is deployed.

Named resource

stack "s" {
    name = Provider.Type {
        prop = value
    }
}
The name on the left is the binding name — a local identifier used to reference this resource’s outputs elsewhere in the stack. It appears as the Pulumi resource logical name in generated code.

Unnamed resource

A resource without a binding is valid but its outputs cannot be referenced:
stack "bootstrap" {
    aws.iam.RolePolicy {
        role   = input.role_name
        policy = input.policy_json
    }
}

Type path

The type after = is a dot-separated type path that maps to a Pulumi provider class:
XCLPython
aws.ec2.Vpcaws.ec2.Vpc(...) with import pulumi_aws as aws
aws.eks.Clusteraws.eks.Cluster(...)
helm.Releasehelm.Release(...) with import pulumi_helm as helm
kubernetes.core.v1.ConfigMapkubernetes.core.v1.ConfigMap(...)
The first segment of the type path determines which provider package is imported. A requirements.txt entry is generated for each unique provider.

Resource properties

Properties are listed as key = value pairs inside the block:
vpc = aws.ec2.Vpc {
    cidr_block       = input.vpc_cidr
    enable_dns_support = true
    tags = {
        Name        = f"vpc-{input.env}"
        Environment = input.env
    }
}
Properties can reference:
  • Other resources by binding name: subnet.vpc_id = vpc.id
  • Input fields: cidr_block = input.vpc_cidr
  • Locals fields: acl = locals.bucket_acl
  • Literals: strings, numbers, booleans, null, lists, objects
  • F-strings: f"my-{input.name}-bucket"
  • Cross-stack references: vpc_id = @networking.vpc_id
Properties cannot contain match or if/else (ternary) expressions directly — move those to a locals block.

For clause

Creates one resource per element of a collection:
// Single variable (list):
subnets = aws.ec2.Subnet {
    for az in input.azs

    vpc_id            = vpc.id
    availability_zone = az
}

// Key-value variables (map):
bucket_policies = aws.s3.BucketPolicy {
    for bucket_name, cfg in input.bucket_configs

    bucket = bucket_name
    policy = cfg.policy
}
The for clause variable(s) are in scope only within the resource block’s properties. Referencing a for-resource produces a list. In generated Python, any access to a for-resource’s output becomes a list comprehension:
output "s" {
    subnet_ids = subnets.id  // → [r.id for r in subnets]
}

Count modifier

Creates N copies of a resource, indexed by the for-clause variable:
subnets = aws.ec2.Subnet * 3 {
    for idx in input.azs

    vpc_id     = vpc.id
    cidr_block = "10.0.0.0/16"
}
The * N expression is evaluated once at compile time if N is a literal, or remains dynamic if it references an input. It combines with the for clause. Generated Python:
subnets = [aws.ec2.Subnet(f"subnets-{idx}",
    vpc_id=vpc.id,
    cidr_block="10.0.0.0/16",
) for idx in azs]

When clause — conditional resources

A resource with a when clause is created only if the condition is truthy:
nat = aws.ec2.NatGateway when input.deploy_nat {
    subnet_id = subnets.id
}
Generated Python:
nat = (aws.ec2.NatGateway("nat",
    subnet_id=[r.id for r in subnets],
) if deploy_nat else None)
The when condition must be Resolved — it can reference input.* or locals.* values, but not resource outputs (which are Pending<T>):
when condition references "vpc" which is Pending<T> (a resource output) — when conditions must be Resolved<T> (use an input or locals value instead)

Dependency ordering

The compiler performs a topological sort of resources based on their property references. You don’t need to declare ordering explicitly — write resources in any order and the compiler resolves dependencies automatically.