Complete AWS EKS-based infrastructure with GitOps deployment using ArgoCD. Fully automated provisioning via Terraform with multi-AZ high availability, featuring production-grade security with bastion host access and AWS Secrets Manager integration.
┌──────────────────────────────────────────────────────────────────┐
│ AWS Cloud (us-east-1) │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ VPC (Multi-AZ) │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ Public Subnet│ │ Public Subnet│ │ Public Subnet│ │ │
│ │ │ (AZ-1) │ │ (AZ-2) │ │ (AZ-3) │ │ │
│ │ │ Bastion │ │ NAT GW │ │ NAT GW │ │ │
│ │ └──────┬───────┘ └──────────────┘ └──────────────┘ │ │
│ │ │ │ │
│ │ ┌──────▼───────┐ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │Private Subnet│ │Private Subnet│ │Private Subnet│ │ │
│ │ │ EKS Nodes │ │ EKS Nodes │ │ EKS Nodes │ │ │
│ │ │ + RDS │ │ │ │ │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────┘ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────┐ ┌─────────────┐ ┌──────────────────────────┐ │
│ │ ECR Repo │ │ RDS MySQL │ │ EKS Cluster + ArgoCD │ │
│ │ │ │ (Secrets │ │ - External Secrets Ops │ │
│ │ │ │ Manager) │ │ - Jenkins │ │
│ └────────────┘ └─────────────┘ └──────────────────────────┘ │
│ │
│ ┌──────────────────────┐ ┌──────────────────────────────────┐ │
│ │ AWS Secrets Manager │ │ IAM Pod Identity (7 roles) │ │
│ │ - DB Password │ │ - EKS, Nodes, EBS CSI │ │
│ │ - Application Creds │ │ - Jenkins, ArgoCD, ESO, App │ │
│ └──────────────────────┘ └──────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
# 1. Configure AWS credentials
cd terraform/environment/<env> # dev or prod
cat > creds << EOF
[default]
aws_access_key_id = YOUR_KEY
aws_secret_access_key = YOUR_SECRET
EOF
# 2. Configure variables
vim terraform.tfvars
# 3. Deploy infrastructure
terraform init
terraform apply
# 4. Access cluster
aws eks update-kubeconfig --name bigrs-cluster --region us-east-1
kubectl get nodes
# 5. Access via bastion (prod only)
aws ssm start-session --target <bastion-instance-id>Infrastructure/
├── README.md # This file
├── argocd/ # ArgoCD bootstrap config
│ ├── bootstrap-app.yaml # App of Apps pattern
│ └── README.md
├── helm-values/ # Helm chart values
│ ├── argocd-values.yaml # ArgoCD customization
│ └── README.md
├── scripts/ # Automation scripts
│ ├── bootstrap-argocd.sh # Install ArgoCD
│ ├── cleanup-argocd.sh # Remove ArgoCD safely
│ ├── delete-nlb.sh # Cleanup AWS NLB
│ ├── README.md
│ └── env/ # Environment configs
│ ├── dev/
│ └── prod/
└── terraform/
├── README.md
├── environment/
│ ├── dev/ # Dev environment
│ │ ├── main.tf # Module orchestration
│ │ ├── variables.tf
│ │ ├── terraform.tfvars # Configuration values
│ │ ├── outputs.tf
│ │ ├── provider.tf
│ │ ├── data.tf
│ │ ├── creds # AWS credentials (gitignored)
│ │ └── README.md
│ └── prod/ # Production environment
│ └── ... (same structure)
└── modules/ # Reusable modules
├── argocd/ # ArgoCD deployment
├── bastion/ # Bastion host (prod only)
├── ecr/ # Container registry
├── eks/ # Kubernetes cluster
├── iam/ # IAM roles & policies
├── rds/ # MySQL database
└── vpc/ # Network infrastructure
| Component | Purpose | Key Resources | Environments |
|---|---|---|---|
| VPC | Network isolation | 3 public + 3 private subnets, NAT gateways | dev, prod |
| EKS | Kubernetes cluster | Control plane, node groups, addons | dev, prod |
| IAM | Access control | 7 IAM roles, 5 pod identity associations | dev, prod |
| ECR | Container registry | KMS encrypted, lifecycle policies | dev, prod |
| RDS | MySQL database | Private subnet, EKS access only, Secrets Manager | dev, prod |
| Bastion | Secure SSH access | EC2 in public subnet, SSM Session Manager | prod only |
| ArgoCD | GitOps | App of Apps pattern, bootstrap config | dev, prod |
1. VPC → Network foundation (with bastion subnet in prod)
2. ECR → Container registry
3. IAM → Roles created (includes bastion role in prod)
4. Bastion → Bastion host created (prod only)
5. EKS → Cluster with roles
6. IAM → Pod identities (after cluster)
7. RDS → Database in private subnet (password from Secrets Manager)
8. ArgoCD → GitOps deployment
- ✅ Multi-AZ HA - 3 availability zones
- ✅ GitOps Ready - ArgoCD with App of Apps
- ✅ Pod Identity - Modern IRSA replacement
- ✅ Auto Networking - VPC, NAT, security groups
- ✅ Secure - Private subnets, encrypted storage
- ✅ External Secrets - AWS Secrets Manager integration
- ✅ Bastion Host - Secure access via SSM (prod only)
- ✅ Secrets Management - RDS password in AWS Secrets Manager
- ✅ ECR Integration - Automated token refresh for image pulls
- ✅ Auto Cleanup - Scales down ArgoCD controllers on destroy
- ✅ NLB Cleanup - Automated script to delete AWS-created load balancers
VPC ──────┐
├──> EKS ──┐
IAM ──────┘ ├──> ArgoCD
↓ │
Bastion (prod) ├──> Platform Apps
│
ECR ──────────> IAM │
│
VPC + EKS ────> RDS ─┘
- Purpose: Testing and development
- Components: VPC, EKS, ECR, RDS, IAM, ArgoCD
- Access: Direct kubectl access
- RDS: Single-AZ, minimal resources
- Node Type: t3.small
- No bastion - Direct access allowed
- Purpose: Production workloads
- Components: All dev components + Bastion host
- Access: Via bastion host only
- RDS: Password stored in AWS Secrets Manager
- Bastion: EC2 t3.micro with SSM Session Manager
- Security: Enhanced with bastion-only SSH access
- Node Type: t3.small (3-6 nodes with autoscaling)
| File | Purpose |
|---|---|
terraform/environment/<env>/terraform.tfvars |
Configuration values |
terraform/environment/<env>/creds |
AWS credentials (gitignored) |
scripts/bootstrap-argocd.sh |
ArgoCD installation |
scripts/cleanup-argocd.sh |
ArgoCD cleanup |
scripts/delete-nlb.sh |
NLB cleanup (AWS-created load balancers) |
argocd/bootstrap-app.yaml |
App of Apps config |
# Cluster information
terraform output cluster_name
terraform output cluster_endpoint
# Container registry
terraform output ecr_repository_url
# Database
terraform output rds_endpoint
# Bastion (prod only)
terraform output bastion_instance_id
terraform output ssm_connect_commandcd terraform/environment/<env> # dev or prod
terraform apply# Edit variables
vim terraform.tfvars
# Apply changes
terraform applyaws eks update-kubeconfig --name bigrs-cluster --region us-east-1
kubectl get all -n argocd# Connect to bastion via SSM
aws ssm start-session --target <bastion-instance-id>
# From bastion, access cluster
aws eks update-kubeconfig --name bigrs-cluster --region us-east-1
kubectl get nodes# ArgoCD and nginx-ingress create AWS NLBs that aren't tracked by Terraform
# Use this script to clean them up before destroy
bash scripts/delete-nlb.sh# First, cleanup NLBs created by Kubernetes
bash scripts/delete-nlb.sh
# Then destroy Terraform resources
terraform destroyNote: ArgoCD cleanup is automated - controllers are scaled down before deletion to prevent app recreation.
Each folder has detailed README.md:
- argocd/ - Bootstrap configuration
- helm-values/ - Helm customization
- scripts/ - Automation scripts
- terraform/ - IaC overview
- terraform/environment/dev/ - Dev environment config
- terraform/environment/prod/ - Prod environment config
- terraform/modules/*/ - Module documentation
Related Documentation:
- Platform Repository - ArgoCD apps, Helm values, manifests
# Check ArgoCD status
kubectl get pods -n argocd
# View applications
kubectl get applications -n argocd
# Manually cleanup if needed
bash scripts/cleanup-argocd.sh# Update kubeconfig
aws eks update-kubeconfig --name bigrs-cluster --region us-east-1
# Test connection
kubectl cluster-info# Verify instance is running
aws ec2 describe-instances --instance-ids <bastion-id>
# Connect via SSM
aws ssm start-session --target <bastion-id>
# If SSM fails, check IAM permissions and VPC endpoints# Reinitialize
terraform init -upgrade
# View state
terraform state list
# View plan
terraform plan# List all NLBs
aws elbv2 describe-load-balancers --query 'LoadBalancers[*].[LoadBalancerName,LoadBalancerArn]'
# Use cleanup script
bash scripts/delete-nlb.sh
# Or manually delete
aws elbv2 delete-load-balancer --load-balancer-arn <arn>- Bastion Access: All SSH access goes through bastion host
- SSM Session Manager: No direct SSH keys needed
- Private EKS: Nodes in private subnets only
- Secrets Manager: Database passwords never in plain text
- Pod Identity: Secure AWS access for pods
- ECR Encryption: Images encrypted with KMS
- Network Isolation: Security groups restrict access
- RDS Password: Stored in AWS Secrets Manager
- GitHub Token: Passed as environment variable, stored in Kubernetes secret
- AWS Credentials: Stored in local
credsfile (gitignored) - Application Secrets: Managed by External Secrets Operator
- Terraform 1.5+
- AWS Provider 6.20.0
- Kubernetes 1.31+
- ArgoCD 8.1.0
- Helm 3.x
- Bash (automation scripts)
- AWS SSM Session Manager (bastion access)
- AWS Secrets Manager (sensitive data storage)
- State Management: S3 backend recommended for production use
- Credentials: Never commit
credsfile to Git - Pod Identity: Modern replacement for IRSA
- Auto-versioning: EKS addons use latest compatible versions
- External Secrets: Syncs secrets from AWS Secrets Manager to Kubernetes
- ECR Token Refresh: Automated CronJob keeps ECR credentials current
- Cleanup: Controllers scaled to 0 before destruction (prevents app recreation)
- Bastion: Production-only for enhanced security
- RDS Password: Retrieved from AWS Secrets Manager dynamically
- NLB Cleanup: Required before Terraform destroy to remove AWS-managed load balancers
- Dev: Single-AZ RDS, minimal node count
- Prod: Consider Reserved Instances for cost savings
- ECR: Lifecycle policies automatically delete old images
- NAT: 3 NAT gateways for HA (consider reducing for dev)
- Bastion: t3.micro instance (minimal cost)
- RDS: db.t3.micro for dev, scale up for prod
- Create feature branch from
main - Test in
devenvironment - Create pull request to
main - After approval, merge to
prodbranch - Deploy to production environment
MIT
BIGRS-ITI Team