Introduction To Terraform Basics
Overview of Terraform basics, including installation, configuration, providers, and resource management.

Hitesh Sahu
Terraform basics - 11%
- 3a Install and version Terraform
providers
- 3b Describe
plugin-based architecture
- 3c Write Terraform configuration using
multiple providers
- 3d Describe how Terraform finds and fetches providers
Initialize Terraform working directory
command | use |
---|---|
terraform init |
initialize directory, pull down providers |
terraform init -get-plugins=false |
initialize directory, do not download plugins |
terraform init -verify-plugins=false |
initialize directory, do not verify plugins for Hashicorp signature |
terraform version |
display Terraform binary version, also warns if version is old |
terraform get -update=true |
download and update modules in the "root" module. |
Misclaneous
command | use |
---|---|
terraform -install-autocomplete |
Setup tab auto-completion, requires logging back in |
terraform login |
obtain and save API token for Terraform cloud |
terraform logout |
Log out of Terraform Cloud, defaults to hostname app.terraform.io |
terraform graph | dot -Tpng > graph.png |
produce a PNG diagrams |
echo 'join(",",["foo","bar"])' | terraform console |
echo expression into terraform console |
Terrform help
-
ā
-h, --h
eg: terraform plan -h, terraform plan --h, -h plan, plan -h -
ā
-help , --help
eg: terraform plan -help, terraform plan --help, --help plan, -help plan
HashiCorp Terraform
infrastructure as code tool that lets you define both cloud and on-prem resources in human-readable configuration files that you can version, reuse, and share.
- Provides a consistent workflow to provision and manage all of infrastructure throughout its lifecycle.
- Can manage low-level components like compute, storage, & networking resources, as well as high-level components like DNS entries and SaaS features.
To deploy infrastructure with Terraform:
- Scope - Identify the infrastructure for your project.
- Author - Write the configuration for your infrastructure.
- Initialize - Install the plugins Terraform needs to manage the infrastructure.
- Plan - Preview the changes Terraform will make to match your configuration.
- Apply - Make the planned changes.
Terraform plugins
Terraform is comprised of Terraform Core and Terraform Plugins:
1. Terraform Core(core-plugins-api)
Terraform binary that communicates with plugins to manage infrastructure resources.
- Provides a common interface that allows to leverage many different cloud providers, databases, services olutions.
- Reads the configuration and builds the resource dependency graph.
2. Terraform Plugins (providers and provisioners)
Bridge Terraform Core and their respective target APIs.
- Implement resources via basic CRUD (create, read, update, and delete) APIs to communicate with third party services.
.terraform.lock.hcl
Terraform remembers the compatible version of dependencies such as providers and modules through dependency lock file.
- Terraform automatically creates or updates the dependency lock file each time you run the terraform init command.
Providers
Top level abstraction that define the available resources and data sources
- Plugins to interact with cloud providers, SaaS providers, & other APIs.
- installed from Terraform registry
Version Constraint Syntax
The following operators are valid:
-
=
(or no operator): Allows only one exact version number. Cannot be combined with other conditions. -
!=
: Excludes an exact version number. -
>, >=, <, <=
: Comparisons against a specified version, allowing versions for which the comparison is true. " Greater-than" requests newer versions, and "less-than" requests older versions. -
~>
: pessimistic constraint operator Allows only the rightmost version component to increment.-
~> 1.9.0 means >= 1.9.0 and < 1.10.0
# allows: 1.9.1, 1.9.9 # not allow: 1.20.1, 1.8.9 provider "google" { version = "~> 1.9.0" }
-
Provisioners
Use to model specific actions on the local machine or on a remote machine in order to prepare servers or other infrastructure objects for service.
Note: Provisioners should only be used as a last resort.
provisioner
block is always added inside the resource
block of compute instances
Provisioners that fail will also cause the Terraform apply itself to fail. The on_failure
setting can be used to:
continue and fail
- ā continue
- ā fail
local-exec provisioner
invokes a local executable after a resource is created. This invokes a process on the machine running Terraform, not on the resource.
resource "aws_instance" "web" {
provisioner "local-exec" {
command = "echo The server's IP address is ${self.private_ip}"
on_failure = continue # Failure behaviour
}}
Destroy provisioners
Destroy provisioners are run before the resource is destroyed. If they fail, Terraform will error and rerun the provisioners again on the next terraform apply.
# Destroy-Time Provisioners
resource "aws_instance" "web" {
provisioner "local-exec" {
when = destroy <--When
command = "echo 'Destroy-time provisioner'"
}
remote-exec Provisioner
invokes a script on a remote resource after it is created.
resource "aws_instance" "web" {
# ...
# Establishes connection to be used by all
# generic remote provisioners (i.e. file/remote-exec)
connection {
type = "ssh"
user = "root"
password = var.root_password
host = self.public_ip
}
provisioner "remote-exec" {
inline = [
"puppet apply",
"consul join ${aws_instance.web.private_ip}",
]
}
}
If resource is not available use terraform-data
resource "aws_instance" "cluster" {
count = 3
# ...
}
resource "terraform_data" "cluster" {
# Replacement of any instance of the cluster requires re-provisioning
triggers_replace = aws_instance.cluster.[*].id
# Bootstrap script can run on any instance of the cluster
# So we just choose the first in this case
connection {
host = aws_instance.cluster.[0].public_ip
}
provisioner "remote-exec" {
# Bootstrap script called with private_ip of each node in the cluster
inline = [
"bootstrap-cluster.sh ${join(" ", aws_instance.cluster.*.private_ip)}",
]
}
}
File Provisoner
copies files or directories from the machine running Terraform to the newly created resource.
# Copies the myapp.conf file to /etc/myapp.conf
provisioner "file" {
source = "conf/myapp.conf"
destination = "/etc/myapp.conf"
}
Connection Block
Access to the remote resource via SSH
or WinRM
and expect a nested connection block with details about how to
connect.
Mode SSH
# Copies the file as the root user using SSH
provisioner "file" {
source = "conf/myapp.conf"
destination = "/etc/myapp.conf"
connection {
type = "ssh"
user = "root"
password = "${var.root_password}"
host = "${var.host}"
}
}
Mode WinRM
# Copies the file as the Administrator user using WinRM
provisioner "file" {
source = "conf/myapp.conf"
destination = "C:/App/myapp.conf"
connection {
type = "winrm"
user = "Administrator"
password = "${var.admin_password}"
host = "${var.host}"
}
}
Resources
abstraction that allow Terraform to manage infrastructure objects, such as a compute instance, an access policy, or disk.
Providers act as a translation layer between Terraform and an API, offering one or more resources for practitioners to define in a configuration.
resource "aws_instance" "web" {
count = 4
}
Resource Addressing
is a string that identifies zero or more resource instances in your overall configuration.
Resource addressed as: <RESOURCE_TYPE>.<RESOURCE_NAME>[resource instance index]
aws_instance.web[3] # Third instance
aws_instance.web # Only the last instance
With for each:
resource "aws_instance" "demo" {
for_each = {
"terraform": "infrastructure",
"vault": "security",
"consul": "connectivity",
"nomad": "scheduler",
}}
# address by key
access aws_instance.demo["vault"]
Module addressed as: module.<MODULE_NAME>[module index
module.foo[0].module.bar["a"]
Data Sources
Allow Terraform to use information defined outside of Terraform, defined by another separate Terraform configuration, or modified by functions.
The data source + name together serve as an identifier for a given resource and so must be unique within a module.
A data block requests that Terraform read from a given data source ("aws_ami") and export the result under the given local name ("ubuntu").
data "aws_ami" "ubuntu" {
most_recent = true
owners = ["self"]
tags = {
Name = "app-server"
Tested = "true"
}
}
Data Source value is addressed as data.<DATA TYPE>.<NAME>.<ATTRIBUTE>
resource "aws_instance" "web" {
ami = data.aws_ami.ubuntu.id # Data Source
instance_type = "t3.micro"
}
Building Infrastructure
launch Docker using Teraform file: main.tf
terraform { <---- TERRAFORM BLOCK
required_providers {
docker = {
source = "kreuzwerker/docker"
version = "~> 3.0.1"
}
}
}
provider "docker" {} <---PROVIDER BLOCK
resource "docker_image" "nginx" { <-----RESOURCE BLOCK
name = "nginx"
keep_locally = false
}
resource "docker_container" "nginx" {
image = docker_image.nginx.image_id
name = "tutorial"
ports {
internal = 80
external = 8000
}
}
Initialize the project, which downloads a plugin called a provider
terraform init
Format & print out the names of the files it modified,
terraform fmt
Provision the infrastructure.
terraform apply