Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

packer templates and conditional statements

Tags:

packer

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?

like image 924
wwr Avatar asked Nov 17 '14 09:11

wwr


People also ask

What is a Packer template?

A Packer template is a configuration file that defines the image you want to build and how to build it. Packer templates use the Hashicorp Configuration Language (HCL).

What is Provisioner in Packer?

Provisioners use built-in and third-party software to install and configure the machine image after booting. Provisioners prepare the system, so you may want to use them for the following use cases: installing packages. patching the kernel. creating users.

How can we make sure the sensitive variable are not printed in the Packer logs?

You can make sure that sensitive variables won't get printed to the logs by adding them to the "sensitive-variables" list within the Packer template: { "variables": { "my_secret": "{{env `MY_SECRET`}}", "not_a_secret": "plaintext", "foo": "bar" }, "sensitive-variables": ["my_secret", "foo"], ... }

What are builders in Packer?

Builders are components of Packer that are able to create a machine image for a single platform. Builders read in some configuration and use that to run and generate a machine image. A builder is invoked as part of a build in order to create the actual resulting images.


2 Answers

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.

like image 110
Sascha Avatar answered Sep 25 '22 09:09

Sascha


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"]
}
like image 26
jeffreyveon Avatar answered Sep 24 '22 09:09

jeffreyveon