<aside> π€
Before we create any Terraform files, here is my planned proejct file structure (hierarchy) for the Terraform project - single environment:
terraform-aws/
βββ main.tf # calling modules
βββ providers.tf # declaring different providers (AWS, ...)
βββ variables.tf # inputs (regions, CIDRs, ...)
βββ locals.tf # constants (naming conventions, tags, ...)
βββ outputs.tf
βββ ...
βββ modules/
βββ **vpc/**
β βββ main.tf
β βββ variables.tf
βββ **ecr/**
β βββ main.tf
β βββ outputs.tf
βββ **...*other modules*/**
βββ ...
βββ ...
</aside>
From the local machine, create the Terraform provider configuration with a specified AWS region (us-east-1 in this example), and initialize the Terraform directory:
/* variables.tf (root) */
variable "region_primary" {
description = "Primary AWS Region to deploy resources"
type = string
default = "us-east-1"
}
/* provider.tf (root) */
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.0"
}
}
required_version = ">= 1.2"
}
provider "aws" {
region = var.region_primary
}
terraform init
Before working on the anything, first we need to output the AWS account ID, which will be necessary for the whole guide:
/* main.tf (root) */
# Getting Account ID
data "aws_caller_identity" "current" {}
/* output.tf (root) */
output "aws_account_id" {
value = data.aws_caller_identity.current.account_id
}
<aside> β οΈ
With Terraform, you can create new ECR repositories directly with resource "aws_ecr_repository", but once the repositories are not empty (there are images inside), you cannot delete them unless you have force_delete = true
β In this project, Iβll create the ECR repos in the console/CLI instead separately, then reference them in the Terraform project with data "aws_ecr_repository" instead
</aside>
With Terraform, create an ECR repo (private) for each app / GitHub repo
Create the repositories through AWS Console or AWS CLI

aws ecr create-repository --repository-name <repository_name> --region us-east-1
Reference them in Terraform
/* main.tf (root) */
module "ecr" {
source = "./modules/ecr"
}
### CREATE A NEW MODULE: **ecr** ###
/* modules/**ecr**/main.tf */
****
data "aws_ecr_repository" "app_frontend_repo" {
name = "mortredn/eks-demo-coffeeshop-frontend"
}
data "aws_ecr_repository" "app_customer_repo" {
name = "mortredn/eks-demo-coffeeshop-customer"
}
data "aws_ecr_repository" "app_shopping_repo" {
name = "mortredn/eks-demo-coffeeshop-shopping"
}
Also, we can output the reposβ URLs so we can reference it later in our k8s:
/* modules/**ecr**/outputs.tf */
****
output "app_urls" {
value = {
frontend = data.aws_ecr_repository.app_frontend_repo.repository_url
customer = data.aws_ecr_repository.app_customer_repo.repository_url
shopping = data.aws_ecr_repository.app_shopping_repo.repository_url
}
}
/* outputs.tf (root) */
output "ecr_app_urls" {
value = module.ecr.app_urls
}
For each Terraform configuration for each step from now on, use these terminal commands to apply, in order to create the necessary infrastructure. In step 1, we will create the ECR repositories on your AWS account:
# (Later for every module created, we will need to initialize the Terraform project again. We have already init as instructed above, no need to init now)
terraform init
# Validate and Terraform will print out a success or error message
terraform validate
# Format and Terraform will print out the names of files it modifies
terraform fmt
# Preview the changes Terraform will make to your infrastructure
terraform plan -out tf.plan
# (Optionally if you want to check the plan with a text file)
terraform show -no-color tf.plan > tfplan.txt
# After the plan is verified and ready, apply the configuration
terraform apply "tf.plan"

Build each Docker image, tag them with AWS tags, then push to ECR (Push commands are available at console)
For example, t3a instances use amd64 CPU, while t4g ones use arm64 CPU.
Using amd64 images on a t4g.micro instance cause lower performance
Some AWS instance types use AWSβs own processor - Graviton: https://aws.amazon.com/ec2/graviton/
β Better and cheaper instance types, but because it is ARM-based, so image platform must be considered
# Make sure AWS CLI is installed and authenticated, login docker to ECR
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin <**aws_account_id**>.dkr.ecr.us-east-1.amazonaws.com
# Navigate to each app repo, build & tag the image, then push it to AWS
docker build -t mortredn/eks-demo-coffeeshop-frontend:latest .
docker tag mortredn/eks-demo-coffeeshop-frontend:latest <**aws_account_id**>.dkr.ecr.us-east-1.amazonaws.com/mortredn/eks-demo-coffeeshop-frontend:latest
docker push <aws_id>.dkr.ecr.us-east-1.amazonaws.com/mortredn/eks-demo-coffeeshop-frontend:latest
# Or building for multi-platform and pushing straight to AWS, use buildx
docker buildx build --platform linux/amd64,linux/arm64 -t <**aws_account_id**>.dkr.ecr.us-east-1.amazonaws.com/mortredn/eks-demo-coffeeshop-frontend:latest --provenance=false --push .
<**aws_account_id**> through the Terraform projectβs outputapp_urls