How to pass argument in packer provision script? - packer

I am struggling to pass input parameter to packer provisioning script. I have tried various options but no joy.
Objective is my provision.sh should accept input parameter which I send during packer build.
packer build -var role=abc test.json
I am able to get the user variable in json file however I am unable to pass it provision script. I have to make a decision based on the input parameter.
I tried something like
"provisioners":
{
"type": "shell",
"scripts": [
"provision.sh {{user `role`}}"
]
}
But packer validation itself is failed with no such file/directory error message.
It would be real help if someone can help me on this.
Thanks in advance.

You should use the environment_vars option, see the docs Shell Provisioner - environment_vars.
Example:
"provisioners": [
{
"type": "shell"
"environment_vars": [
"HOSTNAME={{user `vm_name`}}",
"FOO=bar"
],
"scripts": [
"provision.sh"
],
}
]

If your script is already configured to use arguments; you simply need to run it as inline rather than in the scripts array.
In order to do this, the script must exist on the system already - you can accomplish this by copying it to the system with the file provisioner.
"provisioners": [
{
"type": "file",
"source": "scripts/provision.sh",
"destination": "/tmp/provision.sh"
},
{
"type": "shell",
"inline": [
"chmod u+x /tmp/provision.sh",
"/tmp/provision.sh {{user `vm_name`}}"]
}
]

Related

Can I have a scripts call each other? How does packer make it possible to compose many scripts?

I haven't found any examples of this so I am not sure it's possible.
I want to have utility scripts other packer scripts use. Can scripts call each other directly?
For example if inside of script1.ps1 it calls script2.ps1 (via relative path) will it be there? Will packer upload both files to the same location and the scripts can access each other?
{
"script": "./script1.ps1",
"type": "powershell"
},
{
"script": "./script2.ps1",
"type": "powershell"
}
What if I have them in diff folders like this, will I be able to access script2 inside script1 like ./../subdir/script2.ps1?
{
"script": "./otherdir/script1.ps1",
"type": "powershell"
},
{
"script": "./subdir/script2.ps1",
"type": "powershell"
}
Yes, you can certainly do that, although I am not sure what the real advantage is over running the two Powershell provisions like you are already are.
For the first script to call the second, the second script will already have to be present on the target machine. You can do the following:
{
"type": "file",
"source": "./script2.ps1",
"destination": "C:/Windows/Temp/script2.ps1"
},
{
"script": "./script1.ps1",
"type": "powershell"
}
Call script2 from script1 and then remove it:
& C:\Windows\Temp\script2.ps1
Remove-Item C:\Windows\Temp\script2.ps1

Get ami_name from inside a script in packer

I have a script in provisioner's section of my Packer template, which requires the variable $ami_name. Is there any way that I can reliably get this information?
Provisioners section
"provisioners": [
{
"type": "shell",
"execute_command": "sudo {{.Vars}} bash '{{.Path}}'",
"scripts": [
"./scripts/packer/myscript.sh"
]
}
]
./scripts/packer/myscript.sh:
#!/usr/bin/env bash
aminame=$ami_name
make_an_external_call "$aminame"
Here I need to get the ami_name from Packer. As this provisioners stage happens after Packer does a pre-validation of AMI Name, there should be a way I can get this right?

How to use user variables with file provisioner in Packer?

I have a packer json like:
"builders": [{...}],
"provisioners": [
{
"type": "file",
"source": "packer/myfile.json",
"destination": "/tmp/myfile.json"
}
],
"variables": {
"myvariablename": "value"
}
and myfile.json is:
{
"var" : "{{ user `myvariablename`}}"
}
The variable into the file does get replaced, is a sed replacement with shell provisioner after the file the only option available here?
Using packer version 0.12.0
You have to pass these as environment variables. For example:
"provisioners": [
{
"type": "shell"
"environment_vars": [
"http_proxy={{user `proxy`}}",
],
"scripts": [
"some_script.sh"
],
}
],
"variables": {
"proxy": null
}
And in the script you can use $http_proxy
So far I've come just with the solution to use file & shell provisioner. Upload file and then replace variables in file via shell provisioner which can be fed from template variables provided by e.g. HashiCorp Vault
Yo may use OS export function to set environment and pass it to Packer
Here is a config using OS ENV_NAME value to choose local folder to copy from
export ENV_NAME=dev will set local folder to dev
{
"variables": {
...
"env_folder": "{{env `ENV_NAME`}}",
},
"builders": [{...}]
"provisioners": [
{
"type": "file",
"source": "files/{{user `env_folder`}}/",
"destination": "/tmp/"
},
{...}
]
}
User variables must first be defined in a variables section within your template. Even if you want a user variable to default to an empty string, it must be defined. This explicitness helps reduce the time it takes for newcomers to understand what can be modified using variables in your template.
The variables section is a key/value mapping of the user variable name to a default value. A default value can be the empty string. An example is shown below:
{
"variables": {
"aws_access_key": "",
"aws_secret_key": ""
},
"builders": [{
"type": "amazon-ebs",
"access_key": "{{user `aws_access_key`}}",
"secret_key": "{{user `aws_secret_key`}}",
// ...
}]
}
check this link for more information

How to specify metadata for GCE in packer?

