• HashiCorp Developer

  • HashiCorp Cloud Platform
  • Terraform
  • Packer
  • Consul
  • Vault
  • Boundary
  • Nomad
  • Waypoint
  • Vagrant
Packer
  • Install
  • Tutorials
  • Documentation
  • Guides
  • Plugins
  • Try Cloud(opens in new tab)
  • Sign up
Packer Home

Guides

Skip to main content
  • Guides
  • Upgrade Your Plugin to use the Packer plugin sdk
  • Upgrade Your Template to use Packer init
    • Overview
    • Variables
    • Upgrade Packer JSON Template to HCL2
    • Making a plugin HCL2 enabled

  • Resources

  • Tutorial Library
  • Community Forum
    (opens in new tab)
  • Support
    (opens in new tab)
  • GitHub
    (opens in new tab)
  1. Developer
  2. Packer
  3. Guides
  4. HCL guides
  5. Variables
  • Packer
  • v1.7.x
  • v1.6.x
  • v1.5.x

ยปInput Variables and local variables

Note: Packer version 1.5.0 introduced support for HCL2 templates as a beta feature. As of version 1.7.0, HCL2 support is no longer in beta and is the preferred way to write Packer configuration(s).

This page introduces input variables and local variables as a way to parameterize a configuration. Once defined, input variables are settable from a default value, environment, special var files, and command line arguments. Local variables can be a compound of input variables and local variables.

Defining Variables and locals

In the legacy JSON Packer templates, any variables we hadn't already defined in the "variables" stanza of our json template could simply be passed in via the command line or a var-file; if a variable was never defined it would generally be interpolated to an empty string.

In the HCL2 Packer templates, we must always pre-define our variables in the HCL equivalent of the "variables" stanza.

Another difference between JSON and HCL Packer templates is that in JSON packer templates, the "variables" stanza, if used, was always in the same .json file as the builds and builders. In HCL, it can exist in its own file if you want it to.

To demonstrate, let's create a file variables.pkr.hcl with the following contents.

Note: that the file can be named anything, since Packer loads all files ending in .pkr.hcl in a directory. If you split your configuration across multiple files, use packer build <command line flags> <source directory> to initiate a build.

//  variables.pkr.hcl

// For those variables that you don't provide a default for, you must
// set them from the command line, a var-file, or the environment.

variable "weekday" {}

variable "sudo_password" {
  type =  string
  default = "mypassword"
  // Sensitive vars are hidden from output as of Packer v1.6.5
  sensitive = true
}

variable "flavor" {
  type = string
  default = "strawberry"
}

variable "exit_codes" {
  type = list(number)
  default = [0]
}

locals {
  ice_cream_flavor = "${var.flavor}-ice-cream"
  foo             = "bar"
}

This defines several variables within your Packer configuration, each showing off a different way to set them. The first variable, "weekday", is an empty block {}, without a type or a default.

However, it's generally best to provide the type in your variable definition, as you can see in variable "flavor", which we have given a type of "string", and variable "exit_codes", which we have given a type of "list(number)", meaning it is a list/array of numbers.

When a variable is passed from the cli or environment and the variable's type is not set, Packer will expect it to be a string. But if it is passed from a var-file where Packer can interpret HCL properly it can be a slice or any supported type.

In addition to setting the type, the "flavor" and "exit_codes" variables also provide a default. If you set a default value, then you don't need to set the variable at run time. Packer will use a provided command-line var, var-file, or environment var if it exists, but if not Packer will fall back to this default value.

If you do not set a default value, Packer will fail immediately when you try to run a build if you have not provided the missing variable via the command-line, a var-file, or the environment. The validate, inspect and console commands will work, but variables with unknown values will be <unknown>.

This also defines two locals: ice_cream_flavor and foo.

Note: that it is not possible to reference a variable in the definition of another variable. But it is possible to use locals and variables in the definition of a local, as shown in the ice_cream_flavor definition.

Using Variables and locals in Configuration

For simplicity's sake, we're going to put a null source in the same file as we are putting the build configuration. This file demonstrates how to use the variables we previously defined.

// null_example.pkr.hcl

source "null" "example" {
    communicator = "none"
}

build {
  sources = [
    "source.null.example"
  ]
  provisioner "shell-local" {
    // Note that for options that are documented as template engines,
    // we still have to use the Go template engine syntax rather than our
    // specialized HCL2 variable syntax. This example shows a combination of
    // an HCL2 variable and the Go template engines built into the
    // execute_command option
    execute_command  = ["/bin/sh", "-c", "echo ${var.sudo_password}| {{.Vars}} {{.Script}}"]
    environment_vars = ["HELLO_USER=packeruser", "UUID=${build.PackerRunUUID}"]
    inline           = ["echo the Packer run uuid is $UUID"]
  }
  provisioner "shell-local" {
    inline           = ["echo var.flavor is: ${var.flavor}",
                        "echo local.ice_cream_flavor is: ${local.ice_cream_flavor}"]
    valid_exit_codes = var.exit_codes
  }
}

As you can see in the example, you can access your variables directly by giving them the var. or local. prefix. If you want to embed the variables in a string, you can do so with the ${} HCL interpolation syntax. If you are using an option that is a template engine, you still need to use the Go templating engine syntax {{ .OPTION }} for those engines.

