Deploy a Packer Image with Terraform
Traducciones al EspañolEstamos traduciendo nuestros guías y tutoriales al Español. Es posible que usted esté viendo una traducción generada automáticamente. Estamos trabajando con traductores profesionales para verificar las traducciones de nuestro sitio web. Este proyecto es un trabajo en curso.
Both the Packer and Terraform tools by HashiCorp stand out for remarkable infrastructure-automating. Despite some overlap, the tools have distinct and complimentary features. This makes them an effective pair, with Packer used to create images that Terraform then deploys as a complete infrastructure.
Learn more about Packer in our Using the Linode Packer Builder to Create Custom Images guide. Discover how you can leverage Terraform in our Beginner’s Guide to Terraform.
In this tutorial, find out how to use Packer and Terraform together to deploy Linode instances. The tutorial uses the Linode Terraform provider to deploy several instances based on a Linode image built with Packer.
Before You Begin
If you have not already done so, create a Linode account and Compute Instance. See our Getting Started with Linode and Creating a Compute Instance guides.
Follow our Setting Up and Securing a Compute Instance guide to update your system. You may also wish to set the timezone, configure your hostname, create a limited user account, and harden SSH access.
NoteThis guide is written for a non-root user. Commands that require elevated privileges are prefixed withsudo
. If you’re not familiar with thesudo
command, see the Users and Groups guide.
How to Install the Prerequisites
To get started, install both Packer and Terraform on the same system. Below you can find links to installation guides for the two tools, as well as steps covering most Linux operating systems.
Installing Packer
Packer’s installation process varies substantially depending on your operating system. Refer to the official installation guide for instructions if your system is not covered here.
sudo apt-get update && sudo apt-get install -y gnupg software-properties-common
curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -\
sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"
sudo apt-get update && sudo apt-get install packer
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
sudo yum -y install packer
sudo dnf install -y dnf-plugins-core
sudo dnf config-manager --add-repo https://rpm.releases.hashicorp.com/fedora/hashicorp.repo
sudo dnf -y install packer
Afterward, verify your installation and display the installed version with the following command:
packer --version
1.8.4
Installing Terraform
Terraform’s installation process also varies depending on your operating system. Refer to HashiCorp’s official documentation on installing the Terraform CLI for systems that are not covered here. You can also refer to the section on installing Terraform in our guide Use Terraform to Provision Linode Environments.
sudo apt-get update && sudo apt-get install -y gnupg software-properties-common
wget -O- https://apt.releases.hashicorp.com/gpg | gpg --dearmor | sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
sudo yum -y install terraform
sudo dnf install -y dnf-plugins-core
sudo dnf config-manager --add-repo https://rpm.releases.hashicorp.com/fedora/hashicorp.repo
sudo dnf -y install terraform
Afterward, verify your installation with:
terraform -version
Terraform v1.3.3
on linux_amd64
How to Build a Packer Image
Packer automates the creation of machine images. These images are helpful when looking to streamline your process for provisioning infrastructure. Such images give you a consistent basis for deploying instances.
Moreover, images are much more efficient. Rather than executing a series of installations and commands with each provisioned instance, the provisioning tool can deploy ready-made images.
The examples in this tutorial uses a Linode image built with Packer. Linode has a builder available for Packer, which lets you put together images specifically for a Linode instance.
To do so, follow along with our guide on Using the Linode Packer Builder to Create Custom Images. By the end, you should have a Packer-built image on your Linode account.
The remaining steps in this tutorial should work no matter what kind of image you built following the guide linked above. However, the Packer image used in the examples to follow has the label packer-linode-image-1
, runs on an Ubuntu 20.04 base, and has NGINX installed.
How to Configure Terraform
Terraform focuses on automating the provisioning process, allowing you to deploy your infrastructure entirely from code.
To learn more about deploying Linode instances with Terraform, see our tutorial on how to Use Terraform to Provision Linode Environments.
This tutorial covers a similar series of steps, but specifically demonstrates how you can work with custom Linode images.
Before moving ahead, create a directory for your Terraform scripts, and change that to your working directory. This tutorial uses the linode-terraform
directory in the current user’s home directory:
mkdir ~/linode-terraform
cd ~/linode-terraform
The rest of the tutorial assumes you are working out of this directory.
Setting Up the Linode Provider
Terraform’s providers act as abstractions of APIs, giving Terraform an interface for working with various resources on host platforms.
Linode has its own Terraform provider, which you can learn more about from its Terraform provider registry page.
To use the provider, you just need a couple of short blocks in a Terraform script.
Create a new Terraform file named packer-linode.tf
, which acts as the base for this tutorial’s Terraform project:
nano packer-linode.tf
Give it the contents shown here:
- File: packer-linode.tf
1 2 3 4 5 6 7 8 9 10 11
terraform { required_providers { linode = { source = "linode/linode" version = "1.29.3" } } } provider "linode" { token = var.token }
The terraform
block starts the project by indicating its required providers (e.g. Linode). The provider
block then starts the Linode provider. The token
argument allows the provider to authenticate its connection to the Linode API.
When done, press Ctrl + X to exit nano, Y to save, and Enter to confirm.
Assigning Terraform Variables
Above, you can see that the token
value for the Linode provider uses the var.token
variable. Although not required, variables make Terraform scripts much more adaptable and manageable.
This tutorial handles variables using two files.
First, create a
variables.tf
file:nano variables.tf
Now fill it with the contents shown below. This file defines all the variables for the Terraform project. Some of these variables have default values, which Terraform automatically uses if not otherwise assigned. Other variables need to be assigned, which you can see in the next file.
- File: variables.tf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
variable "token" { description = "The Linode API Personal Access Token." } variable "password" { description = "The root password for the Linode instances." } variable "ssh_key" { description = "The location of an SSH key file for use on the Linode instances." default = "~/.ssh/id_rsa.pub" } variable "node_count" { description = "The number of instances to create." default = 1 } variable "region" { description = "The name of the region in which to deploy instances." default = "us-east" } variable "image_id" { description = "The ID for the Linode image to be used in provisioning the instances" default = "linode/ubuntu20.04" }
When done, press Ctrl + X to exit nano, Y to save, and Enter to confirm.
Now create a
terraform.tfvars
file:nano terraform.tfvars
This file, with the
.tfvars
ending, is a place for assigning variable values. Give the file the contents below, replacing the values in arrow brackets (<...>
) with your actual values:- File: terraform.tfvars
1 2 3 4
token = "<LinodeApiToken>" password = "<RootPassword>" node_count = 2 image_id = "private/<LinodeImageId>"
The
<LinodeApiToken>
needs to be an API token associated with your Linode account. You can follow our Get an API Access Token guide to generate a personal access token. Be sure to give the token “Read/Write” permissions.Above, you can see a value of
private/<LinodeImageId>
for theimage_id
. This value should match the image ID for the Linode image you created with Packer. All custom Linode images are prefaced withprivate/
and conclude with the image’s ID. In these examples,private/17691867
is assumed to be the ID for the Linode image built with Packer.There are two main ways to get your image ID:
The Linode image ID appears at the end of the output when you use Packer to create the image. For instance, in the guide on creating a Linode image with Packer linked above, you can find the output:
==> Builds finished. The artifacts of successful builds are: --> linode.example-linode-image: Linode image: packer-linode-image-1 (private/17691867)
The Linode API has an endpoint for listing available images. The list includes your custom images if you call it with your API token.
You can use a cURL command to list all images available to you, public and private. Replace
$LINODE_API_TOKEN
with your Linode API token:curl -H "Authorization: Bearer $LINODE_API_TOKEN" \https://api.linode.com/v4/images
The output can be overwhelming in the command line, so you may want to use another tool to prettify the JSON response. This has been done with the result shown here:
{ "pages": 1, "data": [{ "id": "private/17691867", "label": "packer-linode-image-1", "description": "Example Packer Linode Image", // [...]
When done, press + X to exit nano, Y to save, and Enter to confirm.
Defining the Linode Resource
The next step for the Terraform script is to define the actual resource to be provisioned. In this case, the script needs to provision Linode instances, which can be done using the linode_instance
resource.
Open the packer-linode.tf
file created earlier and add the details shown here to the end:
- File: packer-linode.tf
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
resource "linode_instance" "packer_linode_instance" { count = var.node_count image = var.image_id label = "packer-image-linode-${count.index + 1}" group = "packer-image-instances" region = var.region type = "g6-standard-1" authorized_keys = [ chomp(file(var.ssh_key)) ] root_pass = var.password connection { type = "ssh" user = "root" password = var.password host = self.ip_address } provisioner "remote-exec" { inline = [ # Update the system. "apt-get update -qq", # Disable password authentication; users can only connect with an SSH key. "sed -i '/PasswordAuthentication/d' /etc/ssh/sshd_config", "echo \"PasswordAuthentication no\" >> /etc/ssh/sshd_config", # Check to make sure NGINX is running. "systemctl status nginx --no-pager" ] } }
And with that, the Terraform project is ready to provision two Linode instances based on your Packer-built image. Most of the configuration details for the resource
block are managed by variables. So you shouldn’t need to fiddle with much of the resource
block to adjustment things like the number of instances to provision.
The remote-exec
provisioner, and specifically the inline
list within it, is where much of the customization comes in. This block defines shell commands to be executed on the newly provisioned instance. The commands here are relatively simple, but this provisioner can give you fine-grained control of operations on the instance.
How to Provision a Packer Image with Terraform
From here, a handful of Terraform commands are all you need to provision and manage Linode instances from the Packer-built image.
First, Terraform needs to run some initialization around the script. This installs any prerequisites, specifically the linode
provider in this example, and sets up Terraform’s lock file.
terraform init
Running Terraform’s plan
command is also good practice. Here, Terraform checks your script for immediate errors and provides an outline of the projected resources to deploy. You can think of it as a light dry run.
terraform plan
Review the plan, and when ready, provision your instances with the apply
command. This may take several minutes to process, depending on your systems and the number of instances being deployed.
terraform apply
linode_instance.packer_linode_instance[0] (remote-exec): Connected!
linode_instance.packer_linode_instance[0] (remote-exec): ● nginx.service - A high performance web server and a reverse proxy server
linode_instance.packer_linode_instance[0] (remote-exec): Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
linode_instance.packer_linode_instance[0] (remote-exec): Active: active (running) since Thu 2022-10-27 15:56:42 UTC; 9s ago
[...]
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
In the future, whenever you want to remove the instances created with Terraform, you can use the destroy
command from within your Terraform script directory.
terraform destroy
As with the apply
command, you get a preview of the instances and are asked to confirm before the instances are destroyed.
Conclusion
This tutorial outlined how to use Terraform to deploy Linode instances built with a Packer image. This arrangement provides an efficient setup for provisioning and managing Linode instances. Terraform streamlines the process of provisioning infrastructure, and it is made even more efficient using pre-built images from Packer.
The example covered in this tutorial is fairly simple. But the setup can be readily adapted and expanded on to deploy more robust and complex infrastructures.
More Information
You may wish to consult the following resources for additional information on this topic. While these are provided in the hope that they will be useful, please note that we cannot vouch for the accuracy or timeliness of externally hosted materials.
This page was originally published on