Cloud infrastructure • Terraform • Ansible • AWS

AWS three-tier infrastructure lab.

I built a disposable AWS environment to practice the full infrastructure lifecycle: provision a segmented network, configure three Linux servers, verify the application and network boundaries, then tear the environment down without leaving running resources.

This is a working project, not a proposed architecture. On September 22, 2026, the lab created 28 Terraform resources, configured three EC2 instances, passed its tests, and destroyed all 28 resources.

Lab scope: Single availability zone, operator-restricted HTTP, and deliberately short-lived resources. This is not a high-availability production deployment. The environment has been destroyed; there is no live API endpoint to visit.
WorkstationOperator IPv4 /32: HTTP and SSH to public web tier
Public web tierEC2 · Nginx reverse proxy · SSH bastion
↓ HTTP :8000
Private application tierEC2 · Python API · Gunicorn · systemd
↓ PostgreSQL :5432
Private database tierEC2 · PostgreSQL 16
Security groups allow intended tier-to-tier traffic; direct web-to-database access is denied.

What I built

Terraform defines the AWS infrastructure. Ansible configures each instance after it boots. The environment is created for testing and then removed.

Provisioning

Terraform

VPC, three subnets, route tables, internet gateway, NAT gateway, security groups, EC2 instances, SSH key pair, and encrypted EBS volumes.

Configuration

Ansible

Installs PostgreSQL on the database host, deploys the Python API on the application host, and configures Nginx on the public web host.

Testing

Network boundaries

Confirms app-to-database access, web-to-app access, denial of direct web-to-database access, and a database-backed external HTTP health response.

Build, test, destroy

The project deliberately exercises operational behavior rather than stopping after a successful terraform apply.

01
Guard against accidental deployment.
Explicit --run, root-account refusal, operator IP restriction, and isolated run directory.
02
Provision AWS resources.
Terraform creates the three-tier VPC and EC2 environment.
03
Configure Linux services.
Ansible deploys Nginx, the Python application, and PostgreSQL.
04
Run configuration twice.
The second Ansible run reports changed=0 on all three hosts.
05
Validate the traffic path.
Tests the intended ports, denied database access, reverse proxy, and PostgreSQL SELECT 1.
06
Destroy and verify cleanup.
Terraform deletes 28 resources; separate AWS CLI queries confirm no remaining tagged EC2 instances, NAT gateway, Elastic IP, or VPC.
Cost note: EC2, NAT gateways, public IPv4 addresses, and related resources can incur charges. A budget alert is not a spending cap. The runner attempts teardown if tests fail, and a recovery script is available if a run is interrupted.

Verified deployment — September 22, 2026

These are results from the completed run in us-east-1, not simulated console images.

CheckObserved result
Terraform provisioning28 resources created
Ansible first configurationThree hosts; zero failures
Ansible second configurationZero changes on all three hosts
Application → PostgreSQLPassed
Web → applicationPassed
Direct web → PostgreSQLBlocked as intended
External Nginx → application → databasestatus=ok; db_result=1
Terraform teardown28 resources destroyed
Independent AWS cleanup checksNo remaining lab instances, NAT gateways, Elastic IPs, or VPCs
GitHub ActionsTerraform and Ansible static validation passed

Evidence screenshots

The console screenshot was captured while the instances existed. The validation logs and CI screenshots were preserved after teardown.

Three AWS EC2 instances named web, app, and db running with passing status checks
All three EC2 instances running and passing status checks. Open full-size image.
Successful GitHub Actions Terraform and Ansible validation
GitHub Actions passed its static checks. This CI job does not deploy AWS resources.
Ansible configuration, zero-change idempotency run, network tests, external health check, and Terraform lifecycle results
Preserved Ansible recaps, connectivity checks, database health response, and lifecycle summary. Open full-size image to read the output.

What this demonstrates

Cloud architecture

Separate public and private tiers, route tables, NAT, SSH bastion, and security-group-controlled traffic.

Automation and repeatability

Provisioning with Terraform, host configuration with Ansible, versioned dependencies, and zero-change idempotency checks.

Operational discipline

Positive and negative smoke tests, retained evidence, explicit destruction, and independent cleanup verification.

Continuous integration

GitHub Actions checks Terraform formatting and validation, Ansible playbook syntax, and shell scripts without running billable cloud infrastructure.

Back to top