Imagine if you could write code that would set up your infrastructure on any cloud system and would not have change if you switch providers. This is the concept of infrastructure as code.
Terraform is one of the most popular tools to do this and will be the subject of this post. Let’s get started by installing Terraform and create a ngnix server.
Install terraform on the mac
Install and update it using the following commands.
brew tap hashicorp/tap brew install hashicorp/tap/terraform brew upgrade hashicorp/tap/terraform
Verify the installation
Verify that the installation worked by opening a new terminal session and listing Terraform’s available subcommands using terraform -help
.
terraform -help Usage: terraform [global options] <subcommand> [args] The available commands for execution are listed below. The primary workflow commands are given first, followed by less common or more advanced commands. Main commands: init Prepare your working directory for other commands validate Check whether the configuration is valid plan Show changes required by the current configuration apply Create or update infrastructure destroy Destroy previously-created infrastructure All other commands: console Try Terraform expressions at an interactive command prompt fmt Reformat your configuration in the standard style force-unlock Release a stuck lock on the current workspace get Install or upgrade remote Terraform modules graph Generate a Graphviz graph of the steps in an operation import Associate existing infrastructure with a Terraform resource login Obtain and save credentials for a remote host logout Remove locally-stored credentials for a remote host output Show output values from your root module providers Show the providers required for this configuration refresh Update the state to match remote systems show Show the current state or a saved plan state Advanced state management taint Mark a resource instance as not fully functional untaint Remove the 'tainted' state from a resource instance version Show the current Terraform version workspace Workspace management Global options (use these before the subcommand, if any): -chdir=DIR Switch to a different working directory before executing the given subcommand. -help Show this help output, or the help for a specified subcommand. -version An alias for the "version" subcommand.
Add any subcommand to terraform <subcommand> -help
to learn more about what it does and available options.
terraform init -help Usage: terraform init [options] [DIR] Initialize a new or existing Terraform working directory by creating initial files, loading any remote state, downloading modules, etc. This is the first command that should be run for any new or existing Terraform configuration per machine. This sets up all the local data necessary to run Terraform that is typically not committed to version control. This command is always safe to run multiple times. Though subsequent runs may give errors, this command will never delete your configuration or state. Even so, if you have important information, please back it up prior to running this command, just in case. If no arguments are given, the configuration in this working directory is initialized. Options: -backend=true Configure the backend for this configuration. -backend-config=path This can be either a path to an HCL file with key/value assignments (same format as terraform.tfvars) or a 'key=value' format. This is merged with what is in the configuration file. This can be specified multiple times. The backend type must be in the configuration itself. -force-copy Suppress prompts about copying state data. This is equivalent to providing a "yes" to all confirmation prompts. -from-module=SOURCE Copy the contents of the given module into the target directory before initialization. -get=true Download any modules for this configuration. -get-plugins=true Download any missing plugins for this configuration. -input=true Ask for input if necessary. If false, will error if input was required. -lock=true Lock the state file when locking is supported. -lock-timeout=0s Duration to retry a state lock. -no-color If specified, output won't contain any color. -plugin-dir Directory containing plugin binaries. This overrides all default search paths for plugins, and prevents the automatic installation of plugins. This flag can be used multiple times. -reconfigure Reconfigure the backend, ignoring any saved configuration. -upgrade=false If installing modules (-get) or plugins (-get-plugins), ignore previously-downloaded objects and install the latest version allowed within configured constraints. -verify-plugins=true Verify the authenticity and integrity of automatically downloaded plugins.
Enable tab completion
To enable autocomplete, run the following command and then restart your shell.
terraform -install-autocomplete
Lets have some fun with Terraform!
The next steps require Docker. If you are unfamiliar with Docker read our previous blog post don’t dock docker, and come back here once you installed it and played with it a little.
After you install Terraform and Docker on your local machine, start Docker Desktop.
open -a Docker
Create a ngnix server
Create a directory named terraform-docker-demo
.
mkdir terraform-docker-demo cd terraform-docker-demo
Create a new file using vim main.tf
then press i
and paste the following Terraform configuration. Then save the file by entering esc
then :wq
.
terraform { required_providers { docker = { source = "terraform-providers/docker" } } } provider "docker" {} resource "docker_image" "nginx" { name = "nginx:latest" keep_locally = false } resource "docker_container" "nginx" { image = docker_image.nginx.latest name = "tutorial" ports { internal = 80 external = 8080 } }
Enter cat main.tf
to make sure the file has been updated.
Initialize the project, which downloads a plugin that allows Terraform to interact with Docker.
terraform init Initializing the backend... Initializing provider plugins... - Finding latest version of terraform-providers/docker... - Installing terraform-providers/docker v2.7.2... - Installed terraform-providers/docker v2.7.2 (signed by HashiCorp) Terraform has created a lock file .terraform.lock.hcl to record the provider selections it made above. Include this file in your version control repository so that Terraform can guarantee to make the same selections by default when you run "terraform init" in the future. Warning: Additional provider information from registry The remote registry returned warnings for registry.terraform.io/terraform-providers/docker: - For users on Terraform 0.13 or greater, this provider has moved to kreuzwerker/docker. Please update your source in required_providers. 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.
Provision the NGINX server container with apply
. When Terraform asks you to confirm type yes
and press ENTER
. If you unable to pull an image from nginx:latest
make sure you are logged into Docker Hub.
terraform apply 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: # docker_container.nginx will be created + resource "docker_container" "nginx" { + attach = false + bridge = (known after apply) + command = (known after apply) + container_logs = (known after apply) + dns = (known after apply) + dns_opts = (known after apply) + entrypoint = (known after apply) + exit_code = (known after apply) + gateway = (known after apply) + hostname = (known after apply) + id = (known after apply) + image = (known after apply) + ip_address = (known after apply) + ip_prefix_length = (known after apply) + ipc_mode = (known after apply) + log_driver = (known after apply) + log_opts = (known after apply) + logs = false + must_run = true + name = "tutorial" + network_data = (known after apply) + read_only = false + restart = "no" + rm = false + shm_size = (known after apply) + start = true + user = (known after apply) + working_dir = (known after apply) + ports { + external = 8000 + internal = 80 + ip = "0.0.0.0" + protocol = "tcp" } } # docker_image.nginx will be created + resource "docker_image" "nginx" { + id = (known after apply) + keep_locally = false + latest = (known after apply) + name = "nginx:latest" } Plan: 2 to add, 0 to change, 0 to destroy. Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value: yes
Verify the existence of the NGINX container by visiting http://localhost:8080. Wow that was easy!
To stop the container, run terraform destroy
.
$ terraform destroy
What is really cool about using terraform destroy
is that all the docker containers, images, and networks are automatically deleted. This saves a lot of time.
In our next post, we will create real infrastructure in AWS.