A Beginner's Journey into Terraform and Infrastructure as Code
Week 15- Diving into Infrastructure as Code and Terraform

Hello readers π! Upon completing a couple of beginner Kubernetes projects, I am feeling confident about my understanding of core Kubernetes concepts. Kubernetes is much more vast than I currently know, so I will continue to learn more about it in the future. Last week, I got started with Infrastructure as Code and Terraform. In this article, I will be sharing my learnings from the past week.
Infrastructure as Code
Before getting started with Terraform, one has to understand Infrastructure as Code, and I also started with the same question - What is IaC? Infrastructure as Code can be broken down into two terms - Infrastructure and Code.
Infrastructure refers to the underlying resources required to support applications and services, such as servers, networking components, operating systems, storage, databases, and security configurations. These resources are hidden from the user's eyes, but they are as important as the code of the application. Without these resources, an application cannot be deployed, maintained, and served to the audience.
Traditionally, physical/on-premises servers were used to deploy and maintain any application, with system administrators continuously monitoring everything. When the application's demand increased, the scaling process was done manually, and we all can imagine how painful and time-consuming that sounds.
But then the cloud revolution began. The evolution of cloud resources and providers such as AWS, GCP, Azure, Oracle, etc., came up with a solution to the traditional way of maintaining infrastructure for applications. The solution was to shift the focus from managing physical hardware to leveraging on-demand, scalable, and managed services over the internet. So now, instead of purchasing and configuring physical infrastructure, businesses can provision resources through cloud providers on a pay-per-use basis, significantly reducing upfront capital expenditures and operational costs.
However, with time, managing infrastructure through these cloud providers was becoming a pain too. Scaling up and down, replicating and tearing down infrastructure according to demand was becoming too time-consuming and was not fully automated.
Terraform Origin
AWS introduced their own IaC tool called AWS CloudFormation, which enables the automation and consistent provisioning of AWS resources through Infrastructure as Code. But this was only limited to AWS, forcing users and teams to use multiple tools for multi-cloud environments. This problem was highlighted by the co-founder of HashiCorp in 2011. Later in 2014, HashiCorp announced Terraform - an open-source Infrastructure as Code tool that could provide consistent workflows regardless of the cloud provider.
Terraform uses HCL (HashiCorp Configuration Language) to declare and provision resources across cloud providers. Terraform solves the complexity and fragility of managing infrastructure by turning it into code β making creation, change, and teardown of cloud (and some on-premises) resources repeatable, auditable, and automatable.
Some benefits of Terraform I learned:
Declarative: You describe what you want, not how to build it.
Idempotent & reproducible: Re-running the same configuration brings your infrastructure to the same desired state.
Versionable: Configuration files live in Git like application code (review, branch, rollback).
Multi-cloud & provider ecosystem: Single tool to manage many providers.
Plan/Apply safety: Previews changes before they happen.
Modularity & reuse: Modules let you package infrastructure patterns.
Terraform Basics
After thoroughly understanding the concept of IaC and its necessity, I focused on learning the core concepts of Terraform. I started by installing Terraform using Chocolatey on my laptop.
The first concept was Provider - it is a plugin that knows how to create resources on a platform (e.g., AWS, Google). It allows users to interact with different APIs and services. Resource is a cloud object or service of a provider that the user manages, such as EC2 instances, S3 buckets, etc. These are provisioned with the help of Terraform, which communicates with the provider to create, configure, or delete the resources.
Then I learned HCL (HashiCorp Configuration Language), which is used to write Terraform files (.tf file extension). It is a structured configuration language created by HashiCorp. An HCL file contains blocks, arguments, and expressions.
block_type "label1" "label2" {
argument = value
}
Arguments are key-value pairs inside blocks where the configurations are written about the resource, and expressions are the values assigned to the arguments, which can be variables, references, or functions. I also learned about variables, comments, and other basic syntax.
Then it was time to learn about the core and important commands of Terraform:
terraform init- Initializes a Terraform working directory and downloads provider plugins and modules.terraform validate- Validates syntax and checks if the configuration is structurally correct.terraform fmt- Formats.tffiles into a canonical style and ensures consistent formatting.terraform plan- Shows the execution plan of changes and compares current state vs desired state.terraform apply- Executes the changes shown in the plan.terraform destroy- Removes all managed infrastructure.
These are the commands that are used most frequently in the Terraform workflow. Upon completing these basic but essential concepts, I decided to try creating some local and AWS resources using Terraform.
Terraform Implementation
At first, I created a simple text file in the same repository with the help of the "local_file" resource like this:
resource "local_file" "my_file" {
filename = "example.txt"
content = "First file created with Terraform"
}
After this, I tried to create an S3 bucket with a simple HCL file like this:
resource "aws_s3_bucket" "terraform_bucket" {
bucket = "terraform-bucket-akshansh029"
}
After being able to create these simple resources with the help of Terraform's official documentation, I continued to learn advanced concepts like loops (count, for_each) and conditional expressions. Loops help users create multiple resources from a single block instead of duplicating them. The count argument creates N copies of the specified resource, whereas the for_each argument creates resources from a map or set of values. Along with these, I learned about interpolation arguments like user_data.
With these concepts, I then created multiple EC2 instances along with key-pairs, security groups, VPC, and installed Nginx on them by using a custom script in user_data like this:
# Key pair for EC2 instance
resource "aws_key_pair" "deployer" {
key_name = "terraform-ec2-key"
public_key = file("terraform-ec2-key.pub")
}
# VPC for EC2 instance
resource "aws_default_vpc" "default" {}
# Security group for EC2 instance
resource "aws_security_group" "allow_tls" {
name = "terraform-ec2-sg"
description = "Allow TLS inbound traffic and all outbound traffic"
vpc_id = aws_default_vpc.default.id # Interpolation
# inbound rules
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "SSH from anywhere"
}
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "Access to HTTP from anywhere"
}
# outbound rules
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "allow_tls"
}
}
# EC2 Instance
resource "aws_instance" "terraform_ec2" {
# count = 2 # meta argument
for_each = tomap({
terraform-ec2-instance-t2 = "t2.micro"
terraform-ec2-instance-t3 = "t3.micro"
})
ami = var.ubuntu_ec2_ami_id # Ubuntu 24
instance_type = each.value
key_name = aws_key_pair.deployer.key_name
security_groups = [aws_security_group.allow_tls.name]
root_block_device {
volume_size = var.env == "prd" ? var.aws_root_storage_size : var.aws_default_root_storage_size
volume_type = "gp3"
}
user_data = file("install_nginx.sh") # Run install_nginx script
tags = {
Name = each.key
}
}