Assigning Variables

There are multiple ways to assign variables. Below is also the order in which variable values are chosen. The following is the descending order of precedence in which variables are considered. So first defined; first used.

Command-line flags

You can set variables directly on the command-line with the -var flag.:

$ packer build \
  -var "weekday=Sunday" \
  -var "flavor=chocolate" \
  -var "sudo_password=hunter42" .

Once again, setting variables this way will not save them, and they'll have to be input repeatedly as commands are executed.

If you plan to assign variables via the command line, we strongly recommend that you at least set a default type instead of using empty blocks; this helps the HCL parser understand what is being set. Otherwise it will interpret all of your command line variables as strings.

From a file

To persist variable values, create a file and assign variables within this file. A variable definitions file uses the same basic syntax as Packer language files, but consists only of variable name assignments.

sudo_password = "partyparrot"
weekday       = "Sunday"

When assigning values in a variable definitions file please make sure to quote any string values, wrap list values within brackets [...], and wrap maps within curly-braces {...}.

Given a file named variables.pkrvars.hcl Packer can be instructed to use the variable definitions file by using the -var-file command line flag.

$ packer build -var-file="variables.pkrvars.hcl" .

Packer will automatically load any var file that matches the name *.auto.pkrvars.hcl, without the need to pass the file via the command line. If we rename the above variable definitions file from variables.pkrvars.hcl to variables.auto.pkrvars.hcl, then we can run our build simply by calling

$ packer build .

You may provide as many -var-file flags as you would like:

$ packer build \
  -var-file="secret.pkrvars.hcl" \
  -var-file="production.pkrvars.hcl" .

These files can also be JSON:

variables.json:

{
  "weekday": "sunday",
  "flavor": "mint"
}
$ packer build -var-file=variables.json

We don't recommend saving sensitive information to version control, but you can create a local secret variables file and use -var-file to load it.

You can use multiple -var-file arguments in a single command, with some checked in to version control and others not checked in. For example:

$ packer build \
  -var-file="secret.pkrvars.hcl" \
  -var-file="production.pkrvars.hcl" .

From environment variables

Packer will read environment variables in the form of PKR_VAR_name to find the value for a variable. For example, the PKR_VAR_access_key variable can be set to set the access_key variable.

$ export PKR_VAR_weekday=Monday
$ packer build .

Variable Defaults

If no value is assigned to a variable via any of these methods and the variable has a default key in its declaration, that value will be used for the variable.

If all of your variables have defaults, then you can call a packer build using:

$ packer build .

You can make this work for yourself using the variable example file above by commenting out or removing the "weekday" variable declaration, since it is not actually used in the example build.

If your variable definitions are stored in the same file as your source and build, you can call the build against that specific file:

$ packer build self_contained_example.pkr.hcl

Unspecified Values Fail

If you call packer build with any variables defined but not set, Packer will error.

Variable Type Reference

Lists

Lists are defined either explicitly or implicitly

# implicitly by using brackets [...]
variable "cidrs" { default = [] }

# explicitly
variable "cidrs" { type = list }

You can specify lists in a variables.pkrvars.hcl file:

cidrs = [ "10.0.0.0/16", "10.1.0.0/16" ]

Maps

Maps are a way to create variables that are lookup tables. An example will show this best. Let's extract our AMIs into a map and add support for the us-west-2 region as well:

variable "amis" {
  type = map(string)
  default = {
    "us-east-1" = "ami-b374d5a5"
    "us-west-2" = "ami-4b32be2b"
  }
}

A variable can have a map type assigned explicitly, or it can be implicitly declared as a map by specifying a default value that is a map. The above demonstrates both.

Then we can lookup in maps like in the following:

source "amazon-ebs" "example" {
  source_ami    = "${lookup(var.amis, var.region)}"
  instance_type = "t2.micro"
}

This introduces a new type of interpolation: a function call. The lookup function does a dynamic lookup in a map for a key. The key is var.region, which specifies that the value of the region variables is the key.

You can also do a static lookup of a map directly with ${var.amis["us-east-1"]}.

Assigning Maps

We set defaults above, but maps can also be set using the -var and -var-file values. For example:

$ packer build -var 'amis={ us-east-1 = "foo", us-west-2 = "bar" }'
# ...

Here is an example of setting a map's keys from a file. Starting with these variable definitions:

variable "region" {}
variable "amis" {
  type = map
}

You can specify keys in a variables.pkrvars.hcl file:

amis = {
  "us-east-1" = "ami-abc123"
  "us-west-2" = "ami-def456"
}

And access them via lookup():

output "ami" {
  value = "${lookup(var.amis, var.region)}"
}

Like so:

$ packer build -var region=us-west-2
Edit this page on GitHub

On this page

  1. Input Variables and local variables
  2. Defining Variables and locals
  3. Using Variables and locals in Configuration
  4. Assigning Variables
  5. Variable Type Reference
  6. Assigning Maps
Give Feedback(opens in new tab)
  • Certifications
  • System Status
  • Terms of Use
  • Security
  • Privacy
  • Trademark Policy
  • Trade Controls
  • Give Feedback(opens in new tab)