wwr
wwr

Reputation: 327

packer templates and conditional statements

I'd like to use conditional statements in the packer template at the "provisioners" stage.

  "provisioners": [
    {
      "execute_command": "echo 'vagrant'|sudo -S sh '{{.Path}}'",
      "override": {
        "virtualbox-iso": {
          "scripts": [
            "scripts/base.sh",
            "scripts/puppet.sh",
          ]
        }
      },
      "type": "shell",
    }
  ]

For instance, if the user, at the "packer build" command line, specifies, somehow, a "puppet" parameter then then the "scripts/puppet.sh" will be executed otherwise skipped.

How can I do that?

Upvotes: 8

Views: 21285

Answers (3)

stackprotector
stackprotector

Reputation: 13588

As the title does not match the question entirely, some people might get here looking for if-then-else statements in packer template files (.pkrtpl.hcl). In this case, you can use a Jinja2-like syntax for if statements (docs):

%{ if os_family == "windows" ~}
    winrm_username = var.build_username
    winrm_password = var.build_password
%{ else ~}
    ssh_username   = var.build_username
    ssh_password   = var.build_password
%{ endif ~}

To solve the problem of the question body, you can use conditional expressions of the HCL2, if you use HCL2 instead of JSON[1] (docs):

scripts = var.myvar == "myvalue" ? ["scripts/base.sh", "scripts/puppet.sh"] : ["scripts/base.sh"]

[1] HCL2 is the preferred way to write Packer configuration (source).

Upvotes: 0

Sascha
Sascha

Reputation: 357

I don't think that this is possible with packers native template formate, because packer uses the JSON format for configuration which as long as I know does not support flow control mechanisms like the conditional statement. But it should be possible to archive a similar behaviour with a user variables and the shell provisioner.

The idea

The easiest way should be to set a user variable from the build command and pass this variables from packer to the shell provisioner script which detects the value of the user variable and calls the appropriate provisioner script e.g. puppet, salt,...

Disclaimer:

I didn't test this approach, but it should give you a hint to what I mean and maybe you can come up with an even better solution. ;-)

The problem solving approach

1. Define the variable which indicates the used provisioner:

There are multiple ways to define a user variable,

  • by calling the packer build command with the -var flag (see end of answere)

  • define the user variables in the box-template file

    The packer box template file: **template.json**
    
         "variables": [
             "using_provision_system": "puppet"
         ]
         "provisioners": [
              {...}
    
  • define a variable definition file and specify the path to it in the build command with -var-file

    The variable file: **variables.json**
    Is a great alternative if you want to define variables in a seperate file.
    
         {
            "using_provision_system": "puppet"
         }
    

2. Calling the shell script which calls the provisioner scripts:

Now modify the execute_command in a way that the 'master' script is called with the defined variable as argument.

"provisioners": [
    {
      "execute_command": "echo 'vagrant'|sudo -S sh '{{.Path}}' '{{user `using_provision_system`}}'",
      "override": {
        "virtualbox-iso": {
          "scripts": [
            call_provisioner_script.sh
          ]
        }
      },
      "type": "shell",
    }
  ]

Notice, we only need to specify one script. This one 'master' script takes the passed variable as argument and compares the value with some predefined provisioner names in a switch condition. (Short: It chooses which provisioner scripts will be executed.)

master provision script: call_provisioner_script.sh

case $1 in
    puppet*) sh puppet_provisioner.sh
    *) sh shell_provisioner.sh

Take care!
Because this script will run inside your box, you might need to upload the scripts to the box before this command gets executed.

3. Last step is to build your box :)

Calling packers build command:

#define variable in build command (first option from above)
$ packer build -var 'using_provision_system=puppet' template.json

#define variables in variable file and set path in build command
$ packer build -var-file=variables.json template.json

Instead of using user variables, there should maybe a way to set enviromental variables in your box and use this type of variable to specify the provisioner.

Upvotes: 10

jeffreyveon
jeffreyveon

Reputation: 13830

An alternative way is to define 2 different builders which are of the same type but having different names. You can then exclude a provisioning step from a specific build using the only field:

{
  "source": "foo.txt",
  "destination": "/opt/foo.txt",
  "type": "file",
  "only": ["docker-local"]
}

Upvotes: 7

Related Questions