Search code examples
packer

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?


Solution

  • 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.