Terraform Tutorial for Beginners 2026 (Step-by-Step Guide + Real Project) is a practical Terraform guide on DevOpsLabX. In this tutorial, you will learn implementation steps, key commands, best practices, and production-ready patterns you can apply in real projects.
What is Terraform?
Terraform is HashiCorp's Infrastructure as Code (IaC) tool. It lets you define resources and infrastructure in human-readable, declerative configuration files, and manages your infrastructure's lifecycle.Using Terraform has several advantages over manually managing your infrastructure:
Terraform can manage infrastructure on multiple cloud platforms.
The human-readable configuration langauge helps you write infrastructure code quickly.
Terraform's state allows you to track resources changes throughout your deployments.
You can commit you configurations to version control to safely collaborate on infrastructure.
What is Infrastructure as Code (IaC)?
Infrastructure as a Code (IaC) is an approach to managing and provisioning computing infrastructure through machine-readable files, rather than physical hardware configuration or interactive configuration tools. It involves defining infrastructure elements like virtual machines, networks, storage, and more using code, typically in a declarative configuration language. This code can be version-controlled, tested, and automated, allowing for consistent, repeatable, and scalable infrastructure deployments across different environments, such as development, testing, and production. IaC tools like Terraform, Ansible, and AWS CloudFormation facilitate the implementation of this approach.
Terraform Installation on Linux (Ubuntu):
To install Terraform on Ubuntu, add the HashiCorp GPG key to your system:
curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add -Next, add the official HashiCorp Terraform Linux repository to
apt:sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main"Then update apt and install Terraform:
sudo apt-get update && sudo apt-get install terraformOnce installed, verify the installation:
terraform -v
Hashicorp Configuration Langauge (HCL):
HashiCorp Configuration Language (HCL) is a domain-specific language (DSL) developed by HashiCorp for defining configurations of HashiCorp tools. HCL is used to write configuration files for various HashiCorp products, such as Terraform, Consul, Vault, and others. It is designed to be easy to read and write, facilitating the creation of infrastructure as code.
Some key features of HCL include:
Simple Syntax: HCL is designed to have a simple and clean syntax, making it easy for both humans to read and write.
Configuration Blocks: HCL uses configuration blocks to define different parts of a configuration. Each block has a specific purpose and is used to configure a particular aspect of a HashiCorp tool.
Key-Value Pairs: Configuration settings are typically expressed as key-value pairs, making it intuitive for users to understand and define configurations.
Nested Structures: HCL supports nested structures, allowing for the organization of configuration settings in a hierarchical manner.
Comments: HCL allows the use of comments to provide additional information or explanations within the configuration files.
Here's a simple example of HCL syntax as used in a Terraform configuration file:
# Define provider and access credentials
provider "aws" {
region = "us-west-2"
access_key = "your-access-key"
secret_key = "your-secret-key"
}
# Define an AWS S3 bucket
resource "aws_s3_bucket" "example_bucket" {
bucket = "example-bucket-name"
acl = "private"
}In this example, you can see the use of provider and resource blocks, key-value pairs, and comments. This configuration is written in HCL and is used by Terraform to create an AWS S3 bucket.
Terraform Commands:
Sure, let's go through the Terraform commands:
terraform init:
Purpose: Initializes a new or existing Terraform working directory by downloading the necessary provider plugins.
Usage:
terraform initWhen to use: Run this command when setting up a new Terraform configuration or when adding or updating providers in an existing configuration.
terraform validate:
Purpose: Validates the syntax and configuration of Terraform files without executing them.
Usage:
terraform validateWhen to use: Run this command to check if your Terraform configuration files are syntactically correct and meet the expected format. It helps catch errors before applying changes.
terraform plan:
Purpose: Creates an execution plan for Terraform to preview the changes it will make to infrastructure.
Usage:
terraform planWhen to use: Run this command to see what changes Terraform will apply to your infrastructure based on the current configuration. It does not make any changes but provides an overview of the proposed modifications.
terraform apply:
Purpose: Applies the changes described in the Terraform execution plan.
Usage:
terraform applyWhen to use: Run this command to execute the changes proposed in the
terraform plan. It prompts for confirmation before making any modifications. This is where the actual infrastructure provisioning or modification takes place.
These commands are fundamental to working with Terraform. The typical workflow involves running terraform init to set up the environment, terraform validate to ensure correct syntax, terraform plan to preview changes, and finally terraform apply to apply those changes. The use of these commands helps ensure safe and predictable infrastructure management.
Create a local file using Terraform:
In Terraform, you can use the
local_fileresource to create a local file on the machine where Terraform is executed. Thelocal_fileresource is useful for generating configuration files, scripts, or any other file that needs to be present on the local machine. Here's a simple example:resource "local_file" "devops"{ filename="/home/ubuntu/Terraform/Terraform-local/devops-automated.txt" content= "I want to become a DevOps Engineer who knows Terraform" }In this example:
The
localprovider is declared, which is a built-in provider for interacting with the local machine.The
local_fileresource is defined with the name "example."The
contentattribute is used to specify the content of the file.The
filenameattribute is used to specify the path where the local file should be created.
To use this configuration, create a
.tffile (e.g.,main.tf) with the above content and run the following Terraform commands:terraform init terraform validate terraform plan terraform apply
Terraform will create the specified local file with the specified content. If the file already exists, Terraform will overwrite its content.
Note: The local provider and local_file resource are generally used for local tasks and testing. In a production environment, you may need to use remote state or other mechanisms for more robust and scalable solutions.
Result:

