1. Docs
  2. Infrastructure as Code
  3. Languages & SDKs
  4. HCL
  5. Reference

Pulumi HCL reference

    Pulumi programs can be defined in many languages, and the Pulumi HCL dialect offers an additional language for authoring Pulumi programs using Terraform-like HCL syntax.

    A Pulumi HCL program consists of one or more .hcl files in a directory whose Pulumi.yaml specifies runtime: hcl:

    name: my-project
    runtime: hcl
    

    HCL files declare infrastructure using the top-level blocks listed below. The full set of HCL expressions and Terraform built-in functions is supported, along with Pulumi-specific asset and archive functions. See Terraform compatibility for the small number of differences.

    Top-level blocks

    BlockPurpose
    variableDeclare input variables
    resourceManage cloud resources
    dataRead external data via provider invocations
    providerConfigure provider instances
    outputExport values from the stack
    localsDefine reusable intermediate values
    moduleInvoke local or remote modules as components
    callInvoke methods on resources
    movedRename resources without recreation
    importImport existing cloud resources
    pulumiRequired providers, version constraints, and component declarations

    In many locations within these blocks, values are HCL expressions that reference variables, locals, resources, data sources, or modules. See Expressions for the supported forms.

    Variables

    variable blocks declare input values for the program. Each block has one label, the variable name, which is referenced in expressions as var.<name>.

    variable "region" {
      type        = string
      default     = "us-west-2"
      description = "AWS region to deploy into"
    }
    
    variable "instance_count" {
      type    = number
      default = 1
    }
    
    variable "name" {
      type = string
    
      validation {
        condition     = length(var.name) > 0
        error_message = "Name must not be empty."
      }
    }
    
    AttributeTypeRequiredDescription
    typetype expressionNoType constraint (for example, string, number, bool, list(string), map(number), object({...})).
    defaultexpressionNoDefault value when the variable is not configured.
    descriptionstringNoHuman-readable description.
    sensitiveboolNoWhen true, the value becomes a Pulumi secret.
    nullableboolNoWhen false, rejects null values. Defaults to true.
    validationblockNoOne or more validation rules (see below).

    Each validation block has the following attributes:

    AttributeTypeRequiredDescription
    conditionexpressionYesExpression that must evaluate to true.
    error_messageexpressionYesError message shown when the condition is false.

    Variables are set through Pulumi’s config system, in priority order:

    1. Environment variables: TF_VAR_<name>=<value> (highest priority).
    2. Stack config: pulumi config set <project>:<varName> <value>.
    3. Default values in variable blocks (lowest priority).

    Resources

    resource blocks declare managed infrastructure. The first label is the resource type (Terraform-like, for example aws_instance); the second label is the logical name; the body contains the resource’s input properties.

    resource "aws_instance" "web" {
      ami           = "ami-12345678"
      instance_type = "t3.micro"
    
      tags = {
        Name = "web-server"
      }
    }
    

    resource blocks support the full set of Pulumi resource options as top-level attributes (with snake_case names — for example retain_on_delete, parent, aliases). See Resource options for the HCL surface and the canonical resource-options reference for full semantics. Some options are also reachable via the lifecycle block under their Terraform-like names (for example prevent_destroyprotect, ignore_changes).

    Meta-arguments

    ArgumentTypeDescription
    countnumberCreate multiple instances indexed by count.index.
    for_eachmap or setCreate instances keyed by each.key with each.value.
    depends_onlistExplicit dependencies on other resources.
    providerreferenceSpecific provider configuration to use.
    providerslistExplicit provider resources (component resources only).

    Lifecycle block

    resource "aws_instance" "web" {
      # ...
    
      lifecycle {
        create_before_destroy = true
        prevent_destroy       = true
        ignore_changes        = [tags]
      }
    }
    
    AttributeTypeDescription
    create_before_destroyboolWhen false, deletes the old resource before creating the replacement (Pulumi creates first by default).
    prevent_destroyboolProtect the resource from accidental deletion. Maps to Pulumi’s protect option.
    ignore_changeslistProperty paths to exclude from diff detection.

    The lifecycle block also supports precondition and postcondition blocks, each of which takes a condition expression and an error_message expression:

    resource "aws_instance" "web" {
      # ...
    
      lifecycle {
        precondition {
          condition     = var.instance_type != ""
          error_message = "Instance type must be specified."
        }
    
        postcondition {
          condition     = self.public_ip != ""
          error_message = "Instance must have a public IP."
        }
      }
    }
    

    Timeouts block

    resource "aws_instance" "web" {
      # ...
    
      timeouts {
        create = "60m"
        update = "30m"
        delete = "2h"
      }
    }
    
    AttributeTypeDescription
    createstringTimeout for create operations.
    updatestringTimeout for update operations.
    deletestringTimeout for delete operations.

    Provisioners

    Provisioners run commands during resource lifecycle events. They map to the Pulumi Command provider.

    resource "aws_instance" "web" {
      ami           = "ami-12345678"
      instance_type = "t3.micro"
    
      provisioner "local-exec" {
        command = "echo ${self.public_ip} >> hosts.txt"
      }
    
      provisioner "remote-exec" {
        inline = [
          "sudo apt-get update",
          "sudo apt-get install -y nginx",
        ]
      }
    
      provisioner "file" {
        source      = "config.txt"
        destination = "/tmp/config.txt"
      }
    
      connection {
        type        = "ssh"
        user        = "ubuntu"
        private_key = file("~/.ssh/id_rsa")
        host        = self.public_ip
      }
    }
    
    Provisioner typePulumi equivalent
    local-execcommand:local:Command
    remote-execcommand:remote:Command
    filecommand:remote:CopyToRemote

    Provisioner blocks accept when ("create" or "destroy") and on_failure ("continue" or "fail") attributes. The self reference inside a provisioner refers to the parent resource. connection blocks support SSH only; WinRM is not supported.

    Referencing resources

    Resources are referenced by <type>.<name>:

    output "instance_id" {
      value = aws_instance.web.id
    }
    

    When using count, instances are indexed: aws_instance.web[0].id, or aws_instance.web[*].id for the splat form. When using for_each, instances are keyed: aws_instance.web["key"].id.

    Resource options

    In addition to the Terraform-like meta-arguments above, resource blocks accept Pulumi-specific resource options as top-level attributes.

    resource "aws_instance" "web" {
      # ...
    
      parent                     = module.my_component
      additional_secret_outputs  = ["password"]
      retain_on_delete           = true
      deleted_with               = aws_vpc.main
      replace_on_changes         = ["ami"]
      replace_with               = [aws_instance.replacement]
      hide_diffs                 = ["user_data"]
      replacement_trigger        = var.force_replace
      import_id                  = "i-1234567890abcdef0"
      aliases                    = ["old-name"]
      version                    = "6.0.0"
      plugin_download_url        = "https://example.com/plugins"
    }
    
    AttributeTypeDescription
    parentreferenceParent resource for component hierarchy.
    additional_secret_outputslist(string)Output properties to encrypt in state.
    retain_on_deleteboolKeep the cloud resource when removed from the program.
    deleted_withreferenceCascade deletion when the referenced resource is deleted.
    replace_withlistResources whose replacement triggers replacement of this one.
    hide_diffslist(string)Property paths whose diffs should not be displayed.
    replace_on_changeslist(string)Property paths that force replacement when changed.
    replacement_triggerexpressionExpression whose change triggers replacement.
    import_idstringCloud resource ID to import.
    aliaseslistAlternative names for this resource (used during renames).
    versionstringProvider plugin version.
    plugin_download_urlstringURL to download the provider plugin from.

    Provider resources (with type pulumi_providers_<name>) additionally accept an env_var_mappings attribute, which remaps environment variables for the provider.

    Data sources

    data blocks read information from providers via invocations. They use the same type-naming convention as resources, and results are referenced as data.<type>.<name>.<attribute>.

    data "aws_ami" "ubuntu" {
      most_recent = true
      owners      = ["099720109477"]
    
      filter {
        name   = "name"
        values = ["ubuntu/images/hvm-ssd/ubuntu-*-amd64-server-*"]
      }
    }
    
    output "ami_id" {
      value = data.aws_ami.ubuntu.id
    }
    

    Data sources support the same meta-arguments as resources: count, for_each, depends_on, and provider.

    Providers

    Providers supply the implementation for resources and data sources.

    Required providers

    Declare provider requirements inside the pulumi block:

    pulumi {
      required_providers {
        aws = {
          source  = "pulumi/aws"
          version = ">= 6.0"
        }
        random = {
          source  = "pulumi/random"
          version = "4.16.0"
        }
      }
    }
    

    Provider sources must use the pulumi/ namespace, not hashicorp/.

    Provider configuration

    Configure providers with provider blocks:

    provider "aws" {
      region = "us-west-2"
    }
    

    Multiple provider configurations

    Use alias to create multiple configurations of the same provider:

    provider "aws" {
      region = "us-west-2"
    }
    
    provider "aws" {
      alias  = "east"
      region = "us-east-1"
    }
    
    resource "aws_instance" "web" {
      provider = aws.east
      # ...
    }
    

    Provider resources

    Providers can also be declared as resource blocks for use with component resources:

    resource "pulumi_providers_aws" "explicit" {
      region = "us-west-2"
    }
    
    resource "aws_instance" "web" {
      providers = [pulumi_providers_aws.explicit]
      # ...
    }
    

    The pulumi_providers_<name> resource type creates an explicit provider instance. Pass it to component resources via the providers meta-argument.

    Outputs

    output blocks export values from the stack via pulumi stack output.

    output "instance_ip" {
      value       = aws_instance.web.public_ip
      description = "Public IP of the web server"
    }
    
    output "db_password" {
      value     = random_password.db.result
      sensitive = true
    }
    
    output "vpc_id" {
      value      = aws_vpc.main.id
      depends_on = [aws_internet_gateway.gw]
    
      precondition {
        condition     = aws_vpc.main.id != ""
        error_message = "VPC must have an ID."
      }
    }
    
    AttributeTypeRequiredDescription
    valueexpressionYesThe value to export.
    descriptionstringNoHuman-readable description.
    sensitiveboolNoWhen true, the output becomes a Pulumi secret.
    depends_onlistNoExplicit dependencies.
    preconditionblockNoValidation checks before export.

    Locals

    locals blocks define reusable intermediate values. Multiple locals blocks are allowed in a program; reference locals as local.<name>.

    locals {
      common_tags = {
        Environment = "dev"
        Project     = "my-project"
      }
    
      name_prefix = "myapp-${var.environment}"
    
      user_data = <<-EOF
        #!/bin/bash
        echo "Hello, World!" > index.html
        nohup python3 -m http.server 80 &
      EOF
    }
    
    resource "aws_instance" "web" {
      tags      = local.common_tags
      user_data = local.user_data
    }
    

    Modules

    module blocks invoke reusable configurations as Pulumi component resources. Module outputs are referenced as module.<name>.<output_name>.

    module "vpc" {
      source     = "./modules/vpc"
      cidr_block = "10.0.0.0/16"
    }
    
    output "vpc_id" {
      value = module.vpc.vpc_id
    }
    

    Module sources

    Source typeExample
    Local path./modules/vpc
    Gitgit::https://github.com/org/repo.git?ref=v1.0.0
    Git with subdirectorygit::https://github.com/org/repo.git//modules/vpc?ref=v1.0.0
    GitHub shorthandgithub.com/org/repo
    Bitbucket shorthandbitbucket.org/org/repo
    Terraform Registryterraform-aws-modules/vpc/aws
    HTTP archivehttps://example.com/module.zip

    Remote modules are cached in ~/.pulumi/modules/.

    Module meta-arguments

    ArgumentTypeDescription
    sourcestringModule source (required).
    versionstringVersion constraint (for registry modules).
    countnumberCreate multiple module instances.
    for_eachmap or setCreate keyed module instances.
    depends_onlistExplicit dependencies.
    providersmapProvider configuration mappings for the module.

    Call blocks

    call blocks invoke methods on existing resources. This is a Pulumi-specific extension with no Terraform equivalent.

    resource "aws_instance" "web" {
      ami           = "ami-12345678"
      instance_type = "t3.micro"
    }
    
    call "web" "get_password_data" {
    }
    
    output "password_data" {
      value = call.web.get_password_data.password_data
    }
    

    The first label is the logical name of a declared resource. The second label is the method name. The body contains arguments to the method. Results are referenced as call.<resource_name>.<method_name>.<attribute>.

    Moved and import blocks

    Moved blocks

    moved blocks rename resources without recreating them. They map to Pulumi’s aliases resource option.

    moved {
      from = aws_instance.old_name
      to   = aws_instance.new_name
    }
    
    AttributeTypeRequiredDescription
    fromreferenceYesOriginal resource address.
    toreferenceYesNew resource address.

    Import blocks

    import blocks import existing cloud resources into Pulumi state.

    import {
      to       = aws_instance.web
      id       = "i-1234567890abcdef0"
      provider = aws.east
    }
    
    AttributeTypeRequiredDescription
    toreferenceYesTarget resource address.
    idstringYesCloud resource ID to import.
    providerreferenceNoProvider configuration to use.

    Expressions

    Pulumi HCL supports the full HCL expression language.

    Literals

    "hello"           # string
    42                # number
    3.14              # number
    true              # bool
    null              # null
    ["a", "b", "c"]   # list
    {key = "value"}   # map
    

    String interpolation

    "Hello, ${var.name}!"
    "prefix-${local.env}-suffix"
    

    Heredocs

    <<-EOF
      multi-line
      string content
    EOF
    

    References

    ReferenceDescription
    var.<name>Input variable.
    local.<name>Local value.
    <type>.<name>Resource attribute.
    <type>.<name>[<index>]Counted resource instance.
    <type>.<name>["<key>"]For-each resource instance.
    data.<type>.<name>Data source attribute.
    module.<name>Module output.
    call.<res>.<method>Call block result.
    selfCurrent resource (in provisioners).
    count.indexCurrent count iteration index.
    each.keyCurrent for_each key.
    each.valueCurrent for_each value.
    path.modulePath to the current module.
    path.rootPath to the root module.
    path.cwdCurrent working directory.
    pulumi.stackCurrent stack name.
    pulumi.projectCurrent project name.
    pulumi.organizationCurrent organization name.

    Operators

    CategoryOperators
    Arithmetic+, -, *, /, %
    Comparison==, !=, <, <=, >, >=
    Logical&&, ||, !

    Conditional expression

    condition ? true_value : false_value
    

    For expressions

    # List comprehension
    [for name in var.names : upper(name)]
    
    # With index
    [for i, name in var.names : "${i}-${name}"]
    
    # Map comprehension
    {for k, v in var.tags : k => upper(v)}
    
    # With filter
    [for name in var.names : name if name != ""]
    

    Splat expressions

    # Equivalent to [for r in aws_instance.web : r.id]
    aws_instance.web[*].id
    

    Property access

    resource.name.property
    resource.name["key"]
    resource.name[0]
    

    try and can

    try(var.optional.nested.value, "default")
    can(var.optional.nested.value)  # returns true or false
    

    Built-in functions

    Pulumi HCL supports nearly all Terraform built-in functions, grouped by category below.

    CategoryFunctions
    Numericabs, ceil, floor, log, max, min, pow, signum, parseint
    Stringchomp, endswith, format, formatlist, indent, join, lower, regex, regexall, replace, split, startswith, strcontains, strrev, substr, title, trim, trimprefix, trimsuffix, trimspace, upper
    Collectionalltrue, anytrue, chunklist, coalesce, coalescelist, compact, concat, contains, distinct, element, entries, flatten, index, keys, length, list, lookup, map, matchkeys, merge, one, range, reverse, setintersection, setproduct, setsubtract, setunion, slice, sort, sum, transpose, values, zipmap
    Encodingbase64decode, base64encode, base64gzip, csvdecode, jsondecode, jsonencode, textdecodebase64, textencodebase64, urlencode, yamldecode, yamlencode
    Filesystemabspath, basename, dirname, file, filebase64, fileexists, fileset, pathexpand, templatefile
    Date and timeformatdate, timeadd, timecmp, timestamp
    Hash and cryptobase64sha256, base64sha512, bcrypt, filebase64sha256, filebase64sha512, filemd5, filesha1, filesha256, filesha512, md5, rsadecrypt, sha1, sha256, sha512, uuid, uuidv5
    IP networkcidrhost, cidrnetmask, cidrsubnet, cidrsubnets
    Type conversioncan, issensitive, nonsensitive, sensitive, tobool, tolist, tomap, tonumber, toset, tostring, try, type

    Pulumi-specific functions

    FunctionDescription
    fileAsset(path)Create a Pulumi FileAsset from a local file path.
    stringAsset(text)Create a Pulumi StringAsset from a string value.
    remoteAsset(uri)Create a Pulumi RemoteAsset from a URL.
    fileArchive(path)Create a Pulumi FileArchive from a local path.
    remoteArchive(uri)Create a Pulumi RemoteArchive from a URL.
    assetArchive(map)Create a Pulumi AssetArchive from a map of assets or archives.
    pulumiResourceName(resource)Get the logical name from a resource’s URN.
    pulumiResourceType(resource)Get the type token from a resource’s URN.

    Functions not supported

    These Terraform functions have no equivalent in Pulumi HCL:

    FunctionReason
    templatestringInline template-string rendering is not supported.
    plantimestampNo Pulumi equivalent for plan-time timestamps.
    ephemeralasnullPulumi has no ephemeral value concept.

    The provider::terraform::* provider functions and terraform.applying are Terraform-internal and have no equivalent.

    Stack references

    Access outputs from other Pulumi stacks using the pulumi_stackreference resource:

    resource "pulumi_stackreference" "network" {
      name = "myorg/networking/prod"
    }
    
    output "vpc_id" {
      value = pulumi_stackreference.network.outputs["vpc_id"]
    }
    

    Pulumi block

    The pulumi block configures Pulumi-specific settings.

    Version constraints

    pulumi {
      required_version_range = ">= 3.0.0"
    }
    

    Multi-language components

    The component and package blocks declare an HCL module as a reusable Pulumi component consumable from any Pulumi language. See the Pulumi HCL component reference for details.

    pulumi {
      component {
        name   = "VpcNetwork"
        module = "index"
      }
      package {
        name    = "my-networking"
        version = "1.0.0"
      }
    }
    

    Terraform compatibility

    Pulumi HCL is broadly compatible with Terraform-like HCL syntax. This section covers the differences.

    Required changes

    The top-level terraform block is not supported. Move provider requirements into the pulumi block, and use the pulumi/ namespace for provider sources instead of hashicorp/:

    pulumi {
      required_providers {
        aws = {
          source  = "pulumi/aws"  # not "hashicorp/aws"
          version = ">= 6.0"
        }
      }
    }
    

    Terraform’s backend, cloud, and required_version are not modeled — Pulumi manages state independently and uses pulumi { required_version_range = "..." } for its own version constraints.

    Behavioral differences

    Resource replacement order. Pulumi creates the new resource before deleting the old one (the opposite of Terraform). Set create_before_destroy = false in the lifecycle block to opt into delete-first behavior.

    Sensitive values. Variables and outputs marked sensitive = true become Pulumi secrets, encrypted at rest in state.

    Property names. HCL uses snake_case. The plugin automatically converts to Pulumi’s camelCase for the engine. Map keys are not translated.

    Feature mappings

    Terraform featurePulumi equivalentNotes
    prevent_destroyprotectSame behavior.
    ignore_changesignoreChangesSame behavior.
    create_before_destroydeleteBeforeReplaceInverted logic.
    moved blocksaliasesRenames without recreation.
    import blocksImport resource optionImports existing resources.
    timeoutscustomTimeoutsSame duration format.
    ModulesComponent resourcesAll source types supported.
    ProvisionersCommand providerlocal-exec, remote-exec, file.

    Unsupported features

    • replace_triggered_by — Terraform cascades replacement when other resources change. Use Pulumi HCL’s replace_with resource option for the same effect; the Terraform-like replace_triggered_by attribute on a lifecycle block produces an error.
    • dynamic blocks — Dynamic block generation is not implemented.
    • WinRM connectionsconnection blocks support type = "ssh" only.