Terraform is a convergence-based "infrastructure as code" (IaC) tool that describes the intended infrastructure state using a declarative programming language (HCL).
Terraform maintains systems that are generally changeable, which means you manage their configuration during their entire lifecycle rather than recreating them from scratch on every configuration change, as you would with Docker containers, for example.
HCL (HashiCorp Configuration Language):
🌀HCL (HashiCorp Configuration Language) is a configuration language designed to be both human and machine-readable for use in infrastructure automation. It is a domain-specific language (DSL) created by HashiCorp, the company behind popular infrastructure automation tools like Terraform.
🌀 HCL is primarily used for defining and configuring infrastructure and services in a declarative manner. It has a specific syntax for creating and configuring resources, variables ,blocks ,attributes and values with Terraform configurations.
Here's an example of HCL syntax:
resource "aws_instance" "example" {
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t2.micro"
subnet_id = "subnet-12345678"
tags = {
Name = "example-instance"
Environment = "production"
}
}
In this example, we define a resource block using HCL. Let's break down the components:
resource
is a keyword in HCL indicating that we're defining a resource block."aws_instance"
is the resource type. It specifies that we're creating an AWS EC2 instance."example"
is the name of the resource block. It's a user-defined name and serves as a reference to this resource elsewhere in the configuration.The block is enclosed in curly braces
{}
and contains the attributes and their values that configure the EC2 instance.ami
,instance_type
,subnet_id
are attributes of the resource block, each assigned a value using the=
sign.tags
is an attribute that defines a complex type, a map or dictionary, using curly braces{}
. It contains key-value pairs, where the keys are strings and the values can be any valid expression.🌀HCL supports various data types such as strings, numbers, Booleans, lists, and maps. It also allows for interpolation, which is a way to reference and combine values from different sources within the configuration.
🌀Overall, HCL provides a organized and concise syntax for defining infrastructure configurations in Terraform, making it easier to communicate the desired state of your infrastructure resources.
Blocks and Resources in Terraform
💡Terraform employs a configuration language to define the desired state of your infrastructure, which is made up of numerous block kinds. These block types are critical to Terraform's infrastructure definition and configuration.
💡In Terraform, a block is a core unit that defines and configures several components of your infrastructure. Blocks are written in HashiCorp Configuration Language (HCL) and enable you to declare resources, providers, variables, outputs, and other configuration items in your Terraform code.
💡A block is a container for other content. Blocks have a type
that can have zero or more required labels, followed by{ }
brackets that contain the block's body. Blocks can be nested inside each other. This is a general representation of a block:
type "label_1" "label_2" {
argument_1 = value_1
argument_2 = value_2
}
Each block serves a specific purpose and has its own syntax and set of properties. Here are some common block types used in Terraform:
Terraform Block
Provider Block
Data Block
Resource Block
Module Block
Variable Block
Output Block
Locals Block
✔Terraform Block:
Terraform blocks are used to specify the version of the terraform that we desire. It may also include a required_providers block, which defines the versions of the providers we require as well as where Terraform should obtain these providers from. Terraform blocks are frequently placed in a separate file named terraform.tf to segregate settings into their own files.
terraform { required_providers { aws = { source = "hashicorp/aws" version = "~> 2.0" } } required_version = ">= 1.0.1" }
✔Provider Block :
Provider blocks define a particular sort of module that allows Terraform to communicate with multiple cloud-hosting platforms or data centers. Before we can use providers, we must first configure them with the required credentials. Provider versions and download locations are commonly supplied within the terraform block, but they can also be specified within this block.
provider "aws" { version = "~> 3.0" region = "us-east-1" }
✔Data Block :
data block is used to retrieve information from external sources or existing resources. We can use a data block to obtain information about existing resources, such as a list of available AWS AMIs or the current state of a Kubernetes cluster.
Using data blocks, you may incorporate external data into your infrastructure design and make intelligent decisions based on it.
One way we would use data block in future articles is to query AWS API to get a list of active Availability Zones to deploy resources in. Data is then accessed using dot notation using
var
identifier.Retrieving information about existing infrastructure to be used in the Terraform configuration.
Querying and filtering data for use in resource configuration.
data "data_type" "data_name" { variable_1 = expression }
✔Resource Block:
Resource blocks are used to regulate resources including compute instances, virtual networks, databases, buckets, and DNS servers. This block type is the foundation of any terraform design because it represents actual resources, while the bulk of other block types serve as supporting components.
Each resource block specifies the resource type, name, and configuration parameters specific to that resource. By using resource blocks effectively, we can create and manage the desired infrastructure resources in a consistent and repeatable manner.
resource "aws_instance" "example_resource" { ami = "ami-005e54dee72cc1d00" # us-west-2 instance_type = "t2.micro" credit_specification { cpu_credits = "unlimited" } }
✔Module Block:
Module block are defined and configured as reusable modules to encapsulate and manage infrastructure components. It allows us to organize and modularize your infrastructure code, promoting reusability, maintainability, and scalability. A module consists of
.tf
and/or.tf.json
files stored in a directory. It is the primary way to package and reuse resources in Terraform.Modules are a great way to compartmentalize reusable collections of resources in multiple configurations.
Promoting modularity, reusability, and maintainability of infrastructure code.
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "2.0.0"
name = "example-vpc"
cidr = "10.0.0.0/16"
azs = ["us-west-2a", "us-west-2b", "us-west-2c"]
public_subnets = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
private_subnets = ["10.0.11.0/24", "10.0.12.0/24", "10.0.13.0/24"]
}
✔Variable Block :
Declaring input variables that can be leveraged during Terraform execution to provide flexible settings. Variables allow you to provide arguments for your infrastructure and make it more flexible.
You can establish variables for values that differ between environments or deployments, such as the number of instances or the size of a storage volume. Using variable blocks allows you to easily adapt and reuse your settings for numerous scenarios without changing the underlying code.
Variables are often in their own file, called variables.tf
. To use a variable, it needs to be declared as a block. One block for each variable.
variable "instance_count" { description =
"Number of instances to create" type
= number default = 1 }
✔Output Block:
This is a block which is almost always present in all configurations, along with main.tf
and variables.tf
block. It allows Terraform to output structured data about your configuration. Outputs can include information like IP addresses, URLs, or resource identifiers that are useful for interacting with the given infrastructure. This output can be used by users to see data like IPs or resources names in one convenient place. Another use case involves using this data in other Terraform workspace or sharing data between modules.
Exposing calculated or derived values from the Terraform configuration.
Communicating important information to users after the Terraform run.
Sharing outputs with other configurations or scripts.
output "instance_ip" { value = aws_instance.example.
public_ip description = "Public IP of the created instance" }
✔Local Block :
Declaring local variables within the Terraform configuration for easier code readability and reusability. Local variables are temporary and can be defined within a block to compute and store intermediate values that are used within the same configuration file. These variables are not exposed to other configurations or modules and are purely for internal use within the same configuration.
They can also reference other values in the module to transform or combine them. These variables can be accessed using local.var_name
notation, note that it is called local.
when used to access values inside.
Defining local variables within a Terraform configuration.
Calculating derived values or performing transformations on other variables.
locals { instance_name = "my-instance" instance_tags = { Name = local.instance_name } }
✔Provisoner Block :
Provisioners enable us to specify activities to be taken on local or remote devices in order to prepare resources for service. Provisioners enable us to do things like install needed software, configure the resource, run scripts, and execute commands on it.
Provisioners can be specified within a resource block and run in a certain order, depending on the type of provisioner.
There are two types of Terraform provisioners:
local-exec
andremote-exec
.local-exec
invokes local executable after a resource is created. It runs the process on the machine running Terraform, meaning the machine where you runterraform apply
. This is most likely your own computer.remote-exec
invokes remote executable, something like an EC2 instance on AWS. This is an example of a provisioner for an EC2 instance and contains both 'local-exec' and aremote-exec
.resource "aws_instance" "web_server" { provisioner "local-exec" { command = "Get-Date > completed.txt" interpreter = ["PowerShell", "-Command"] } provisioner "remote-exec" { inline = [ "chmod +x /tmp/script.sh", "/tmp/script.sh args", ]}} allows
Terraform may be used to manage infrastructure effectively if these block types are properly implemented. Using the correct blocks in the right places enables you to specify the required infrastructure state, provision resources, retrieve data, and transmit critical information.
This modular and declarative approach automates infrastructure management, supports code reuse, encourages consistency, and improves collaboration among infrastructure teams.