This project introduces Terraform — the most widely used Infrastructure as Code tool in the DevOps industry. Instead of clicking through the AWS console to create resources, you describe what you want in a `.tf` file, run one command, and Terraform creates everything automatically. You will provision three real AWS resources: an S3 bucket for file storage, an EC2 instance to run a server, and a security group to control access. You will also learn how Terraform tracks what it has created using a state file — the mechanism that makes updates and deletions safe. Your Terraform Code (.tf files) | v terraform plan (shows what will change) | v terraform apply (creates the resources) | +----+----+ | | v v S3 Bucket EC2 Instance (storage) (+ security group) | | +---------+ | terraform.tfstate (tracks what exists)
Manually creating AWS resources through the console causes two major problems. First, nobody can see exactly how the infrastructure was built — there is no record of which settings were chosen, which checkboxes were ticked, or why. When something breaks, reconstruction is guesswork. Second, creating the same setup in a second environment (staging, production) means clicking through every screen again and hoping you match the original exactly. Mistakes creep in. Environments drift apart. Terraform solves both problems. Your `.tf` files are the exact specification of your infrastructure — checked into Git, reviewed in pull requests, and applied identically across every environment.
### Step 1: Install Terraform and Configure AWS ```bash ## Install Terraform using tfenv (manages multiple versions) git clone https://github.com/tfutils/tfenv.git ~/.tfenv export PATH="$HOME/.tfenv/bin:$PATH" echo 'export PATH="$HOME/.tfenv/bin:$PATH"' >> ~/.bashrc tfenv install 1.7.0 tfenv use 1.7.0 terraform --version ## Expected: Terraform v1.7.0 ## Configure AWS credentials ## Create an IAM user in AWS console with programmatic access ## Attach the AdministratorAccess policy (for learning only) aws configure ## AWS Access Key ID: your-access-key ## AWS Secret Access Key: your-secret-key ## Default region name: ap-south-1 ## Default output format: json ## Verify AWS credentials work aws sts get-caller-identity ## Expected: Shows your AWS account ID and IAM user name ``` > ⚠️ **Security:** For this learning project, AdministratorAccess is acceptable. In a real job, always use the principle of least privilege — only grant the permissions Terraform actually needs. Create specific IAM policies for each use case. ### Step 2: Understand the Terraform Workflow Before writing any code, understand the three commands you will use constantly: * `terraform init` — Downloads the AWS provider plugin. Run once per new project. * `terraform plan` — Shows what Terraform WILL DO without actually doing it. Always run this before apply. * `terraform apply` — Actually creates, updates, or destroys resources. Always review the plan first. * `terraform destroy` — Deletes everything Terraform created. Useful for cleanup. ```bash ## Create your project directory mkdir terraform-basics && cd terraform-basics ``` ### Step 3: Write Your First Terraform Configuration Create `main.tf`: ```hcl ## Tell Terraform which providers (cloud SDKs) to use terraform { required_version = ">= 1.7.0" required_providers { aws = { source = "hashicorp/aws" version = "~> 5.0" } } } ## Configure the AWS provider ## This is how Terraform knows which region to use provider "aws" { region = "ap-south-1" # Mumbai, India } ``` Create `variables.tf` — this keeps your configuration flexible: ```hcl ## Variables let you reuse the same code with different values ## Think of them like function parameters variable "project_name" { description = "Name prefix for all resources we create" type = string default = "devops-learning" } variable "environment" { description = "Deployment environment" type = string default = "development" # Validation ensures only valid values are accepted validation { condition = contains( ["development", "staging", "production"], var.environment ) error_message = "Must be development, staging, or production." } } variable "my_ip" { description = "Your IP for SSH access (find at checkip.amazonaws.com)" type = string } ``` Create `outputs.tf` — values Terraform prints after applying: ```hcl ## Outputs are like the return value of your Terraform code ## They print useful information after terraform apply completes output "s3_bucket_name" { description = "Name of the created S3 bucket" value = aws_s3_bucket.main.bucket } output "ec2_public_ip" { description = "Public IP of the EC2 instance" value = aws_instance.web.public_ip } output "ec2_instance_id" { description = "Instance ID for AWS console" value = aws_instance.web.id } ``` ### Step 4: Create an S3 Bucket Add to `main.tf`: ```hcl ## Local values — computed values reused throughout the config locals { common_tags = { Project = var.project_name Environment = var.environment ManagedBy = "terraform" Owner = "devops-network-student" } } ## S3 Bucket for file storage ## Resource format: resource "TYPE" "LOCAL_NAME" ## TYPE = AWS resource type ## LOCAL_NAME = name used within Terraform code only resource "aws_s3_bucket" "main" { # Bucket names must be globally unique across ALL AWS accounts # Adding a random suffix ensures uniqueness bucket = "${var.project_name}-${var.environment}-files" tags = local.common_tags } ## Enable versioning — keeps old versions of files when overwritten resource "aws_s3_bucket_versioning" "main" { bucket = aws_s3_bucket.main.id versioning_configuration { status = "Enabled" } } ## Block all public access — S3 buckets are private by default ## This resource makes that explicit and prevents accidental exposure resource "aws_s3_bucket_public_access_block" "main" { bucket = aws_s3_bucket.main.id block_public_acls = true block_public_policy = true ignore_public_acls = true restrict_public_buckets = true } ## Enable server-side encryption — all files encrypted at rest resource "aws_s3_bucket_server_side_encryption_configuration" "main" { bucket = aws_s3_bucket.main.id rule { apply_server_side_encryption_by_default { sse_algorithm = "AES256" } } } ``` ### Step 5: Create a Security Group and EC2 Instance Add to `main.tf`: ```hcl ## Security Group — controls which traffic can reach the EC2 instance ## Like a virtual firewall specific to this instance resource "aws_security_group" "web" { name = "${var.project_name}-web-sg" description = "Allow SSH from my IP and HTTP from anywhere" # Allow SSH only from your IP address ingress { description = "SSH from my IP only" from_port = 22 to_port = 22 protocol = "tcp" cidr_blocks = ["${var.my_ip}/32"] } # Allow HTTP web traffic from anywhere ingress { description = "HTTP from internet" from_port = 80 to_port = 80 protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } # Allow all outbound traffic # (so the server can download packages, reach AWS APIs, etc.) egress { description = "All outbound traffic" from_port = 0 to_port = 0 protocol = "-1" # -1 means all protocols cidr_blocks = ["0.0.0.0/0"] } tags = local.common_tags } ## Find the latest Ubuntu 22.04 AMI automatically ## This data source queries AWS for current AMI IDs ## Much better than hardcoding an AMI ID that may become outdated data "aws_ami" "ubuntu" { most_recent = true owners = ["099720109477"] # Canonical (Ubuntu's publisher) filter { name = "name" values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"] } filter { name = "virtualization-type" values = ["hvm"] } } ## EC2 Instance resource "aws_instance" "web" { ami = data.aws_ami.ubuntu.id instance_type = "t2.micro" # Free tier eligible vpc_security_group_ids = [aws_security_group.web.id] # User data runs when the instance first boots # This installs a basic web server automatically user_data = <<-EOF #!/bin/bash apt update -y apt install -y nginx systemctl start nginx systemctl enable nginx echo '<h1>Provisioned by Terraform!</h1>' \ > /var/www/html/index.html EOF tags = merge(local.common_tags, { Name = "${var.project_name}-web-server" }) } ``` ### Step 6: Initialise, Plan, and Apply ```bash ## Step 1: Initialise Terraform ## Downloads the AWS provider plugin (only needed once per project) terraform init ## Expected: Terraform has been successfully initialized! ## Step 2: Validate your configuration syntax terraform validate ## Expected: Success! The configuration is valid. ## Step 3: Create a terraform.tfvars file with your values cat > terraform.tfvars << EOF project_name = "devops-learning" environment = "development" my_ip = "$(curl -s https://checkip.amazonaws.com)" EOF ## Step 4: Preview what Terraform will create terraform plan ## Expected: Plan: 7 to add, 0 to change, 0 to destroy ## Read every line — understand what is being created ## Step 5: Apply the configuration terraform apply ## Type 'yes' when prompted to confirm ## Expected: Apply complete! Resources: 7 added. ## Step 6: See the outputs terraform output ## Expected: ## ec2_instance_id = "i-0abc123" ## ec2_public_ip = "13.x.x.x" ## s3_bucket_name = "devops-learning-development-files" ``` > 💡 **Tip:** Always run `terraform plan` before `terraform apply`. The plan output tells you exactly what will be created, changed, or destroyed. Never apply without reviewing the plan — this habit saves you from expensive mistakes. ### Step 7: Explore State and Make a Change The state file is how Terraform knows what already exists. ```bash ## View the state file (do not edit manually) cat terraform.tfstate ## Expected: JSON file listing all created resources and their IDs ## List all resources in state terraform state list ## Expected: ## aws_instance.web ## aws_s3_bucket.main ## aws_s3_bucket_versioning.main ## aws_s3_bucket_public_access_block.main ## aws_s3_bucket_server_side_encryption_configuration.main ## aws_security_group.web ## data.aws_ami.ubuntu ## Show details of a specific resource terraform state show aws_instance.web ## Expected: All attributes of the EC2 instance ## Make a change — add a tag to the EC2 instance ## Edit main.tf, add 'Team = "platform"' to the instance tags ## Then run plan to see the change terraform plan ## Expected: Plan: 0 to add, 1 to change, 0 to destroy ## The ~ symbol means modify in place terraform apply ## Terraform only changes the tag — everything else stays the same ``` ### Step 8: Clean Up ```bash ## Destroy all resources when done ## This is important — EC2 instances cost money when running terraform plan -destroy ## Review what will be deleted terraform destroy ## Type 'yes' to confirm ## Expected: Destroy complete! Resources: 7 destroyed. ## Verify in AWS console that everything is gone aws ec2 describe-instances \ --filters "Name=tag:Project,Values=devops-learning" \ --query 'Reservations[].Instances[].State.Name' ## Expected: ["terminated"] ```
```bash ## 1. Verify S3 bucket was created with encryption aws s3api get-bucket-encryption \ --bucket devops-learning-development-files ## Expected: SSEAlgorithm = AES256 ## 2. Verify S3 bucket blocks public access aws s3api get-public-access-block \ --bucket devops-learning-development-files ## Expected: all four settings = true ## 3. Verify EC2 is running and Nginx works EC2_IP=$(terraform output -raw ec2_public_ip) curl http://$EC2_IP ## Expected: HTML with 'Provisioned by Terraform!' ## 4. Test idempotency (the most important Terraform property) ## Run apply again — nothing should change terraform apply ## Expected: Apply complete! Resources: 0 added, 0 changed, 0 destroyed. ## Running apply twice is safe — Terraform only changes what differs ## 5. Verify state file exists ls -la terraform.tfstate ## Expected: File exists with recent timestamp ## 6. Test changing a variable value ## Change environment to 'staging' in terraform.tfvars ## Run plan — Terraform will want to recreate resources ## with the new name (staging instead of development) terraform plan ## Expected: Several resources to be destroyed and created ## (because the bucket name includes the environment) ## This shows how Terraform handles configuration changes ## 7. Clean up before paying for EC2 terraform destroy --auto-approve echo "Terraform project complete!" ```
This project introduces Terraform — the most widely used Infrastructure as Code tool in the DevOps industry. Instead of ...
Manually creating AWS resources through the console causes two major problems. First, nobody can see exactly how the inf...
Step 1: Install Terraform and Configure AWS > ⚠️ Security: For this learning project, AdministratorAccess is acceptable....
...
Aligns directly with DevOps, Site Reliability (SRE), and Platform Engineering job descriptions.