Tutorial

How To Build a Hashicorp Vault Server Using Packer and Terraform on DigitalOcean [Quickstart]

Published on March 9, 2020
English
How To Build a Hashicorp Vault Server Using Packer and Terraform on DigitalOcean [Quickstart]

Introduction

Vault, by Hashicorp, is an open-source tool for securely storing secrets and sensitive data in dynamic cloud environments. Packer and Terraform, also developed by Hashicorp, can be used together to create and deploy images of Vault.

In this tutorial, you’ll use Packer to create an immutable snapshot of the system with Vault installed, and orchestrate its deployment using Terraform.

For a more detailed version of this tutorial, please refer to How To Build a Hashicorp Vault Server Using Packer and Terraform on DigitalOcean.

Prerequisites

  • Packer installed on your local machine. For instructions, visit the official documentation.
  • Terraform installed on your local machine. Visit the official documentation for a guide.
  • A personal access token (API key) with read and write permissions for your DigitalOcean account. Visit How to Create a Personal Access Token to create one.
  • An SSH key you’ll use to authenticate with the deployed Vault Droplets, available on your local machine and added to your DigitalOcean account. You’ll also need its fingerprint, which you can copy from the Security page of your account once you’ve added it. See the DigitalOcean documentation for detailed instructions or the How To Set Up SSH Keys tutorial.

Step 1 — Creating a Packer Template

Create and move into the ~/vault-orchestration directory to store your Vault files:

  1. mkdir ~/vault-orchestration
  2. cd ~/vault-orchestration

Create separate directories for Packer and Terraform configuration by running:

  1. mkdir packer terraform

Navigate to the Packer directory:

  1. cd packer

Using Template Variables

Create a variables.json in your packer subdirectory to store your private variable data:

  1. nano variables.json

Add the following lines:

~/vault-orchestration/packer/variables.json
{
  "do_token": "your_do_api_key",
  "base_system_image": "ubuntu-18-04-x64",
  "region": "nyc3",
  "size": "s-1vcpu-1gb"
}

You’ll use these variables in the template you are about to create. You can edit the base image, region, and Droplet size values according to the developer docs.

Replace your_do_api_key with your API key, then save and close the file.

Creating Builders and Provisioners

Create your Packer template for Vault in a file named template.json:

  1. nano template.json

Add the following lines:

~/vault-orchestration/packer/template.json
{
   "builders": [{
       "type": "digitalocean",
       "api_token": "{{user `do_token`}}",
       "image": "{{user `base_system_image`}}",
       "region": "{{user `region`}}",
       "size": "{{user `size`}}",
       "ssh_username": "root"
   }],
   "provisioners": [{
       "type": "shell",
       "inline": [
           "sleep 30",
           "sudo apt-get update",
           "sudo apt-get install unzip -y",
           "curl -L https://releases.hashicorp.com/vault/1.3.2/vault_1.3.2_linux_amd64.zip -o vault.zip",
           "unzip vault.zip",
           "sudo chown root:root vault",
           "mv vault /usr/local/bin/",
           "rm -f vault.zip"
       ]
}]
}

You define a single digitalocean builder. Packer will create a temporary Droplet of the defined size, image, and region using the provided API key.

The provisioner will connect to it using SSH with the specified username and will sequentially execute all defined provisioners before creating a DigitalOcean Snapshot from the Droplet and deleting it.

It’s of type shell, which will execute given commands on the target. The commands in the template will wait 30 seconds for the system to boot up, and will then download and unpack Vault 1.3.2. Check the official Vault download page for the most up-to-date version for Linux.

Save and close the file.

Verify the validity of your template:

  1. packer validate -var-file=variables.json template.json

You’ll see the following output:

Output
Template validated successfully.

Step 2 — Building the Snapshot

Build your snapshot with the Packer build command:

  1. packer build -var-file=variables.json template.json

You’ll see a lot of output, which will look like this:

