Skip to content

Repository files navigation

Terraform AWS Product Manager

A small product-management application deployed with Docker, Terraform, AWS, and Ansible.

The application has a static Nginx frontend and an Express/LowDB API. Locally, both run with Docker Compose. In AWS, Terraform creates the infrastructure and an Ansible controller deploys images from Amazon ECR.

Architecture

graph LR
  A[Browser] --> B[Frontend EC2 - Nginx];
  B --> C[Backend ALB];
  C --> D[Backend EC2 Instances];
  D --> E["EFS(db.json)"];
  F[GitHub Actions] -->|Builds Docker images| G[Amazon ECR];
  G -->|Frontend image| B;
  G -->|Backend image| D;
  H[Terraform] -->|Creates| I[Ansible Controller EC2];
  I -->|Deploys frontend| B;
  I -->|Deploys backend| D;
Loading

Terraform creates a VPC, two public and two private subnets, NAT gateways, a public Application Load Balancer, an Ansible controller, one frontend instance, and two backend instances.

Environment variables and secrets

Install and authenticate the following tools before deploying:

  • Docker Desktop/Engine with Docker Compose (Local development)
  • Terraform 1.x
  • AWS CLI v2
  • AWS credentials for an account allowed to create the resources in this project

Configure the AWS CLI using an IAM identity with appropriate permissions:

aws configure

This stores these required credentials in your local AWS CLI profile (or supply equivalent environment variables in CI):

AWS_ACCESS_KEY_ID=<your access key ID>
AWS_SECRET_ACCESS_KEY=<your secret access key>
AWS_REGION=us-east-1

Do not commit AWS credentials, .env files, Terraform state, or private keys. The repository ignores these files.

Value Where to configure it Keep secret?
AWS_ACCESS_KEY_ID Local AWS CLI profile or environment variable; GitHub Actions AWS_ACCESS_KEY_ID repository secret Yes
AWS_SECRET_ACCESS_KEY Local AWS CLI profile or environment variable; GitHub Actions AWS_SECRET_ACCESS_KEY repository secret Yes
AWS_REGION Local AWS CLI profile or environment variable; GitHub Actions AWS_REGION repository variable No
API_BASE_URL Docker Compose or the Ansible deployment command No
terraform/modules/aws-key.pem Generated locally by Terraform Yes

PORT is optional for the backend and defaults to 8080. API_BASE_URL is supplied automatically for local Compose and by the Ansible initialization script for AWS; only set it yourself when running the frontend outside those flows.

Deploy to AWS

Cost notice: this creates EC2 instances, two NAT gateways, an Application Load Balancer, and other AWS resources that can incur charges. Destroy the stack when you are finished.

Existing deployments: migrate ECR state

If this stack was applied before ECR was split into terraform/ecr, apply the main Terraform configuration once before creating the new repositories:

terraform -chdir=terraform init
terraform -chdir=terraform apply

The migration removes the legacy main_repository from the main state without deleting it. Review the plan to confirm that the repository is not being destroyed, then remove the legacy repository manually after its images are no longer needed.

1. Create the ECR repositories

The application images must exist in ECR before the main Terraform stack creates the instances.

terraform -chdir=terraform/ecr init
terraform -chdir=terraform/ecr apply

Review the plan and type yes when Terraform asks for confirmation.

2. Build and push the application images

Auto build and push after updating frontend or backend

The GitHub Actions workflows run automatically when the relevant frontend or backend directories change on main.

Run manually in GitHub Actions

Use Run workflow in GitHub Actions to dispatch the Build and Push Backend and Build and Push Frontend workflows manually. For GitHub Actions, configure the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY repository secrets and an AWS_REGION repository variable (for example, us-east-1).

Build and push locally

Set the AWS account and registry variables:

AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
AWS_REGION=us-east-1
ECR_REGISTRY="$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com"

Sign Docker in to ECR:

aws ecr get-login-password --region "$AWS_REGION" \
  | docker login --username AWS --password-stdin "$ECR_REGISTRY"

Build and push both images:

docker build -t "$ECR_REGISTRY/main_repository/backend:latest" backend
docker build -t "$ECR_REGISTRY/main_repository/frontend:latest" frontend
docker push "$ECR_REGISTRY/main_repository/backend:latest"
docker push "$ECR_REGISTRY/main_repository/frontend:latest"

3. Create the AWS infrastructure

terraform -chdir=terraform init
terraform -chdir=terraform apply

Terraform generates an SSH private key at terraform/modules/aws-key.pem, stores a copy as a SecureString in Systems Manager Parameter Store, and starts the Ansible controller.

Get the server IP/DNS and SSH

  • Retrieve the frontend EC2 public IP or DNS from the AWS Console or with the AWS CLI. Example (filters may vary by tags in your deployment):
aws ec2 describe-instances --filters "Name=tag:Name,Values=ansible" "Name=instance-state-name,Values=running" --query 'Reservations[].Instances[].[PublicIpAddress]' --output text
  • Ensure the private key is readable and SSH to the instance (replace user with ec2-user/ubuntu as appropriate):
chmod 400 terraform/modules/aws-key.pem
ssh -i terraform/modules/aws-key.pem ec2-user@<PUBLIC_IP_OR_DNS>

If the AMI uses a different default user, replace ec2-user with ubuntu, centos, or admin accordingly.

Clone this repository and run the initialization script manually on the Ansible controller:

sudo dnf update -y
sudo dnf install -y ansible-core git python3-boto3
git clone https://github.com/AvivAbachi/terraform-aws-ansible-project.git
cd terraform-aws-ansible-project
chmod +x ansible-init.sh
./ansible-init.sh

The script installs the controller dependencies, retrieves its SSH key, discovers the EC2 instances, installs Docker, and deploys the ECR images.

Wait for the EC2 instances and load balancer target health checks to become healthy. Then open the public IP or DNS name of the frontend EC2 instance in a browser. The frontend calls the backend through the public Application Load Balancer.

Clean up AWS resources

Destroy the main stack first, then the ECR repositories:

terraform -chdir=terraform destroy
terraform -chdir=terraform/ecr destroy

Local development

Docker Desktop (or Docker Engine) with Docker Compose v2 is required. No secrets or environment setup are needed; Docker Compose supplies API_BASE_URL=http://localhost:8080 to the frontend.

From the repository root, start the application:

docker compose -f docker-compose.dev.yml up --build

Open http://localhost:8081. The API is available at http://localhost:8080.

Verify the API:

curl http://localhost:8080/health

Stop the containers with Ctrl+C, or remove the containers while keeping the data volume:

docker compose -f docker-compose.dev.yml down

To also delete local product data, run:

docker compose -f docker-compose.dev.yml down -v

Reference

Based on the AWS + Terraform + Ansible mini-project.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages