Backstory

So, I had to create some AMIs.

“Sure, no big deal”, I thought, “I’ll use Packer!”

The Packer templates themselves aren’t too bad. It kind of sucks to parse through Hashicorp’s documentation, but sometimes I just need some really explicit instructions. So I write my templates, they work after some debugging, life is good.

New problem: I need to set passwords for various accounts on these AMIs. Time to use some variables!

Except… the Hashicorp documentation on variables is… a little lacking, to be honest. It explains under the Standard Variable Definitions Files heading that the format should look like such:

image_id = "ami-abc123"
availability_zone_names = [
  "us-east-1a",
  "us-west-1c",
]

Easy enough, right? It’s just some variables being set in a separate file, then you bring it in with a command like so:

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

Error: Failed preparing provisioner-block "shell" ""

  on jenkins.pkr.hcl line 44:
  (source code not available)

jenkins.pkr.hcl:46,27-40: Unsupported attribute; This object does not have an
attribute named "jenkins_pass"., and 3 other diagnostic(s)

So… that error points us to the Important do not skip block in the docs:

Important: Unlike legacy JSON templates the input variables within a variable definitions file must be declared via a variables block within a standard HCL2 template file *.pkr.hcl before it can be assigned a value. Failure to do so will result in an unknown variable error during Packer’s runtime.

Okay, sure… I was a little confused here, to be honest. It sounded like I should be changing the name of the variable file to something like vars.pkr.hcl, but that didn’t work. Then it just asked me to set up variables blocks. And putting everything in the variable definition files in a variables block didn’t work, so what gives?

The Solution

Now that I read back on it, it makes sense. It did not when I was looking at it. So, dear reader, I hope this will help!

Defining Your Variables

So now we have some variable definitions; how do we get those values into our Packer templates?

vars.pkr.hcl - it can technically be named anything, but this name means I don’t get it confused with other stuff.

variable "web_pass" {
  type      = string
  default   = ""
  sensitive = true
}

variable "user_pass" {
  type      = string
  default   = ""
  sensitive = true
}

Notice that we left the default value set to nil. Also, since my use case was for passwords, I set the sensitive flag. This means when the variable is consumed, it won’t be printed in the console or log.

The Variable Definition File

This file should be named something with the extension .pkrvars.hcl. If you want it to be really fancy, as long as you’re only using one pkrvars file you can do .auto.pkrvars.hcl, and Packer will automagically pick up the variables file.

Your file should look something like this:

var1        = "hello"
var2        = 12345
var3        = ["a","b","c"]
...

Now, this file’s purpose is to assign values to existing variables, hence the warning they gave us. So, to make this work for us, we also need to define the variables in some kind of .pkr.hcl file. For me, this was just a vars.pkr.hcl file, like we discussed above.

So, we have two pieces of the puzzle now. Variable declarations, and variable definitions. These definitions will overwrite the default value of our declared variables. But how do we consume them?

Consuming Variables in Packer Templates

To call a variable in packer, you encapsulate it in ${var.x}. Not too bad, right?

Example:

... ${var.var1}
... ${var.var2}
... ${var.var3}

This part is actually way easier than it sounds, especially if you used Auto-loaded Variable Definitions Files. If you did, when you run packer build . it should Just Work.

If not, you’ll want to specific your variables file like so:

$ packer build -var-file=./vars.pkrvars.hcl

[build_output_here]

In Closing

And now, assuming nothing weird happened, you should have a Packer template consuming custom variables at runtime! The beautiful thing about doing vairables this way instead of locals is that they can be overwritten at runtime. So, for example:

packer build -var="key":"value" .

This would overwrite whatever variable name you give it (or create a new one if it doesn’t exist, at runtime).

Pretty nifty, right? I think so. But I’ll probably be writing a few more of these in the future… These docs could use some help, and if I’m using the tools anyway, I might as well help other people use them :)