Output
digitalocean: output will be in this color. ==> digitalocean: Creating temporary ssh key for droplet... ==> digitalocean: Creating droplet... ==> digitalocean: Waiting for droplet to become active... ==> digitalocean: Using ssh communicator to connect: ... ==> digitalocean: Waiting for SSH to become available... ==> digitalocean: Connected to SSH! ==> digitalocean: Provisioning with shell script: /tmp/packer-shell035430322 ... ==> digitalocean: % Total % Received % Xferd Average Speed Time Time Time Current ==> digitalocean: Dload Upload Total Spent Left Speed digitalocean: Archive: vault.zip ==> digitalocean: 100 45.5M 100 45.5M 0 0 154M 0 --:--:-- --:--:-- --:--:-- 153M digitalocean: inflating: vault ==> digitalocean: Gracefully shutting down droplet... ==> digitalocean: Creating snapshot: packer-1581537927 ==> digitalocean: Waiting for snapshot to complete... ==> digitalocean: Destroying droplet... ==> digitalocean: Deleting temporary ssh key... Build 'digitalocean' finished. ==> Builds finished. The artifacts of successful builds are: --> digitalocean: A snapshot was created: 'packer-1581537927' (ID: 58230938) in regions '...'

The last line contains the name of the snapshot (such as packer-1581537927) and its ID in parentheses, highlighted here. Note your ID of the snapshot, because you’ll need it in the next step.

If the build process fails due to API errors, wait a few minutes and then retry.

Step 3 — Writing Terraform Configuration

Navigate to the terraform subdirectory:

  1. cd ~/vault-orchestration/terraform

Create a file named do-provider.tf to store the provider:

  1. nano do-provider.tf

Add the following lines:

~/vault-orchestration/terraform/do-provider.tf
variable "do_token" {
}

variable "ssh_fingerprint" {
}

variable "instance_count" {
default = "1"
}

variable "do_snapshot_id" {
}

variable "do_name" {
default = "vault"
}

variable "do_region" {
}

variable "do_size" {
}

variable "do_private_networking" {
default = true
}

provider "digitalocean" {
token = var.do_token
}

This file provides the digitalocean provider with an API key. To specify the values of these variables you’ll create a variable definitions file similarly to Packer. The filename must end in either .tfvars or .tfvars.json.

Save and close the file.

Create a variable definitions file:

  1. nano definitions.tfvars

Add the following lines:

~/vault-orchestration/terraform/definitions.tf
do_token         = "your_do_api_key"
ssh_fingerprint  = "your_ssh_key_fingerprint"
do_snapshot_id   = your_do_snapshot_id
do_name          = "vault"
do_region        = "nyc3"
do_size          = "s-1vcpu-1gb"
instance_count   = 1

Replace your_do_api_key, your_ssh_key_fingerprint, and your_do_snapshot_id (the snapshot ID you noted from the previous step). The do_region and do_size parameters must have the same values as in the Packer variables file.

Save and close the file.

Create the following file to store the Vault snapshot deployment configuration:

  1. nano deployment.tf

Add the following lines:

~/vault-orchestration/terraform/deployment.tf
resource "digitalocean_droplet" "vault" {
count              = var.instance_count
image              = var.do_snapshot_id
name               = var.do_name
region             = var.do_region
size               = var.do_size
private_networking = var.do_private_networking
ssh_keys = [
  var.ssh_fingerprint
]
}

output "instance_ip_addr" {
value = {
  for instance in digitalocean_droplet.vault:
  instance.id => instance.ipv4_address
}
description = "The IP addresses of the deployed instances, paired with their IDs."
}

You define a single resource of the type digitalocean_droplet named vault. You set its parameters according to the variable values and add an SSH key (using its fingerprint) from your DigitalOcean account to the Droplet resource. You output the IP addresses of all newly deployed instances to the console.

Save and close the file.

Initialize the directory as a Terraform project:

  1. terraform init

You’ll see the following output:

Output
Initializing the backend... Initializing provider plugins... The following providers do not have any version constraints in configuration, so the latest version was installed. To prevent automatic upgrades to new major versions that may contain breaking changes, it is recommended to add version = "..." constraints to the corresponding provider blocks in configuration, with the constraint strings suggested below. * provider.digitalocean: version = "~> 1.14" 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.

