Deploy a basic Nginx web server on AWS using Terraform, fully provisioned from scratch with Infrastructure as Code with no manual console clicks required.
Live server here: http://13.48.3.121/
This project provisions a complete, minimal AWS network stack and launches a
single EC2 instance running Nginx, which serves a static HTML page on boot via
a user_data script.
What gets created:
-
A custom VPC
-
A public subnet
-
An Internet Gateway
-
A route table (routing
0.0.0.0/0→ IGW) with a subnet association -
A security group allowing inbound SSH (22) and HTTP (80)
-
An SSH key pair (generated by Terraform)
-
An EC2 instance in the public subnet, bootstrapped via
user_datato install Nginx and serve a custom HTML page
- Terraform >= 1.14.0
- An AWS account with programmatic access (Access Key ID + Secret Access Key)
- AWS CLI configured locally (
aws configure), or the equivalent environment variables set:export AWS_ACCESS_KEY_ID="..." export AWS_SECRET_ACCESS_KEY="..." export AWS_DEFAULT_REGION="eu-north-1"
.
├── backend.tf # Terraform backend configuration (state storage)
├── ec2.tf # Security group, key pair, EC2 instance, AMI data source
├── iam.tf # IAM user, access key, and minimal policies
├── locals.tf # Common tags and other local values
├── output.tf # Outputs (public IP, private key, etc.)
├── provider.tf # Provider configuration (aws, tls, random)
├── s3.tf # S3 bucket resource(s)
├── user_data.sh # Boot script: installs Nginx and serves the HTML page
├── variables.tf # Input variables (region, project name, team, etc.)
├── version.tf # Required Terraform and provider version constraints
├── vpc.tf # VPC, subnet, IGW, route table, route table associations
└── README.md
1. Initialize Terraform (downloads the aws, tls, and random providers):
terraform init2. Format and validate scripts
terraform fmtterraform validate2. Review the plan:
terraform plan3. Apply:
terraform applyConfirm with yes when prompted.
4. Retrieve your SSH key, accesss, secret key, the instance's public IP:
terraform output -raw private_key_pem > server-key.pem
chmod 400 server-key.pem
terraform output -raw instance_public_ip5. SSH into the instance (optional — Nginx is already running via user_data,
so this is only needed for debugging):
ssh -i server-key.pem ubuntu@$(terraform output -raw instance_public_ip)6. View the web page:
Open http://<instance_public_ip> in a browser. You should see a styled page
displaying the project name and challenge details.
| Name | Description | Default |
|---|---|---|
region |
AWS region to deploy into | eu-north-1 |
project |
Project name, used in resource tags | Web-server-deployment |
team |
Team name, used in resource tags | Developer Team |
environment |
Environment tag | production |
bucket_name |
Name of bucket for terraform state | ogechukwu-web-server-bucket-hug2026 |
| Name | Description |
|---|---|
private_key_pem |
The generated SSH private key (sensitive) |
instance_public_ip |
Public IP address of the EC2 instance |
access and secret key |
Access and secret key |
ec2.tfloads the boot script fromuser_data.sh(viafile()), rather than inlining the script directly — keep the two files in sync if you edit the HTML or install steps.user_dataonly runs on first boot. If you change the boot script after the instance already exists, you'll need to taint and recreate it for the change to take effect:terraform taint aws_instance.web terraform apply
- The security group currently allows SSH (22) from
0.0.0.0/0for hackathon convenience. In a real deployment, restrict this to your own IP. terraform.tfstatecontains sensitive values (including the private key) in plaintext and should never be committed to version control — it's already excluded via.gitignore.
To avoid ongoing AWS charges once you're done:
terraform destroyThis removes every resource created above.