Generating the Random String using Terraform:
Certainly! To generate a random string using Terraform, you can use the random_string resource. Here's an example:
resource "random_string" "rand-str" { length=20 special=true override_special = "!#$%^&*()_-=+[]{}<>:?" } output "rand-str" { value = random_string.rand-str[*].result }
In this example:
The
random_stringresource is used to generate a random string.The
lengthattribute specifies the length of the password.specialattributes control whether to include special characters.override_specialallows you to provide a custom set of special characters.
The generated random string will be displayed as an output.
To use this configuration, create a
.tffile (e.g.,main.tf) with the above content and run the following Terraform commands:
terraform init
terraform validate
terraform plan
terraform applyAfter running the apply command, Terraform will generate a random password and display it as an output.
Remember that the random_string resource is suitable for generating random_string and is recommended for use when creating credentials or authentication tokens in your infrastructure.
Result:

Terraform with Docker:
Using Terraform with Docker involves creating and managing infrastructure as code within Docker containers. Docker can be used to encapsulate and distribute Terraform configurations, making it easy to work with Terraform in different environments without worrying about local dependencies. Below is a step-by-step guide on how to use Terraform within a Docker container:
Certainly! To use Terraform to pull the Nginx Docker image and deploy a container, you can follow these steps:
Step 1: Create a Terraform Configuration File (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
}
}In this Terraform configuration:
The Docker provider is used.
A variable is defined for the Nginx image name and tag.
The
docker_imagedata source pulls the Nginx image.The
docker_containerresource deploys an Nginx container, exposing port 80 internally and port 8080 externally.
Step 2: Initialize Terraform
Run the following command to initialize your Terraform configuration:
terraform initStep 3: Apply Terraform Configuration
Run the following command to apply the Terraform configuration and deploy the Nginx container:
terraform applyType "yes" when prompted.

Step 4: Access Nginx Container
After the deployment is successful, you can access the Nginx container by navigating to http://localhost:8080 in your web browser.

