Hashicorp Terraform - Docker Provider 2023
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
-var
and-var-file
option forterraform apply
*.auto.tfvars
|*.auto.tfvars.json
terraform.tfvars.json
terraform.tfvars
- 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