Skip to main content

Hashicorp Terraform - Docker Provider 2023

Shen Zhen, China

Installation Linux

wget https://releases.hashicorp.com/terraform/1.6.2/terraform_1.6.2_linux_amd64.zip
wget https://releases.hashicorp.com/terraform/1.6.2/terraform_1.6.2_SHA256SUMS
sha256sum terraform_1.6.2_linux_amd64.zip
sha256sum -c terraform_1.6.2_SHA256SUMS

> terraform_1.6.2_linux_amd64.zip: OK
unzip terraform_1.6.2_linux_amd64.zip
rm terraform_1.6.2_linux_amd64.zip

sudo mv terraform /usr/bin/terraform
terraform -v

Terraform v1.6.2
on linux_amd64

Add auto-completion to bash or zsh:

terraform -install-autocomplete

Get Started - Docker

Build, change, and destroy Docker infrastructure using Terraform. Step-by-step, command-line tutorials will walk you through the Terraform basics for the first time.

Hello World

Create a file main.tf inside a sub-dir (all job files need to be located in their own directory):

terraform {
  required_providers {
    docker = {
      source  = "kreuzwerker/docker"
      version = "~> 3.0.2"
    }
  }
}

provider "docker" {}

resource "docker_image" "nginx" {
  name         = "nginx:latest"
  keep_locally = false
}

resource "docker_container" "nginx" {
  image = docker_image.nginx.image_id
  name  = "tutorial"
  ports {
    internal = 80
    external = 8000
  }
}
terraform fmt
> main.tf

terraform validate
> Success! The configuration is valid.

Deployment

In the terminal, initialize the project, which downloads a plugin that allows Terraform to interact with Docker:

terraform init

> Terraform has been successfully initialized!

Provision the NGINX server container with apply. When Terraform asks you to confirm, type yes and press ENTER:

terraform apply

Inspect the current state using:

terraform show
terraform state list

Run docker ps to view the NGINX container running in Docker via Terraform:

docker ps

CONTAINER ID   IMAGE          STATUS         PORTS                  NAMES
1a62207b5026   bc649bab30d1   Up 3 minutes   0.0.0.0:8000->80/tcp   tutorial
curl localhost:8000

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...

Update Infrastructure

Change the docker_container.nginx resource under the provider block in main.tf by replacing the ports.external value of 8000 with 8080:

resource "docker_container" "nginx" {
  image = docker_image.nginx.image_id
  name  = "tutorial"
  hostname = "learn-terraform-docker"
  ports {
    internal = 80
    external = 8080
  }
}

After changing the configuration, run terraform apply again to see how Terraform will apply this change to the existing resources:

terraform apply
docker ps

CONTAINER ID   IMAGE          STATUS         PORTS                   NAMES
1a62207b5026   bc649bab30d1   Up 3 minutes   0.0.0.0:8080-->80/tcp   tutorial

Destroy Infrastructure

To stop the container and destroy the resources created in this tutorial, run terraform destroy. When Terraform asks you to confirm, type yes and press ENTER:

terraform destroy

You have now provisioned and destroyed an NGINX webserver with Terraform.

Variables

Variables File

Create a new file called variables.tf with a block defining a new container_name variable:

variable "container_name" {
  description = "Value of the name for the Docker container"
  type        = string
  default     = "ExampleNginxContainer"
}

In main.tf, update the docker_container resource block to use the new variable. The container_name variable block will default to its default value (ExampleNginxContainer) unless you declare a different value:

resource "docker_container" "nginx" {
  image = docker_image.nginx.image_id
  name  = var.container_name
  ports {
    internal = 80
    external = 8080
  }
}

Apply the configuration. Respond to the confirmation prompt with a yes:

terraform apply
docker ps

CONTAINER ID   IMAGE          STATUS         PORTS                   NAMES
f1188cbea686   bc649bab30d1   Up 39 seconds  0.0.0.0:8080-->80/tcp   ExampleNginxContainer

Override Default Variables

Now apply the configuration again, this time overriding the default container name by passing in a variable using the -var flag. Terraform will update the container's name attribute with the new name. Respond to the confirmation prompt with yes:

terraform apply -var 'container_name=nginx-ingress'
docker ps

CONTAINER ID   IMAGE          STATUS        PORTS                   NAMES
79eb40cfe38c   bc649bab30d1   Up 4 seconds  0.0.0.0:8080-->80/tcp   nginx-ingress

Variables by String Input

Remove the default value to force a user input when the job is started:

variable "container_name" {
  description = "Value of the name for the Docker container"
  type        = string
}
terraform apply

var.container_name
  Value of the name for the Docker container

  Enter a value:

.tfvsrs File

Instead of specifying the variables inside the variables file we can create a terraform.tfvars file with all the information. We still need to instantiate the variable inside variables.tf:

variable "container_name" {
  description = "Value of the name for the Docker container"
  type        = string
}

But all the user editable information will be inside the terraform.tfvars file:

container_name = "nginx-ingress"

Environment Variables

To avoid leaking - e.g. credentials - to your source management system you can use environment variables instead of adding those values to your tf code. In the following example I edited the hostname and external port that Docker should use for my container:

resource "docker_container" "nginx" {
  image    = docker_image.nginx.image_id
  name     = var.container_name
  hostname = var.HOSTNAME
  ports {
    internal = 80
    external = var.EXT_PORT
  }
}

These variables need to be declared in the variables.tf file:

variable "HOSTNAME" {
  description = "Name of the Docker host"
  type        = string
}

variable "EXT_PORT" {
  description = "External port forwarded to the ingress container"
  type        = string
}

Now export the values you want to set for those variables from your terminal:

export TF_VAR_HOSTNAME=docker_hostname
export TF_VAR_EXT_PORT=7777

Apply the changes and verify that the container is now using the new external port:

terraform apply
docker ps

CONTAINER ID   IMAGE          STATUS        PORTS                   NAMES
5cd4ef649771   bc649bab30d1   Up 5 seconds  0.0.0.0:7777-->80/tcp   nginx-ingress

Variables Precedence

  1. -var and -var-file option for terraform apply
  2. *.auto.tfvars | *.auto.tfvars.json
  3. terraform.tfvars.json
  4. terraform.tfvars
  5. Environment variables

Query Data with Outputs

Create a file called outputs.tf and add the configuration below to define outputs for your container's ID and the image ID:

output "container_id" {
  description = "ID of the Docker container"
  value       = docker_container.nginx.id
}

output "image_id" {
  description = "ID of the Docker image"
  value       = docker_image.nginx.id
}

You must apply this configuration before you can use these output values. Apply your configuration now. Respond to the confirmation prompt with yes:

terraform apply

Apply complete! Resources: 1 added, 0 changed, 1 destroyed.

Outputs:

container_id = "c437b5c58531a9b3c4e2eb8486d54960093faff1e4996a6d7445b19742d32f38"
image_id = "sha256:bc649bab30d150c10a84031a7f54c99a8c31069c7bc324a7899d7125d59cc973nginx:latest"

Terraform prints output values to the screen when you apply your configuration. Query the outputs with the terraform output command.

container_id = "c437b5c58531a9b3c4e2eb8486d54960093faff1e4996a6d7445b19742d32f38"
image_id = "sha256:bc649bab30d150c10a84031a7f54c99a8c31069c7bc324a7899d7125d59cc973nginx:latest"

Terraform Modules

Shared Local

mkdir -p {modules/webserver,dev-config,prod-config}
touch {modules/webserver/main.tf,modules/webserver/variables.tf}

https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs https://adamtheautomator.com/terraform-docker/

modules/webserver/main.tf