Terrafrom States:
In Terraform, the state is a crucial aspect of managing infrastructure. The Terraform state file (terraform.tfstate) keeps track of the resources that have been created and their current configuration. Here are key points about Terraform state:
State File:
Terraform maintains a state file that records the current state of the infrastructure.
The state file is crucial for Terraform to understand what resources are already deployed and how they are configured.
Remote State:
Terraform allows storing the state remotely for collaboration and consistency among team members.
Common backends for remote state include Amazon S3, Azure Storage, Google Cloud Storage, and HashiCorp Consul.
Locking:
To prevent conflicts in a multi-user environment, Terraform supports state file locking. It prevents multiple users from making changes simultaneously.
State Commands:
Common state-related commands include
terraform state list(lists resources in the state),terraform state show(shows details of a resource in the state), andterraform state rm(removes a resource from the state).
Importing Existing Infrastructure:
terraform importallows importing existing infrastructure into Terraform state. It is useful when transitioning from manually managed infrastructure to Terraform.
Sensitive Data:
Sensitive data, like passwords, is stored securely in the state file. However, be cautious and avoid exposing sensitive information in clear text within your Terraform configuration.
Recreating Resources:
Changes to Terraform configurations may require recreating resources. Understanding how Terraform manages state helps in predicting and handling such situations.
State Backends:
Terraform supports various state backends. When working in a team, it is common to use a remote backend for shared state.
Terraform Workspace:
Terraform workspaces allow managing multiple instances of the same infrastructure, each with its own state. This is useful for environment isolation (e.g., development, staging, production).
State Eviction and Cleanup:
Over time, state files may grow in size. It is essential to understand state cleanup strategies and potential state eviction policies.
Understanding and managing the Terraform state is critical for successful infrastructure management. Always ensure proper state handling practices, especially in team environments, for smooth collaboration and consistent deployments.
Terraform Variables:
In Terraform, variables allow you to parameterize your configurations, making them more flexible and reusable. Variables can be defined in a separate file (commonly named variables.tf) and then referenced throughout your Terraform code. Here's a brief overview:
Declare Variables:
variable "region" { description = "The AWS region where resources will be created." type = string default = "us-east-1" }Declare a variable named "region" with a description, type, and default value.
Reference Variables: Reference variables in your configuration using the
varkeyword:provider "aws" { region = var.region }Use the
var.regionsyntax to reference the value of the "region" variable.Input Variables: You can set variable values when running
terraform apply:terraform apply -var="region=us-west-2"This overrides the default value of the "region" variable.
Variable Files: Store variable values in a separate file (e.g.,
terraform.tfvars):region = "us-west-2"Terraform automatically loads variable values from files with names ending in
.auto.tfvars,terraform.tfvars, and.tfvars.Output Variables: Declare output values to expose specific information to the user:
output "instance_ip" { value = aws_instance.example.public_ip }Users can access the output value using
terraform output instance_ip.Variable Types: Terraform supports various variable types such as
string,number,list,map, etc. Use thetypeattribute to define the variable type.
Variables in Terraform provide a powerful mechanism to customize your infrastructure deployments and promote reusability across different environments.
Terraform with AWS (Amazon Web Services):
Prerequisites:
To use Terraform with AWS, you need to set up some prerequisites to ensure a smooth workflow. Here are the key steps:
1. AWS Account:
Have an AWS account. If you don't have one, sign up for an account on the AWS website.
2. AWS CLI Installation:
Install the AWS Command Line Interface (CLI) on your local machine. You can download it from the AWS CLI website.
3. AWS CLI Configuration:
Configure the AWS CLI with your AWS credentials.
aws configureCreate EC2 using Terraform:
Certainly! Below is a simple example of a Terraform configuration to create an EC2 instance in AWS. Save this configuration in a file named main.tf:
terraform {
required_providers{
aws = {
source = "hashicorp/aws"
version = "~> 4.16"
}
}
required_version = ">=1.2.0"
}
provider "aws" {
region = "us-east-1"
}
resource "aws_instance" "my_ec2_instance"{
ami="ami-0c7217cdde317cfec"
count = 5
instance_type="t2.micro"
tags = {
name = "Terraform-Test"
}
}In this configuration:
The AWS provider is configured with the desired region.
A variable
instance_typeis defined, allowing you to easily change the instance type.The
aws_instanceresource creates an EC2 instance with the specified AMI, instance type, and tags.An output block is added to display the public IP address of the created instance.
Steps to Use:
Initialize Terraform: Run the following command in the directory containing your
main.tffile:terraform initApply Terraform Configuration: Apply the configuration to create the EC2 instance:
terraform applyView Output:
After the apply is complete, you can view the public IP address of the created EC2 instance using:
terraform output public_ipTerraform will prompt you to confirm the planned changes. Type "yes" to proceed.

Result:

Create S3 Bucket using Terraform:
Certainly! Below is a simple example of creating an S3 bucket using Terraform. This example assumes you have the AWS CLI configured with the necessary credentials.
terraform {
required_providers{
aws = {
source = "hashicorp/aws"
version = "~> 4.16"
}
}
required_version = ">=1.2.0"
}
provider "aws" {
region = "us-east-1"
}
resource "aws_s3_bucket" "my_s3_bucket" {
bucket = "rahulsinha99"
tags= {
Name ="rahulsinha99"
Environment= "Dev"
}
}Save this configuration in a file named main.tf. Now, follow these steps:
Open a terminal in the directory where your
main.tffile is located.Initialize Terraform by running the following command:
terraform initReview the planned changes with:
terraform planApply the changes with:
terraform applyTerraform will prompt for confirmation. Type "yes" and press Enter.
Once the apply is complete, your S3 bucket will be created. You can find the bucket name in the Terraform output.

Result:

Now you have a basic S3 bucket created using Terraform. Customize the configuration to meet your specific requirements, and remember to handle sensitive information like access keys and secret keys securely in a development environment.
Terraform Meta-Arguments:
Meta-arguments in Terraform provide additional functionalities for resource management. Here's a brief description of each with example code:
depends_on: Specifies resource dependencies.resource "aws_instance" "example" { } resource "aws_eip" "example" { depends_on = [aws_instance.example] }count: Creates multiple instances of a resource.resource "aws_instance" "example" { count = 3 }provider: Defines provider configurations.provider "aws" { region = "us-west-2" }lifecycle: Manages resource lifecycle settings.resource "aws_instance" "example" { lifecycle { create_before_destroy = true } }provisioner: Configures resource provisioning settings.resource "aws_instance" "example" { provisioner "local-exec" { command = "echo ${aws_instance.example.private_ip} > private_ip.txt" } }for_each: It is a meta argument that helps in creating multiple instances of a defined resource. It also provides us with the flexibility of dynamically setting the attributes of each resource instance created, depending on the type of variables being used to create real-world replicas.for_eachprimarily works with a set of strings (set(string)) and map of strings (map(string)). The provided string values are used to set instance specific attributes.
A general syntax of using for_each meta argument is expressed below.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.16"
}
}
required_version = ">= 1.2.0"
}
provider "aws" {
region = "us-east-1"
}
locals {
instances = {"Rahul":"ami-0b0dcb5067f052a63","Kumar":"ami-08c40ec9ead489470","Sinha":"ami-08c40ec9ead489470","Rahul2":"ami-0b0dcb5067f052a63"}
}
resource "aws_instance" "aws_ec2_test" {
for_each = local.instances
ami = each.value
instance_type = "t2.micro"
tags = {
Name = each.key
}
}Terraform will prompt you to confirm the planned changes. Type "yes" to proceed.

Result:

Terraform State Management:
Terraform remote state:
terraform_remote_stateis a data sourcethat can be used to fetch details from the remote state file directly. This is useful when you need to reference the outputs of configurations that are stored in different state files. When anoutputblock is defined in your configuration, the contents are included in the state file. These details can then be referenced elsewhere in your project.terraform_remote_state Data Source Example:
For example, again, consider we have an Azure SQL Database and Azure Web App. The configuration files for these should be set up to use different state files. When creating our database that the web app needs to connect to, we add an
outputblock to expose the resulting ID of the database after it has been created:
output "sqldb_id" {
value = azurerm_sql_database.example.id
description = "Database ID"
}We can now reference the resulting SQL database ID in the Web App configuration code by configuring a data block using terraform_remote_state:
data "terraform_remote_state" "dev_sqldb" {
backend = "azurerm"
config = {
storage_account_name = "terraformsa"
container_name = "terraformstate"
key = "development/sqldb.tfstate"
}
}And then reference the name of the output where necessary in the configuration:
data.terraform_remote_state.dev_sqldb.outputs.sqldb_idTerraform State Locking:
Terraform state locking helps prevent concurrent modifications to the state file, which could lead to corruption. Here's an example demonstrating how to enable state locking using a remote backend like AWS S3:
First, configure your Terraform backend to use S3 for storing the state file. Update your Terraform configuration (
backend.tf) to specify the S3 backend:
terraform {
backend "s3" {
bucket = "your-terraform-state-bucket"
key = "terraform.tfstate"
region = "us-west-2"
dynamodb_table = "terraform-lock-table"
}
}Ensure you have an existing DynamoDB table for state locking. If not, create one manually or using Terraform:
resource "aws_dynamodb_table" "terraform_lock_table" {
name = "terraform-lock-table"
hash_key = "LockID"
read_capacity = 5
write_capacity = 5
attribute {
name = "LockID"
type = "S"
}
}Initialize Terraform to configure the backend and create the lock table if necessary:
terraform initNow, when you run Terraform commands such as
terraform applyorterraform destroy, Terraform will acquire a lock on the state file stored in S3 before performing any modifications. If another user or process tries to modify the state simultaneously, it will wait until the lock is released.Once the operation is complete, Terraform releases the lock, allowing other users or processes to acquire it for their operations.
By using state locking with a remote backend like AWS S3 and DynamoDB, you ensure that only one Terraform operation can modify the state at a time, reducing the risk of conflicts and corruption
Congratulations, Terraform Virtuoso! You've conducted a symphony of infrastructure orchestration, seamlessly transforming code into tangible environments. Your Terraform journey from zero to hero marks a milestone in the realm of DevOps orchestration. Onward to orchestration infrastructure with the precision of Terraform maestro! 🚀🌐.