Challenges I Faced
1. Didn't know which files not to push to GitHub
While maintaining the GitHub repository for my Terraform learning, I committed and pushed terraform.tfstate to GitHub. Later, I learned that state files should never be pushed to GitHub because they contain sensitive information about your infrastructure and can lead to security vulnerabilities. The state file also creates conflicts when multiple team members are working on the same infrastructure. I immediately removed it from the repository and added it to .gitignore.
2. Connection error during terraform init
While trying to initialize Terraform using terraform init, I encountered this error:
β Error: Failed to install provider β β Error while installing hashicorp/aws v6.11.0: β releases.hashicorp.com: read tcp β 172.20.146.187:54611->108.159.15.48:443: wsarecv: A β connection attempt failed because the connected party β did not properly respond after a period of time, or β established connection failed because connected host β has failed to respond.
This was a network connectivity issue where my college WiFi was blocking or limiting connections to HashiCorp's servers. The college network likely had firewall restrictions or proxy settings that prevented Terraform from downloading the required provider plugins. I solved this by switching to my personal mobile hotspot.
Resources I Used
Whatβs Next
I will continue to learn the advanced topics of Terraform and then to practice these, I will try to create an infrastructure using Terraform as a project.
Let's Connect!
π My LinkedIn
π My GitHub
If you have any recommended resources, better approaches to my challenges, or insights, I'd love to hear them! Drop your thoughts in the comments.
Have a wonderful day!