I'm trying to create a GCE image from packer template.
Here is the part that I use for that purpose.
"builders": [
...
{
"type": "googlecompute",
"account_file": "foo",
"project_id": "bar",
"source_image": "centos-6-v20160711",
"zone": "us-central1-a",
"instance_name": "packer-building-image-centos6-baz",
"machine_type": "n1-standard-1",
"image_name": "centos6-some-box-name",
"ssh_username": "my_username",
"metadata": {
"startup-script-log-dest": "/opt/script.log",
"startup-script": "/opt/startup.sh",
"some_other_custom_metadata_key": "some_value"
},
"ssh_pty": true
}
],
...
I have also created the required files. Here is that part
"provisioners": [
...
{
"type": "file",
"source": "{{user `files_path`}}/startup.sh",
"destination": "/opt/startup.sh"
},
...
{
"type": "shell",
"execute_command": "sudo sh '{{.Path}}'",
"inline": [
...
"chmod ugo+x /opt/startup.sh"
]
}
...
Everything works for me without "metadata" field. I can create image/instance with provided parameters. but when I try to create an instance from the image, I can't find the provided metadata and respectively I can't run my startup script, set logging file and other custom metadata.
Here is the source that I use https://www.packer.io/docs/builders/googlecompute.html#metadata.
Any suggestion will be helpful.
Thanks in advance
The metadata tag startup-script should contain the actuall script not a path. Provisioners run after the startup script has been executed (at least started).
Instead use startup_script_file in Packer and supply a path to a startup script.

sensu mailer and pipe

i'm switching over from nagios to sensu. i'm using chef to automated the process. everything is working great except the mailer or actually, i narrowed it down to the "pipe" that is suppose to redirect the json output from the check to the handler. it doesn't. when i use
{
"handlers": {
"email": {
"type": "pipe",
"command": "mail -s \"sensu alert\" alert#example.com",
"severities": [
"ok",
"critical"
]
}
}
}
i get a blank email. when i use the mailer.rb handler, i get no email whatsoever. i made sure to include mail to and mail from in the mailer.json. i see the logs have the correct information for the handler and email parameters.
so i've concluded the "pipe" isn't working. can anybody help with that? i would greatly appreciate it. i wish there was a sensu community, but it may be too new to have one.
With regards to the mailer.rb, have you checked the server logs (by default in /var/log/sensu/sensu-server.log) for errors? If there is an error in any of the handlers, they will show up in those logs.
mailer.rb requires several gems in order to run. To find out if you are using sensu's embedded ruby or not, check /etc/default/sensu for EMBEDDED_RUBY. If that is false, you will need to make sure your system ruby has all those gems (sensu-handler, mail, timeout) installed. If it is set to true, do the same with sensu's embedded ruby:
/opt/sensu/embedded/bin/gem list
Make sure the gems are installed, try again, and check the sensu-server.log for errors.
If you have more issues, there is in fact a community - check out #sensu on Freenode.
You can write you own event data JSON and pass it through a PIPE as follows:
cat event.json | /opt/sensu/embedded/bin/ruby mailer.rb
The easiest way to get the event.json file is from the sensu-server.log.
To use mailer.rb you need your own mail server ! if you'll post sensu server logs i think i can help you.
I've done some testing and the mail into pipe does not with GNU mail/mailx (assume you're using Ubuntu or something?).
Two solutions:
1) install BSD mail:
sudo apt-get install bsd-mailx
2) Or modify the command slightly get mail to read from stdin you'll need to do something like:
{
"handlers": {
"email": {
"type": "pipe",
"command": " echo $(cat) > /tmp/mail.txt; mail -s \"sensu alert\" alert#example.com < /tmp/mail.txt"
}
}
}
The idea is normally that you read the event json from stdin within a scripting language and then pull out bits of the event.json that you want to send. The above will e-mail out the entire json file.
You can use sensu mailer handler. Please find below steps to setup:-
sensu-install -p sensu-plugins-mailer
apt-get install postifx
/etc/init.d/postfix start
cd /etc/sensu/conf.d/
when we install this plugin will get 3 ruby files.
This time we are using this file:- handler-mailer.rb
First we need to creat handler file in this location /etc/sensu/conf.d/ :-
vim handler-mailer.json
{
"mailer": {
"admin_gui": "http://127.0.0.1:3000/",
"mail_from": "localhost",
"mail_to": ["yourmailid-1","yourmailid-2"],
"smtp_address": "localhost",
"smtp_port": "25"
}
}
Now we need to create one mail handler file in this location /etc/sensu/conf.d/:-
{
"handlers": {
"mymailer": {
"type": "pipe",
"command": "/opt/sensu/embedded/bin/handler-mailer.rb",
"severities": [
"critical",
"unknown"
]
}
}
}
in above file handler name is mymailer we need to use this handler name in our checks.
Use bin/handler-mailer-mailgun.rb or bin/handler-mailer-ses.rb or bin/handler-mailer.rb
Example:
echo '{
"id": "ef6b87d2-1f89-439f-8bea-33881436ab90",
"action": "create",
"timestamp": 1460172826,
"occurrences": 2,
"check": {
"type": "standard",
"total_state_change": 11,
"history": ["0", "0", "1", "1", "2", "2"],
"status": 2,
"output": "No keepalive sent from client for 230 seconds (>=180)",
"executed": 1460172826,
"issued": 1460172826,
"name": "keepalive",
"thresholds": {
"critical": 180,
"warning": 120
}
},
"client": {
"timestamp": 1460172596,
"version": "1.0.0",
"socket": {
"port": 3030,
"bind": "127.0.0.1"
},
"subscriptions": [
"production"
],
"environment": "development",
"address": "127.0.0.1",
"name": "client-01"
} }' | /opt/sensu/embedded/bin/handler-mailer-mailgun.rb
Output:
mail -- sent alert for client-01/keepalive to your.email#example.com