What is HashiCorp Configuration Language?
Terraform uses a configuration language called HCL, which stands for HashiCorp Configuration Language.
HCL is incredibly easy to understand, even for those who might not have a background in programming or software development.
It's designed to be both human-friendly and machine-readable, so you'll have no trouble understanding the syntax and meaning of your code.
HCL makes it a breeze to define your infrastructure resources and their configurations, ensuring that your resources are deployed and managed exactly as you want them to be.
HCL follows a declarative approach. You just need to tell HCL what you need, and it will take care of the complex process of actually setting up those resources in a way that is reliable and easy to manage.
HCL can promote the reusability of the code through modules.
With modules, you can easily encapsulate and reuse configurations across different projects.
Syntax of HCL
HCL file consists of blocks and arguments.
<block> <parameter> {
key1 = value1
key2 = value2
}
The block contains information about the infrastructure platform.
Let's take the example which we did in the previous blog,
$ mkdir terraform-local
$ cd terraform-local
Now the terraform file local.tf
looks like this
resource "local_file" "devops" {
filename = "/home/ubuntu/terraform-course/terraform-local/devops_automated.txt
content = "This is the day-1 of terraweek challenge in which we will learn basics of it."
}
Let's break down each and every step of it.
resource
: This represents the block name.This keyword indicates that we are defining a resource block in Terraform.
Resources represent infrastructure components that Terraform manages.
local_file
: local = provider & file = resource.This represents the resource type. In this case, it represents a local file that will be created by Terraform.
devops
: This is the name of the resource instance.It serves as a unique identifier for this specific resource block.
Within the resource block, there are two arguments specified:
filename
: This is the argument that defines the path and name of the file to be created.In this example, the file will be created at the specified path:
/home/ubuntu/terraform-course/terraform-local/devops_automated.txt
.content
: This argument specifies the content that should be written into the file. The provided string will be written as the content of the file.
The resource block defines the desired state of the local file resource. When Terraform is executed, it will ensure that the file specified in the filename
argument exists with the specified content.
It's worth noting that this is a simplified example and that Terraform supports many other resource types and configuration options.
The provided example demonstrates the basic structure of a resource block in Terraform.
Variables
So, variables are like placeholders in your Terraform code that allow us to set values that can be used throughout our configuration.
We can use variables to set things like instance types or subnet IDs or to define other dynamic configuration settings.
For example, let's say we're creating an AWS instance in our config.
We might use variables to specify the region we want to use, the instance type, and the subnet ID.
That way, we can reuse those values in other parts of our configuration too.
variable "instance_type" { description = "Type of EC2 instance" type = string default = "t2.micro" }
In this example, we define a variable named "instance_type" of type "string" with a default value of "t2.micro".
Datatypes
Data types are just different kinds of data that we can work with in Terraform. There are things like strings, numbers, and lists that we can use to define values for our resources.
Here's an example of how we can define a variable with a data type in HCL in Terraform:
variable "instance_count" {
type = number
default = 2
}
In this example, instance_count
is a variable of type number
, and it has a default value of 2
.
We can use this variable throughout our Terraform configuration to create two instances of a resource, such as EC2 instances in AWS.
Expressions
Expressions allow us to perform operations on our variables and data types. We can use expressions to fetch values from AWS resources, perform mathematical or logical operations, or construct strings and lists.
Here's an example: let's say we're using a Terraform template file to create a script that we'll use to set up an S3 bucket object.
We might define variables for the name of our bucket and the environment we're creating it.
Then, we can use an expression to construct the bucket name with those variables.
Plus, we can use that same expression to reference our bucket in other resources we create, so we don't have to copy-paste the same bucket name all over the place.
Terraform Configurations using HCL syntax
Let's practice writing some Terraform configurations using HCL syntax.
Continuing from the previous blog, take the same repository or a new one, your choice and let's create a file called main.tf
resource "local_file" "passwords" {
filename = "/root/pass.txt"
content = "abcdef"
}
In the above configuration, we have made a file of resource type local which stores our password.
Don't take this example seriously to store your password, I am doing it just for the sake of explanation:)
So, in the content section, we have given a random text, which we are defining here as our password.
Now do all the jazz!
$ terraform init
$ terraform validate
$ terraform plan
$ terraform apply
Now notice one thing that, while we're performing terraform plan
and apply
cmd, our content is visible to us and every detail is printed on the screen.
And imagine, if we have saved our password as content and another developer is performing these steps, then it's a risk of doing it.
So there is another resource type called local_sensitive_file
This will hide the content while printing on the screen. To edit the main.tf
file do changes in it.
resource "local_sensitive_file" "passwords" {
filename = "/root/pass.txt"
content = "abcdef"
}
Now to apply these changes, we have to run terraform init
cmd again to initialize the changes. And then terraform plan
and terraform apply
cmd.
$ terraform init
$ terraform plan
You can see in the planning stage, it will first destroy the local_file
file because it is not in the configuration now.
And then, it will go into the creation of a new local_sensitive_file
.
$ terraform apply
Now by running terraform apply
cmd, it is doing all the changes that we have given to do by destroying the previous resource type
.
And creating the new one of local_sensitive_file
Now if you again take a look closely, you can see how the content is hidden and showing "sensitive value" as a display message.
Now you can check the changes made that we wanted by running ls
cmd,
We made it with another terraform configuration using HCL syntax!
Using variables.tf
file
Now we will do the same thing but this time by creating a separate file for variables called variables.tf
$ vim variables.tf
variable "file_content" {
description = "Content of the file"
type = string
default = "abcdef"
}
Let's break down each component:
variable
: This keyword is used to declare a variable in Terraform. It indicates that we are defining a new variable.file_content
: This is the name of the variable. It is enclosed in double quotes to represent it as a string value.description
: This attribute is used to provide a description or explanation of the variable. It is a human-readable string that helps convey the purpose or usage of the variable.type
: This attribute specifies the data type of the variable. In this case, the type is set to "string
", indicating that the variable will hold a string value.default
: This attribute sets a default value for the variable. If no value is explicitly provided when running Terraform, the default value will be used. In this example, the default value is set to "abcdef
".
This is the whole process that we did above, it's just this is what we separated from the file from the content
section.
Now we will change the main.tf
file
# Add the required_providers block
terraform {
required_providers {
local = {
source = "hashicorp/local"
version = "2.1.0"
}
}
}
# Use the local_file resource
resource "local_file" "passwords" {
filename = "/root/pass.txt"
content = var.file_content
}
We are using the required_providers
block and then creating a local_file
resource in Terraform. Let's go through each step:
terraform
block: This block is used to configure Terraform settings. In this case, it includes therequired_providers
block.required_providers
block: This block specifies the providers required for the Terraform configuration. In the example, the required provider islocal
. It specifies the source as "hashicorp/local
" and the requiredversion as "2.1.0"
.resource
block: This block is used to define a resource that Terraform manages. In this example, it creates alocal_file
resource named "passwords".filename
attribute: This attribute specifies the path and filename for thelocal
sensitive file to be created. In this case, it is set to"/root/pass.txt"
.content
attribute: This attribute sets the content of thelocal
sensitive file. It references thevar.file_content
variable, which means the value of thefile_content
variable will be used as the content of the file.
By using the required_providers
block, we ensure that the necessary provider is available, and then we create a local_file
resource that represents a file with the specified filename and content.
This is how we can perform the same by using variables.tf
and required_providers
Terraform with Docker
As we above used and saw the case of required_providers
block, we will give docker
as a provider here.
$ mkdir terraform-docker
$ cd terraform-docker
$ vim main.tf
terraform {
required_providers {
docker = {
source = "kreuzwerker/docker"
version = "2.21.0"
}
}
}
provider "docker" {}
resource "docker_image" "nginx" {
name = "nginx:latest"
keep_locally = false
}
resource "docker_container" "nginx" {
image = docker_image.nginx.latest
name = "nginx-tf"
ports {
internal = 80
external = 80
}
}
Let's go through each step and understand what is happening:
terraform
block: This block specifies the required provider for the configuration. In this case, it declares that the Docker provider is needed. Therequired_providers
block specifies thesource
andversion
of the Docker provider.provider "docker"
block: This block declares the Docker provider and configures it to be used in the configuration.resource "docker_image" "nginx"
block: This block defines a Docker image resource named "nginx". It specifies the name of the image as "nginx:latest
" and sets thekeep_locally
attribute to false, indicating that the image should not be kept locally after use.resource "docker_container" "nginx"
block: This block defines a Docker container resource named "nginx". It uses the Docker image resource defined earlier (docker_image.nginx.latest
) as the image for the container. It sets the name of the container as "nginx-tf
".ports
block: This block is nested inside thedocker_container
resource block and specifies the port mappings for the container. It maps the internalport 80
to theexternal port 80
, allowing traffic to access the Nginx web server running inside the container.
These configuration blocks work together to define the infrastructure resources (Docker image and container) that Terraform will manage. When you run terraform apply
, Terraform will create the specified Docker resources based on this configuration.
Now run terraform init
cmd to initialize the change we want.
$ terraform init
Now if you try to run terraform plan
cmd, it will give you an error because we didn't install docker in the system.
So before doing terraform plan
, run
$ sudo apt-get install docker.io
Now run, terraform plan
cmd. Again you will see an error that says "permission denied while connecting to the Docker daemon".
So, to remove this error give the required permissions to the user
sudo chown $USER /var/run/docker.sock
Now run terraform plan
cmd and it will success message finally.
Now run,
$ terraform apply
And you will see "Apply complete!" message.
Now take the correct public IP address of your instance from the instance id dashboard and open a new browser and you can see the nginx is running.
This is a part of the 7-day TerraWeek Challenge initiated by Shubham Londhe sir.
Thank you!🖤