Configure the provider
provider "aws" {
region = "ap-southeast-1"
default_tags {
tags = { Project = "devops-lab", Owner = "fresher" }
}
}
The AWS provider obtains credentials from a supported chain such as environment, shared configuration and credentials, workload identity, or an instance role. Never hard-code access keys in .tf files or put real credentials in an exercise. aws sts get-caller-identity confirms which account and identity Terraform will use; AWS_PROFILE=lab selects a named profile. default_tags labels every resource, which makes cost reports and cleanup much easier.
flowchart LR CRED["AWS credentials<br/>profile or role"] --> PROV["provider aws<br/>region"] PROV --> VPC["aws_vpc<br/>10.0.0.0/16"] VPC --> SUB["aws_subnet<br/>public"] VPC --> IGW["internet gateway<br/>+ route table"] SUB --> SG["security group<br/>allow 22 from my IP"] SG --> EC2["aws_instance<br/>t3.micro"] %% step CRED: Credentials come from a profile, environment variables or a role, never from the .tf file. %% step PROV: The provider block chooses the region. %% step VPC: The VPC is created first because everything else references it. %% step SUB: Subnets split the VPC's address range. %% step IGW: An internet gateway and a route make the subnet public. %% step SG: The security group opens only the ports you need. %% step EC2: The instance comes last; terraform destroy removes everything when the lab is done.
Common building blocks
| Resource | Purpose |
|---|---|
aws_vpc |
Private network with a CIDR block |
aws_subnet |
Range inside one Availability Zone |
aws_internet_gateway |
Connects the VPC to the internet |
aws_nat_gateway |
Outbound internet for private subnets |
aws_route_table |
Routes for associated subnets |
aws_security_group |
Stateful firewall for resources |
aws_instance |
EC2 virtual machine |
aws_s3_bucket |
Object storage |
A typical VPC lab
A VPC lab commonly has an IGW attached to the VPC, a public route table with 0.0.0.0/0 toward the IGW, a NAT Gateway in a public subnet with an Elastic IP, and a private route toward the NAT. NAT provides outbound access from private subnets but does not make an instance public.
Internet ↔ IGW ↔ public subnet [NAT, public workload]
↓ outbound only
private subnet [private workload]
A Security Group is a stateful firewall attached to resources; a route table selects paths and does not open ports. EBS attachments and EC2 instances should express dependencies through references rather than hard-coded IDs, for example subnet_id = aws_subnet.public.id. Look up an AMI with a data "aws_ami" block instead of copying an ID that differs per region.
Run it
| Step | Command |
|---|---|
| Initialize the AWS provider | terraform init |
| Save reviewed changes | terraform plan -var-file=prod.tfvars -out=tfplan |
| Execute the saved plan | terraform apply tfplan |
| Inspect tracking | terraform state list |
| Remove the lab | terraform destroy |
For shared work, keep state in an S3 backend rather than on one laptop, and give the pipeline an IAM role with only the permissions it needs.
Cost and cleanup
A NAT Gateway charges for time and data; after a lab, review the destroy plan and clean up in the correct workspace. Elastic IPs, load balancers, and large instance types also cost money while idle, so set an AWS Budgets alert on your lab account.