Reputation: 327
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
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
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 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,...
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. ;-)
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
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