Step 4 — Deploying Vault Using Terraform

Test the validity of your configuration:

  1. terraform validate

You’ll see the following output:

Output
Success! The configuration is valid.

Run the plan command to see what Terraform will attempt when it comes to provision the infrastructure:

  1. terraform plan -var-file="definitions.tfvars"

The output will look similar to:

Output
Refreshing Terraform state in-memory prior to plan... The refreshed state will be used to calculate this plan, but will not be persisted to local or remote state storage. ------------------------------------------------------------------------ An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # digitalocean_droplet.vault[0] will be created + resource "digitalocean_droplet" "vault" { ... } Plan: 1 to add, 0 to change, 0 to destroy. ------------------------------------------------------------------------ Note: You didn't specify an "-out" parameter to save this plan, so Terraform can't guarantee that exactly these actions will be performed if "terraform apply" is subsequently run.

Execute the plan:

  1. terraform apply -var-file="definitions.tfvars"

The Droplet will finish provisioning and you’ll see output similar to this:

Output
An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: + digitalocean_droplet.vault-droplet ... Plan: 1 to add, 0 to change, 0 to destroy. ... digitalocean_droplet.vault-droplet: Creating... ... Apply complete! Resources: 1 added, 0 changed, 0 destroyed. Outputs: instance_ip_addr = { "181254240" = "your_new_server_ip" }

Step 5 — Verifying Your Deployed Droplet

Run the following to connect to your new Droplet:

  1. ssh root@your_server_ip

Once you are logged in, run Vault with:

  1. vault

You’ll see its “help” output:

Output
Usage: vault <command> [args] Common commands: read Read data and retrieves secrets write Write data, configuration, and secrets delete Delete secrets and configuration list List data or secrets login Authenticate locally agent Start a Vault agent server Start a Vault server status Print seal and HA status unwrap Unwrap a wrapped secret Other commands: audit Interact with audit devices auth Interact with auth methods debug Runs the debug command kv Interact with Vault's Key-Value storage lease Interact with leases namespace Interact with namespaces operator Perform operator-specific tasks path-help Retrieve API help for paths plugin Interact with Vault plugins and catalog policy Interact with policies print Prints runtime configurations secrets Interact with secrets engines ssh Initiate an SSH session token Interact with tokens

Conclusion

You now have an automated system for deploying Hashicorp Vault on DigitalOcean Droplets using Terraform and Packer. To start using Vault, you’ll need to initialize it and further configure it. For instructions on how to do that, visit the official docs.

For more tutorials using Terraform, check out our Terraform content page.

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about us


About the authors
Default avatar
Savic

author



Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
3 Comments


This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

terraform { required_providers { digitalocean = { source = “digitalocean/digitalocean” } } }

Needs to be added to the do-provider.tf file or terraform init fails with:

Initializing the backend…

Initializing provider plugins…

  • Finding latest version of hashicorp/digitalocean…

Error: Failed to query available provider packages

Could not retrieve the list of available versions for provider hashicorp/digitalocean: provider registry registry.terraform.io does not have a provider named registry.terraform.io/hashicorp/digitalocean

If you have just upgraded directly from Terraform v0.12 to Terraform v0.14 then please upgrade to Terraform v0.13 first and follow the upgrade guide for that release, which might help you address this problem.

Did you intend to use digitalocean/digitalocean? If so, you must specify that source address in each module which requires that provider. To see which modules are currently depending on hashicorp/digitalocean, run the following command: terraform providers

The snapshot ID doesn’t seem to work. I created the snap with packer and confirmed that the droplet and the snap are in the same region, but when I apply the terraform state this error. I can switch the snapshot ID with a ‘Slug’ like Ubuntu and it deploys fine. It just doesn’t seem to work with Snapshots.

digitalocean_droplet.vault[0]: Creating…

Error: Error creating droplet: POST https://api.digitalocean.com/v2/droplets: 422 You specified an invalid image for Droplet creation.

on deployment.tf line 1, in resource “digitalocean_droplet” “vault”: 1: resource “digitalocean_droplet” “vault” {

Are there alternative strategies which make it easier to deploy new versions?

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel