From c16354d5947446d7852a82d77152f0136ddee1a1 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Tue, 13 Oct 2020 14:28:08 -0700 Subject: [PATCH 1/3] Update HCL docs with a local resource-free example to empower users to tinker. --- website/pages/guides/hcl/variables.mdx | 210 ++++++++++++++++++++----- 1 file changed, 167 insertions(+), 43 deletions(-) diff --git a/website/pages/guides/hcl/variables.mdx b/website/pages/guides/hcl/variables.mdx index ac0b1e760..763e1be5d 100644 --- a/website/pages/guides/hcl/variables.mdx +++ b/website/pages/guides/hcl/variables.mdx @@ -18,54 +18,124 @@ Local variables can be a compound of input variables and local variables. ## Defining Variables and locals -Let's create a file `variables.pkr.hcl` with the following contents. +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 ` to initiate -a build. +across multiple files, use +`packer build ` to initiate a build. ```hcl -# variables.pkr.hcl +// variables.pkr.hcl -variable "access_key" {} -variable "secret_key" {} -variable "region" { - default = "us-east-1" +// 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 { - debian_ami_name = "${var.image_id}-debian" + ice_cream_flavor = "${var.flavor}-ice-cream" foo = "bar" } ``` -This defines three variables within your Packer configuration. The first -two have empty blocks `{}`. The third sets a default. If a default value is -set, the variable is optional. Otherwise, the variable is required. -This also defines two locals: `debian_ami_name` and `foo`. +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 `{}`. This will work under many simple circumstances, and Packer will +guess what type the variable should be at runtime. --> **Note**: that it is _not_ possible to use variables in a variable definition -but it _is_ possible to use locals and variables in a local definition. +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. + +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. + +This also defines two locals: `ice_cream_flavor` and `foo`. + +-> **Note**: that it is _not_ possible to 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 -Next, you can define a source using the variables : +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. ```hcl -# source.pkr.hcl +// null_example.pkr.hcl -source "amazon-ebs" "debian" { - ami_name = local.debian_ami_name - access_key = var.aws_access_key - secret_key = "${var.aws_secret_key}" - region = "${var.aws_region}" +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 golang template engine syntax rather than our + // specialized HCL2 variable syntax. This example shows a combination of + // an HCL2 variable and the golang 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 + } } ``` -This uses more interpolations, this time prefixed with `var.` and `local.`. -This tells Packer that you're accessing variables. This configures the builder -with the given variables. +As you can see in the example, you can acces 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 golang +templating engine syntax `{{ .OPTION }}` for those engines. ## Assigning Variables @@ -80,15 +150,14 @@ You can set variables directly on the command-line with the ```shell-session $ packer build \ - -var 'access_key=foo' \ - -var 'secret_key=bar' -# ... + -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. @@ -168,17 +237,48 @@ this file. Create a file named `variables.pkrvars.hcl` with the following contents: ```hcl -access_key = "foo" -secret_key = "bar" +sudo_password = "partyparrot" +weekday = "Sunday" ``` -For all files which match `*.auto.pkrvars.hcl` present in the current -directory, Packer automatically loads them to populate variables. If the file -is named something else, you can use the `-var-file` flag directly to specify a -file. These files are the same syntax as Packer configuration files. And like -Packer configuration files, these files can also be JSON. +You tell Packer to use this var file using the `-var-file` command line flag. -We don't recommend saving usernames and password to version control, but you +```shell-session +$ 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 var-file from variables.pkrvars.hcl to +variables.auto.pkrvars.hcl, then we can run our build simply by calling + +```shell-session +$ packer build . +``` + +You may provide as many -var-file flags as you would like: + +```shell-session +$ packer build \ + -var-file="secret.pkrvars.hcl" \ + -var-file="production.pkrvars.hcl" . +``` + +These files can also be JSON: + +variables.json: +```json +{ + "weekday": "sunday", + "flavor": "mint" +} +``` + +```shell-session +$ 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 @@ -187,7 +287,7 @@ checked in to version control and others not checked in. For example: ```shell-session $ packer build \ -var-file="secret.pkrvars.hcl" \ - -var-file="production.pkrvars.hcl" + -var-file="production.pkrvars.hcl" . ``` #### From environment variables @@ -196,18 +296,42 @@ 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. +```shell-session +$ 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. -#### Unspecified values fails +If all of your variables have defaults, then you can call a packer build using: -If you execute `packer build` with certain variables unspecified and those are -used somewhere, Packer will error. +```shell-session +$ packer build . +``` -## Lists +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: + +```shell-session +$ 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 @@ -225,7 +349,7 @@ You can specify lists in a `variables.pkrvars.hcl` file: cidrs = [ "10.0.0.0/16", "10.1.0.0/16" ] ``` -## Maps +### 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 From 00cc425b841516c7dac8ef2bb8d1d5e7d6962981 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Wed, 14 Oct 2020 12:58:04 -0700 Subject: [PATCH 2/3] docs tweaks --- command/build.go | 2 +- command/console.go | 2 +- command/validate.go | 2 +- contrib/zsh-completion/_packer | 4 +-- website/pages/guides/hcl/variables.mdx | 37 +++++++++++++++----------- 5 files changed, 27 insertions(+), 20 deletions(-) diff --git a/command/build.go b/command/build.go index 43d462082..38901701e 100644 --- a/command/build.go +++ b/command/build.go @@ -390,7 +390,7 @@ Options: -parallel-builds=1 Number of builds to run in parallel. 1 disables parallelization. 0 means no limit (Default: 0) -timestamp-ui Enable prefixing of each ui output with an RFC3339 timestamp. -var 'key=value' Variable for templates, can be used multiple times. - -var-file=path JSON file containing user variables. + -var-file=path JSON or HCL2 file containing user variables. ` return strings.TrimSpace(helpText) diff --git a/command/console.go b/command/console.go index 38bc1c726..93a636d3c 100644 --- a/command/console.go +++ b/command/console.go @@ -81,7 +81,7 @@ Usage: packer console [options] [TEMPLATE] Options: -var 'key=value' Variable for templates, can be used multiple times. - -var-file=path JSON file containing user variables. [ Note that even in HCL mode this expects file to contain JSON, a fix is comming soon ] + -var-file=path JSON or HCL2 file containing user variables. [ Note that even in HCL mode this expects file to contain JSON, a fix is comming soon ] ` return strings.TrimSpace(helpText) diff --git a/command/validate.go b/command/validate.go index 8fa60df03..6c0af4787 100644 --- a/command/validate.go +++ b/command/validate.go @@ -92,7 +92,7 @@ Options: -except=foo,bar,baz Validate all builds other than these. -only=foo,bar,baz Validate only these builds. -var 'key=value' Variable for templates, can be used multiple times. - -var-file=path JSON file containing user variables. [ Note that even in HCL mode this expects file to contain JSON, a fix is comming soon ] + -var-file=path JSON or HCL2 file containing user variables. [ Note that even in HCL mode this expects file to contain JSON, a fix is comming soon ] ` return strings.TrimSpace(helpText) diff --git a/contrib/zsh-completion/_packer b/contrib/zsh-completion/_packer index fe1905ced..7653991ea 100644 --- a/contrib/zsh-completion/_packer +++ b/contrib/zsh-completion/_packer @@ -20,7 +20,7 @@ _packer () { '-parallel=[(false) Disable parallelization. (Default: false)]' '-parallel-builds=[(0) Number of builds to run in parallel. (Defaults to infinite: 0)]' '-var[("key=value") Variable for templates, can be used multiple times.]' - '-var-file=[(path) JSON file containing user variables.]' + '-var-file=[(path) JSON or HCL2 file containing user variables.]' '(-)*:files:_files -g "*.json"' ) @@ -34,7 +34,7 @@ _packer () { '-except=[(foo,bar,baz) Validate all builds other than these.]' '-only=[(foo,bar,baz) Validate only these builds.]' '-var[("key=value") Variable for templates, can be used multiple times.]' - '-var-file=[(path) JSON file containing user variables.]' + '-var-file=[(path) JSON or HCL2 file containing user variables.]' '(-)*:files:_files -g "*.json"' ) diff --git a/website/pages/guides/hcl/variables.mdx b/website/pages/guides/hcl/variables.mdx index 763e1be5d..e48a340c9 100644 --- a/website/pages/guides/hcl/variables.mdx +++ b/website/pages/guides/hcl/variables.mdx @@ -72,14 +72,18 @@ locals { 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 `{}`. This will work under many simple circumstances, and Packer will -guess what type the variable should be at runtime. +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, @@ -87,14 +91,17 @@ 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. +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 +``. This also defines two locals: `ice_cream_flavor` and `foo`. --> **Note**: that it is _not_ possible to 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. +-> **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 @@ -119,19 +126,19 @@ build { // specialized HCL2 variable syntax. This example shows a combination of // an HCL2 variable and the golang template engines built into the // execute_command option - execute_command = ["/bin/sh", "-c", "echo ${var.sudo_password}| {{.Vars}} {{.Script}}"] + 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"] + 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}"] + 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 acces your variables directly by +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 golang @@ -178,7 +185,7 @@ build { "source.null.example" ] provisioner "shell-local" { - inline = ["echo $PIZZA"] + inline = ["echo $PIZZA"] environment_vars = ["PIZZA=${var.pizza}"] } } @@ -206,7 +213,7 @@ build { "source.null.example" ] provisioner "shell-local" { - inline = ["echo $PIZZA"] + inline = ["echo $PIZZA"] environment_vars = ["PIZZA=${var.pizza}"] } } @@ -238,7 +245,7 @@ contents: ```hcl sudo_password = "partyparrot" -weekday = "Sunday" +weekday = "Sunday" ``` You tell Packer to use this var file using the `-var-file` command line flag. From 31df482b61c6c1180bf95e6911437f0f9985fc7d Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Wed, 14 Oct 2020 13:11:52 -0700 Subject: [PATCH 3/3] remove old docs for previous behavior --- website/pages/guides/hcl/variables.mdx | 71 +------------------------- 1 file changed, 2 insertions(+), 69 deletions(-) diff --git a/website/pages/guides/hcl/variables.mdx b/website/pages/guides/hcl/variables.mdx index e48a340c9..ada556c12 100644 --- a/website/pages/guides/hcl/variables.mdx +++ b/website/pages/guides/hcl/variables.mdx @@ -167,75 +167,8 @@ 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. - -For example: - -```hcl -variable "pizza" { - type = string -} - -source "null" "example" { - communicator = "none" -} - -build { - sources = [ - "source.null.example" - ] - provisioner "shell-local" { - inline = ["echo $PIZZA"] - environment_vars = ["PIZZA=${var.pizza}"] - } -} -``` - -If you call the above template using the command - -```sh -packer build -var pizza=pineapple shell_local_variables.pkr.hcl -``` - -then the Packer build will run successfully. However, if you define the variable -using an empty block, the parser will not know what type the variable is, and it -cannot infer the type from the command line, as shown in this example: - -```hcl -variable "pizza" {} - -source "null" "example" { - communicator = "none" -} - -build { - sources = [ - "source.null.example" - ] - provisioner "shell-local" { - inline = ["echo $PIZZA"] - environment_vars = ["PIZZA=${var.pizza}"] - } -} -``` -The above template will result in the error: - -``` -Error: Variables not allowed - - on line 1: - (source code not available) - -Variables may not be used here. -``` - -You can work around this either by quoting the variable on the command line, or -by adding the type to the variable block as shown in the previous example. -Setting the expected type is the more resilient option. - -```sh -packer build -var 'pizza="pineapple"' shell_local_variables.pkr.hcl -``` +HCL parser understand what is being set. Otherwise it will interpret all of your +command line variables as strings. #### From a file