As your organization grows, you may adopt a hybrid or multi-cloud strategy to enable innovation, increase resiliency, decrease costs, or integrate different systems. Packer is a cloud-agnostic tool that lets you build identical artifacts for multiple platforms from a single template file. By tracking your build metadata through HCP Packer, you can query it for future downstream Packer builds or reference artifacts in your Terraform configuration.
In this tutorial, you will build and deploy an artifact containing HashiCups, a fictional coffee-shop application, in AWS and Azure. To do so, you will use Packer to build and store the artifacts in AWS and Azure, push the build metadata to HCP Packer, and use Terraform to deploy the artifacts to their respective cloud providers. In the process, you will learn how to use Packer and HCP Packer to standardize artifacts across multi-cloud and hybrid environments.
This tutorial assumes that you are familiar with the workflows for Packer, HCP Packer, and either Terraform Community Edition or HCP Terraform. If you are new to Packer, complete the Packer Get Started tutorials first. If you are new to HCP Packer, complete the Get Started with HCP Packer tutorials.
HCP Terraform is a platform that you can use to manage and execute your Terraform projects. It includes features like remote state and execution, structured plan output, workspace resource summaries, and more. The workflow for HCP Terraform is the same as Terraform Community Edition.
Select the Terraform Community Edition tab if you would rather complete this tutorial using Terraform Community Edition.
Now, install Packer 1.10.1+ locally.
You will also need an HCP account with an HCP Packer Registry.
Next, create a new HCP service principal and set the following environment variables locally.
Environment Variable DescriptionHCP_CLIENT_ID
The client ID generated by HCP when you created the HCP Service Principal HCP_CLIENT_SECRET
The client secret generated by HCP when you created the HCP Service Principal HCP_PROJECT_ID
Find this in the URL of the HCP Overview page, https://portal.cloud.hashicorp.com/orgs/xxxx/projects/PROJECT_ID
You will also need an AWS account with credentials set as local environment variables.
Environment Variable DescriptionAWS_ACCESS_KEY_ID
The access key ID from your AWS key pair AWS_SECRET_ACCESS_KEY
The secret access key from your AWS key pair
If you do not have one already, create an Azure account.
In your Azure account, create an Azure Active Directory Service Principal scoped to your Subscription, with the Contributor role, and an application secret. Be sure to copy the application secret value generated by Azure. Then, set the following environment variables.
Environment Variable DescriptionARM_CLIENT_ID
The Application (client) ID from your Azure Service Principal ARM_CLIENT_SECRET
The value generated by Azure when you created an application secret for your Azure Service Principal ARM_SUBSCRIPTION_ID
Your Azure subscription id ARM_TENANT_ID
The Directory (tenant) ID from your Azure Service Principal
Next, create an Azure Resource Group in the US West 3 region and set the following environment variable.
Environment Variable DescriptionTF_VAR_azure_resource_group
The name of the Azure Resource Group you created. Packer will store artifacts here, and Terraform will create HashiCups infrastructure here.
Note
Some of the infrastructure in this tutorial may not qualify for the AWS or Azure free tiers. Destroy the infrastructure at the end of the guide to avoid unnecessary charges. We are not responsible for any charges that you incur.
In your terminal, clone the example repository.
$ git clone https://github.com/hashicorp-education/learn-packer-multicloud
Navigate to the cloned repository.
$ cd learn-packer-multicloud
The packer
directory contains files Packer uses to build artifacts.
In your editor, open variables.pkr.hcl
. Packer uses the environment variables you set earlier for the first four variables.
packer/variables.pkr.hcl
variable "arm_client_id" {
type = string
default = env("ARM_CLIENT_ID")
}
variable "arm_client_secret" {
type = string
default = env("ARM_CLIENT_SECRET")
}
variable "arm_subscription_id" {
type = string
default = env("ARM_SUBSCRIPTION_ID")
}
variable "azure_resource_group" {
type = string
default = env("TF_VAR_azure_resource_group")
}
variable "azure_region" {
type = string
default = "westus3"
}
variable "aws_region" {
type = string
default = "us-west-1"
}
Now, open packer/build.pkr.hcl
.
The azure-arm.ubuntu-lts
source block uses the client_id
, client_secret
, and subscription_id
parameters to authenticate to Azure. Packer retrieves an Ubuntu 22.04 image to use as the base image and stores built images in the resource group specified by the managed_image_resource_group_name
parameter.
packer/build.pkr.hcl
source "azure-arm" "ubuntu-lts" {
client_id = var.arm_client_id
client_secret = var.arm_client_secret
subscription_id = var.arm_subscription_id
os_type = "Linux"
image_offer = "0001-com-ubuntu-server-jammy"
image_publisher = "Canonical"
image_sku = "22_04-lts"
managed_image_resource_group_name = var.azure_resource_group
## ...
}
The amazon-ebs.ubuntu-lts
source block retrieves an Ubuntu 22.04 AMIs to use as a base image, from the region specified in the region
variable. The Amazon plugin for Packer uses the AWS credential environment variables you set earlier to authenticate to AWS.
packer/build.pkr.hcl
source "amazon-ebs" "ubuntu-lts" {
source_ami_filter {
filters = {
virtualization-type = "hvm"
name = "ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"
root-device-type = "ebs"
}
owners = ["099720109477"]
most_recent = true
}
region = var.aws_region
## ...
}
The build
block references the artifact sources defined in the source blocks. Packer standardizes your artifacts across clouds by following the same instructions to build both artifacts.
packer/build.pkr.hcl
build {
source "source.amazon-ebs.ubuntu-lts" {
name = "hashicups"
}
source "source.azure-arm.ubuntu-lts" {
name = "hashicups"
location = var.azure_region
managed_image_name = "hashicups_${local.date}"
}
# systemd unit for HashiCups service
provisioner "file" {
source = "hashicups.service"
destination = "/tmp/hashicups.service"
}
# Set up HashiCups
provisioner "shell" {
scripts = [
"setup-deps-hashicups.sh"
]
}
## ...
}
First, Packer creates a virtual machine from each source image in both cloud providers. Then, it copies the HashiCups systemd unit file to each machine and runs the setup-deps-hashicups.sh
script to install and configure HashiCups. When the script finishes, Packer asks each cloud provider to create a new image from each virtual machine.
Finally, Packer sends artifact metadata from the newly built images to the specified HCP Packer registry bucket so you can reference the artifacts.
packer/build.pkr.hcl
## ...
# HCP Packer settings
hcp_packer_registry {
bucket_name = "learn-packer-multicloud-hashicups"
description = <<EOT
This is an image for HashiCups.
EOT
bucket_labels = {
"hashicorp-learn" = "learn-packer-multicloud-hashicups",
}
}
}
Change into the packer
directory.
Initialize the Packer template to install the required AWS and Azure plugins.
$ packer init .
Installed plugin github.com/hashicorp/azure v1.0.6 in "…"
Installed plugin github.com/hashicorp/amazon v1.0.9 in "…"
Packer installs the plugins specified in the required plugins
block in the build.pkr.hcl
template. Packer plugins are standalone applications that you can use to perform tasks during builds. They extend Packer's capabilities, similarly to Terraform providers.
Now, build the HashiCups artifacts in both AWS and Azure.
Note
It may take up to 15 minutes for Packer to build the artifacts.
$ packer build .
amazon-ebs.ubuntu-hirsute: output will be in this color.
azure-arm.hashicups: output will be in this color.
==> azure-arm.hashicups: Publishing build details for azure-arm.hashicups to the HCP Packer registry
==> amazon-ebs.ubuntu-hirsute: Publishing build details for amazon-ebs.ubuntu-hirsute to the HCP Packer registry
## ...
Build 'amazon-ebs.ubuntu-hirsute' finished after 4 minutes 58 seconds.
## ...
Build 'azure-arm.hashicups' finished after 5 minutes 32 seconds.
==> Wait completed after 5 minutes 32 seconds
==> Builds finished. The artifacts of successful builds are:
--> azure-arm.hashicups: Azure.ResourceManagement.VMImage:
OSType: Linux
ManagedImageResourceGroupName: packer-rg
ManagedImageName: hashicups_0419
ManagedImageId: /subscriptions/1d1a90a0-b25a-4336-9303-8316efd81952/resourceGroups/packer-rg/providers/Microsoft.Compute/images/hashicups_0419
ManagedImageLocation: westus3
--> azure-arm.hashicups: Published metadata to HCP Packer registry packer/learn-packer-multicloud-hashicups/versions/01HMRZH96S3X418EW8F5FNN9RF
--> amazon-ebs.hashicups: AMIs were created:
us-west-1: ami-0a7afa1b40592d366
--> amazon-ebs.hashicups: Published metadata to HCP Packer registry packer/learn-packer-multicloud-hashicups/versions/01HMRZH96S3X418EW8F5FNN9RF
Packer builds artifacts in parallel in each cloud provider, reducing the total build time.
Continue on to the next section while the build completes to learn how to deploy the artifacts to multiple clouds using Terraform. To skip the deployment step, proceed to Clean up your infrastructure.
The terraform
directory contains the Terraform configuration to deploy the HashiCups machine images to Azure and AWS.
Open terraform/variables.tf
. This file contains variables used by the rest of the configuration. The aws_region
and azure_region
variables control which artifact metadata Terraform should request from HCP Packer for, and the regions where Terraform will deploy HashiCups images and infrastructure.
variable "aws_region" {
description = "The AWS region Terraform should deploy your instance to"
default = "us-west-1"
}
variable "azure_region" {
description = "The Azure region Terraform should deploy your instance to"
default = "westus3"
}
variable "cidr_vpc" {
description = "CIDR block for the VPC"
default = "10.1.0.0/16"
}
variable "cidr_subnet" {
description = "CIDR block for the subnet"
default = "10.1.0.0/24"
}
variable "environment_tag" {
description = "Environment tag"
default = "Learn"
}
variable "hcp_bucket_hashicups" {
description = "HCP Packer bucket name for hashicups image"
default = "learn-packer-multicloud-hashicups"
}
variable "hcp_channel" {
description = "HCP Packer channel name"
default = "production"
}
variable "azure_resource_group" {
description = "Azure Resource Group name where Terraform will create infrastructure"
}
Warning
Ensure that the values assigned to aws_region
and azure_region
match the values for the corresponding Packer variables. If you changed the value of the Packer variables during build, change the Terraform variable values, too.
Now, open terraform/hcp.tf
. This configuration retrieves artifact information from HCP Packer using data sources.
The hcp_packer_version.hashicups
data source retrieves artifact version information from the production
channel of the learn-packer-multicloud-hashicups
bucket. These values are the defaults for the configuration's input variables.
terraform/hcp.tf
data "hcp_packer_version" "hashicups" {
bucket_name = var.hcp_bucket_hashicups
channel_name = var.hcp_channel
}
The hcp_packer_artifact
data sources use the version ID from the hcp_packer_version
data source to retrieve an artifact ID for each cloud provider. Notice the differences in the platform
and region
attributes between the two data sources.
terraform/hcp.tf
data "hcp_packer_artifact" "aws_hashicups" {
bucket_name = data.hcp_packer_version.hashicups.bucket_name
version_fingerprint = data.hcp_packer_version.hashicups.fingerprint
platform = "aws"
region = var.aws_region
}
data "hcp_packer_artifact" "azure_hashicups" {
bucket_name = data.hcp_packer_version.hashicups.bucket_name
version_fingerprint = data.hcp_packer_version.hashicups.fingerprint
platform = "azure"
region = var.azure_region
}
Open terraform/aws.tf
. This configuration defines a VPC and network resources, an AWS EC2 instance running HashiCups, and a security group with public access on port 80. Notice that the HashiCups instance references the hcp_packer_artifact.aws_hashicups
data source.
terraform/aws.tf
resource "aws_instance" "hashicups" {
ami = data.hcp_packer_artifact.aws_hashicups.external_identifier
instance_type = "t2.micro"
subnet_id = aws_subnet.subnet_public.id
vpc_security_group_ids = [aws_security_group.hashicups.id]
associate_public_ip_address = true
tags = {
Name = "HashiCups"
Name = "Learn-Packer-MultiCloud"
}
}
Open terraform/azure.tf
. This configuration defines a VPC and network resources, an Azure virtual machine running HashiCups, and a security group with public access on port 80. The virtual machine references the hcp_packer_artifact.azure_hashicups
data source.
Warning
This configuration hardcodes admin credentials for the Azure virtual machine for demo purposes. Do not hardcode credentials in production.
terraform/azure.tf
resource "azurerm_linux_virtual_machine" "hashicups" {
name = "${var.prefix}-vm"
source_image_id = data.hcp_packer_artifact.azure_hashicups.external_identifier
resource_group_name = data.azurerm_resource_group.main.name
location = data.azurerm_resource_group.main.location
size = "Standard_B1s"
admin_username = "ubuntu"
admin_password = "adminPass1!"
disable_password_authentication = false
network_interface_ids = [azurerm_network_interface.main.id]
os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}
tags = {
name = "hashicups"
learn = "learn-packer-multicloud"
}
}
Change to the terraform
directory.
Set the TF_CLOUD_ORGANIZATION
environment variable to your HCP Terraform organization name. This will configure your HCP Terraform integration.
$ export TF_CLOUD_ORGANIZATION=
Initialize your configuration. Terraform will automatically create the learn-packer-multicloud
workspace in your HCP Terraform organization.
$ terraform init
Initializing HCP Terraform...
Initializing provider plugins...
- Reusing previous version of hashicorp/aws from the dependency lock file
- Reusing previous version of hashicorp/azurerm from the dependency lock file
- Reusing previous version of hashicorp/hcp from the dependency lock file
- Installing hashicorp/aws v4.30.0...
- Installed hashicorp/aws v4.30.0 (signed by HashiCorp)
- Installing hashicorp/azurerm v3.22.0...
- Installed hashicorp/azurerm v3.22.0 (signed by HashiCorp)
- Installing hashicorp/hcp v0.44.0...
- Installed hashicorp/hcp v0.44.0 (signed by HashiCorp)
HCP Terraform has been successfully initialized!
You may now begin working with HCP Terraform. Try running "terraform plan" to
see any changes that are required for your infrastructure.
If you ever set or change modules or Terraform Settings, run "terraform init"
again to reinitialize your working directory.
In HCP Terraform, navigate to the learn-packer-multicloud
workspace.
Set the following workspace-specific variables. Set the correct type and be sure to mark the secrets as sensitive.
Variable name Description TypeARM_CLIENT_ID
The Application (client) ID from your Azure Service Principal Environment variable ARM_CLIENT_SECRET
The value generated by Azure when you created an application secret for your Azure Service Principal Environment variable ARM_SUBSCRIPTION_ID
Your Azure subscription ID Environment variable ARM_TENANT_ID
The Directory (tenant) ID from your Azure Service Principal Environment variable AWS_ACCESS_KEY_ID
The access key ID from your AWS key pair Environment variable AWS_SECRET_ACCESS_KEY
The secret access key from your AWS key pair Environment variable HCP_CLIENT_ID
The client ID generated by HCP when you created the HCP Service Principal Environment variable HCP_CLIENT_SECRET
The client secret generated by HCP when you created the HCP Service Principal Environment variable azure_resource_group
The name of the Azure Resource Group you created. Packer will store images here, and Terraform will create HashiCups infrastructure here. Terraform variable
In your editor, open terraform/terraform.tf
. This file configures the Terraform backend, the minimum provider versions, and the minimum Terraform version.
Comment out the cloud
block that configures the HCP Terraform integration.
terraform/terraform.tf
terraform {
/*
cloud {
workspaces {
name = "learn-packer-multicloud"
}
}
*/
## ...
}
Now, initialize your Terraform configuration.
$ terraform init
Initializing the backend...
##...
Terraform has been successfully initialized!
You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.
If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.
Wait for Packer to finish building your artifacts, then continue with the tutorial.
When Packer finishes building your artifacts, navigate to your learn-packer-multicloud-hashicups
bucket in the HCP Packer dashboard.
Click on Versions, then select the first version, labeled v1
. Notice that this version has two builds — one for Azure, the other for AWS.
The respective AWS and Azure data sources in your Terraform configuration reference each of these artifacts.
HCP Packer channels allow you to reference a specific build version in Packer or Terraform.
In the HCP console, click on Channels, then click on New Channel. Create a new channel named production
and set it to the v1
version of your learn-packer-multicloud-hashicups
bucket.
Terraform will query the production
channel to retrieve the Azure and AWS image IDs and deploy the appropriate artifacts.
In your terminal, apply your configuration to deploy HashiCups artifacts in both Azure and AWS. Respond yes
to the prompt to confirm the operation.
$ terraform apply
Running apply in HCP Terraform. Output will stream here. Pressing Ctrl-C
will cancel the remote apply if it's still pending. If the apply started it
will stop streaming the logs, but will not stop the apply running remotely.
Preparing the remote apply...
To view this run in a browser, visit:
https://app.terraform.io/app/hashicorp-learn/learn-packer-multicloud/runs/run-000
Waiting for the plan to start...
Terraform v1.1.6
on linux_amd64
Initializing plugins and modules...
## ...
Plan: 15 to add, 0 to change, 0 to destroy.
Changes to Outputs:
+ aws_public_ip = (known after apply)
+ azure_public_ip = (known after apply)
Do you want to perform these actions in workspace "learn-packer-multicloud"?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
## ...
Apply complete! Resources: 15 added, 0 changed, 0 destroyed.
Outputs:
aws_public_ip = "54.193.153.230"
azure_public_ip = "104.42.116.112"
Visit the addresses from the aws_public_ip
and azure_public_ip
outputs on port 80
in your browser to view the HashiCups application.
Tip
It may take several minutes for the setup script to complete on each instance. If you cannot view the HashiCups dashboard or receive a error response, please wait a few minutes before trying again.
You successfully built and deployed identical artifacts across multiple clouds with Packer and Terraform.
Before moving on, destroy the infrastructure you created in this tutorial.
In the terraform
directory, destroy the infrastructure for the HashiCups application. Respond yes
to the prompt to confirm the operation.
$ terraform destroy
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
- destroy
Terraform will perform the following actions:
##...
Plan: 0 to add, 0 to change, 15 to destroy.
##...
Do you really want to destroy all resources?
Terraform will destroy all your managed infrastructure, as shown above.
There is no undo. Only 'yes' will be accepted to confirm.
Enter a value: yes
##...
Destroy complete! Resources: 15 destroyed.
Delete Azure resource group and artifacts
Your Azure account still has machines images in the resource group you created for this tutorial.
In the Azure portal, visit Resource groups. Then, select the name of the resource group you created for this tutorial.
If you want to keep the Resource Group, select all images in the resource list whose name begins with hashicups_
. Then select Delete to delete them.
If you no longer need the resource group, select Delete resource group and follow the on-screen instructions to delete it. Azure will delete all resources contained in the resource group, including images, before deleting the group itself.
Delete AWS AMIsYour AWS account still has AMIs and their respective snapshots, which you may be charged for depending on your other usage.
Note
Remember to delete the AMIs and snapshots in the region where Packer created them. If you didn't update the aws_region
variable in the terraform.tfvars
file, they will be in the us-west-1
region.
In your us-west-1
AWS account, deregister the AMI by selecting it, clicking on the Actions button, then the Deregister AMI option, and finally confirm by clicking the Deregister AMI button in the confirmation dialog.
Delete the snapshots by selecting the snapshots, clicking on the Actions button, then the Delete snapshot option, and finally confirm by clicking the Delete button in the confirmation dialog.
Clean up HCP Terraform resourcesIf you used the HCP Terraform workflow, navigate to your learn-packer-multicloud
workspace in HCP Terraform and delete the workspace.
In this tutorial, you built artifacts from the same Packer template in AWS and Azure, pushed the metadata to HCP Packer, and deployed virtual machines using the built artifacts. In the process, you learned how you can use Packer and HCP Packer to standardize your artifacts as you adopt a multi-cloud strategy.
For more information on topics covered in this tutorial, check out the following resources.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4