Packer Tutorial For Beginners – Automate AMI Creation
- Last Updated On: January 30, 2019
- By: devopscube
Packer is a VM image creation tool. It lets you automate the process of image creation on various on-premise and cloud solutions. If you are setting up an immutable infrastructure model using VM’s,
Packer Tutorial For Beginners
In this beginner tutorial, we have covered the steps required to get started with packaging AMI’s on AWS cloud.
Installing Packer on Workstation
You can have the packer installation on your local workstation or on a cloud instance. All you need is the cli connectivity to AWS using access keys.
Note: If you are planning to setup Packer on the local workstation, make sure you have the aws access keys set on file ~/.aws/credentials. It is not mandatory to have credentials file. You can pass the AWS credentials using the packer variable declaration.
1. Download the required package from here. https://www.packer.io/downloads.html
2. Unzip the package and set the path variable in ~/.bashrc
export PATH=$PATH:/path/to/packer
3. Refresh terminal
source ~/.bashrc
4. Verify packer installation by executing the packer command.
packer version
Building Image
Packer configuration templates are written in JSON format.
A template has the following three main parts.
1. variables – Where you define custom variables.
2. builders – Where you mention all the required AMI parameters.
3. provisioners – Where you can integrate a shell script, ansible play or a chef cookbook for configuring a required application in the AMI.
An example template for packaging an AWS AMI is given below.
{
"variables": {
"aws_access_key": "",
"aws_secret_key": ""
},
"builders": [{
"type": "amazon-ebs",
"access_key": "{{user `aws_access_key`}}",
"secret_key": "{{user `aws_secret_key`}}",
"region": "us-west-1",
"source_ami": "ami-sd3543ds",
"instance_type": "t2.medium",
"ssh_username": "ec2-user",
"ami_name": "packer-demo {{timestamp}}"
}],
"provisioners": [
{
"type": "shell",
"script": "demo-script.sh"
}
]
}
In the above example configuration, we are passing the AWS access keys and secret keys as variables. It is not recommended to use access keys as variables. If you have already set the credentials in ~/.aws/credentials file, you don’t need to pass access keys as variables.
Also, we have used shell provisioner which calls a demo-script.sh file.
packer supports the following provisioners.
- Ansible
- Chef
- Salt
- Shell
- Powershell
- Windows cmd
- File – For copying file from local directory to VM image.
Building a Packer Template
To build a packer template, you just need to execute the build command with the JSON template.
For example,
packer build apache.json
Before jumping into building the template, let’s look ar some more important concepts and at last we can try out an example with shell provisioner.
Different Scenarios for Using Variables
You can make use for packer variables for dynamic configuration fo packaging AMI. Let’s discuss these scenarios one by one.
Using Variable Within Template
The variables block holds all the default variables within a template. Asn example is shown below.
"variables": {
"instance_type": "t2.medium",
"region": "us-west-1"
}
The declared variables can be accessed in other parts of the template using "{{user `your-variable-name`}}" syntax. An example is shown below.
"instance_type": "{{user `instance_type`}}",
"region": "{{user `region`}}"
Using Environment Variables in Templates
Packer lets you use the system environment variables. First, you need to declare the environment variables in the variable section to use it in the other parts of the template.
Lets say, you want to use the SCRIPT_PATH environment variable that holds the path to a shell script that has to be used in the shell provisioner. You can declare that variable like shown below.
"variables": {
"script_path": "{{env `SCRIPT_PATH`}}",
}
After the declaration, you can use the script_pathvariable in the provisioner like shown below.
"provisioners": [
{
"type": "shell",
"script": "{{user `script_path` }}/demo-script.sh"
}
]
Using Command Line Variables
First, you need to declare the variable name in the variable block as shown below.
"app_name": "{{app_name_cmd_var}}"
You can pass variables during the run time using -var flag followed by the variable declaration.
You can use this variable in the template using the normal interpolation we used in the above examples.
For example,
packer build -var 'app_name_cmd_var=apache' apache.json
Using a JSON File
You can pass a JSON file with variables block to the build command as shown below.
packer build -var-file=variables.json apache.json
In the above example, variables.json is the variable file and apache.json is the packer template.
Packaging an Image
In this example, we will bake a t2.micro AMI using a shell provisioner. A shell script which has an update and HTTPD install instruction.
We assume that you have the AWS access keys and region set in the ~/.aws/credentials file.
Here we are going to user Oregon region and a Redhat AMI with AMI id ami-6f68cf0f
Follow the steps given below to set up the project.
1. Create a folder call packer
mkdir packer
2. Create a script file named demo-script.sh and copy the following contents on to it.
#!/bin/bash sudo yum -y update sudo yum install -y httpd
The above script just updates the repository and installs httpd.
3. Create a httpd.json file with the following contents.
{
"variables": {
"ami_id": "ami-6f68cf0f",
"app_name": "httpd"
},
"builders": [{
"type": "amazon-ebs",
"region": "eu-west-1",
"source_ami": "{{user `ami_id`}}",
"instance_type": "t2.micro",
"ssh_username": "ec2-user",
"ami_name": "PACKER-DEMO-{{user `app_name` }}",
"tags": {
"Name": "PACKER-DEMO-{{user `app_name` }}",
"Env": "DEMO"
}
}],
"provisioners": [
{
"type": "shell",
"script": "demo-script.sh"
}
]
}
We have our template ready. Next step is to execute the template to for packaging the AMI with HTTPD installation.
4. Lets validate and inspect our template using the following commands.
packer validate httpd.json packer inspect httpd.json
Note: If you are using command line variables or a file as a variable, you should pass it while validating it. An example is shown below.
packer validate -var='env=TEST' httpd.json
The above command should validate and inspect without any errors.
5. To build our new AMI, use the following command.
packer build httpd.json
The above command will build a new AMI.
You can also debug the image creation. Check out this thread for packer debugging.
Few Tips
1. You can capture the output of the image build to a file using the following command.
packer build httpd.json 2>&1 | sudo tee output.txt
2. Then you can extract the new AMI id to a file named ami.txt using the following command.
tail -2 output.txt | head -2 | awk 'match($0, /ami-.*/) { print substr($0, RSTART, RLENGTH) }' > sudo ami.txt
In this packer tutorial for beginners, we covered the basics of image creation. Let us know in the comment section if you face any issues in the setup.
devopscube
Other Interesting Blogs

Git Basics Every Developer and Administrator Should Know
Version control systems are repositories used to version your code/scripts in a collaborative manner with the advantages of tracking changes as features

Setup Jenkins master and Build Slaves as Docker Container
Do you want dockerized Jenkins which includes the configuration for build slaves also as Docker containers? So that you can run this image using

What is Docker? How Does it Work?
Docker has become the defacto standard when it comes to container-based implementations. From small scale implementations to large scale enterprise applications, docker


Comments
will the new AMI gets saved in AWS ? how to control the permission of AMI ? like the image created is public or private etc ?
The AMIs will get stored in s3 which you won’t be able to access. However, under ec2 –> AMIs you can check the registered private AMIs. By default when you create an AMI, it will be in private. To make an AMI public, you need to explicitly change it to public in the AMI settings.
Where are the default values of {{.Vars}} , {{.Path}} taken by the file, if it is not declared in the packer template?
Can you please give some insights on the variables used in execute_command
"provisioners": [ { "type": "shell", "execute_command": "{{.Vars}} sudo -E -S bash '{{.Path}}'", "scripts": ["scripts/bootstrap.sh"] } ]You could try using
{{user `variable`}}in execute_command and pass variable in a variable file or directly in command line.very nice
I believe this please needs to be reformatted
2>&1
as
2>&1
How To Run Ansible Playbook as Root User With Packer?
Hello there!
Could you please help me with variables declaration?
You say:
“`
First, you need to declare the variable name in the variable block as shown below.
“app_name”: “{{app_name_cmd_var}}”
“`
But packer validator throws the following error due to this:
“`
Error initializing core: error interpolating default value for ‘app_name’: template: root:1: function “app_name_cmd_var” not defined
“`
i got it to work! Pretty much need to make the userdata.txt to .sh and in the json file; add this:
“user_data_file”:”./userdata.sh”,
under builder
glad it worked!!
Wow, that was a fast reply! Thanks! I saw that in the documentation but there wasn’t an example for me to follow. However, I did create a txt file called userdata.txt with my instructions inside, but packer did not like the format. Here is the error output:
[email protected]:/packer$ packer build -var-file=/packer/userdata2.txt no_creds.json
invalid value “/packer/userdata2.txt” for flag -var-file: Error reading variables in ‘/packer/userdata2.txt’: invalid character ‘#’ looking for beginning of value
Here is my userdata:
#!/bin/bash
#
touch /var/lock/subsys/local
/etc/init.d/syslog stop
update_root() {
if [ ! -d /root/.ssh ];then
mkdir /root/.ssh
chmod 700 /root/.ssh
curl http://169.254.169.254/latest/meta-data/public-keys/0/openssh-key >> /root/.ssh/authorized_keys
chmod 400 /root/.ssh/authorized_keys
else
curl http://169.254.169.254/latest/meta-data/public-keys/0/openssh-key >> /root/.ssh/authorized_keys
chmod 400 /root/.ssh/authorized_keys
fi
}
update_ssh(){
sed -i “s/PermitRootLogin.*/PermitRootLogin yes/” /etc/ssh/sshd_config
sed -i ‘/^Allow/ s/$/ root/’ /etc/ssh/sshd_config
sed -i “s/StrictModes.*/StrictModes no/” /etc/ssh/sshd_config
sed -i ‘s/GSSAPIAuthentication.*/GSSAPIAuthentication no/’ /etc/ssh/sshd_config
/sbin/service sshd restart
}
update_dns(){
cat > /etc/resolv.conf << EOL
search ec2.internal
options timeout:2 attempts:5
nameserver 172.10.0.2
EOL
}
update_services(){
/sbin/chkconfig –del vmware-tools
}
update_nss(){
/bin/cp /etc/nsswitch.conf.noldap /etc/nsswitch.conf
}
update_root
update_nss
update_dns
update_services
update_ssh
Thanks for the write-up! I was wondering how do you setup userdata for AWS in packer to run during bootup?
Hi Wai,
You can pass userdata as a string or as a file. Please refer the official documentation from here https://www.packer.io/docs/builders/amazon-ebs.html#user_data