End-to-end GitOps With Terraform and ArgoCD For EKS
Architecture Diagram

We will be designing a production grade GitOps architecture to automate both infrastructure provisioning and application delivery on AWS using Terraform.
Overview
We will use Terraform to build the foundation. It will provision the VPC and networking components, deploy the Amazon EKS cluster, and use the Terraform Helm Provider to install ArgoCD directly into the cluster. The Git Repository contains all Kubernetes manifests, Helm charts, and configurations, and ArgoCD sits inside the EKS cluster, watching this repository, detecting any update status and automatically applying changes to EKS if a developer pushes a change to the repo. DevOps team access the ArgoCD UI/API through a dedicated Load Balancer to monitor deployment health. End-users on the other hand hit the actual deployed application, through a separate Load Balancer. This will help us manage traffic and access to the system in a controlled and organized manner.
Drift Detection
Because ArgoCD constantly compares the live state (EKS) with the desired state (Git), it provides automatic drift detection. If someone manually deletes a deployment in the cluster using kubectl, ArgoCD will see the discrepancy and immediately recreate it to match Git.
Code Overview
The main.tf file sets up a complete AWS environment for EKS (Elastic Kubernetes Service) with ArgoCD pre-installed.
VPC & Networking: Uses the
terraform-aws-modules/vpc/awsmodule to create a secure network with public and private subnets across two Availability Zones. It includes a NAT Gateway to allow private instances to access the internet.EKS Cluster: Provisions a managed Kubernetes cluster (v1.30) with a node group of
t3.mediuminstances.
Storage Support: Configures the EBS CSI Driver via IRSA (IAM Roles for Service Accounts), which allows Kubernetes to manage AWS Elastic Block Store volumes (needed for the Postgres database).
ArgoCD Bootstrapping:
Creates the
argocdnamespace.Fetches the official ArgoCD manifests directly via an HTTP data source and applies them using the
kubectl_manifestresource.Patches the
argocd-serverservice to be a LoadBalancer so you can access the ArgoCD UI from your browser.Automated App Deployment: Finally, it deploys the application defined in
manifests/argocd-app.yamlusing ArgoCD itself.
The manifests define a 3-tier web application:
argocd-app.yaml: This is the "App of Apps" or root configuration. It tells ArgoCD to watch a specific Git repository (gitops-manifest-awstf.git) and sync the configurations to the3tirewebapp-devnamespace.frontend.yaml&backend.yaml: Define the application layers. The frontend talks to the backend via aBACKEND_URLenvironment variable. Both useConfigMapsfor non-sensitive configuration.postgres.yaml: Sets up a PostgreSQL database with aPersistentVolumeClaim(PVC), ensuring data persists even if the pod restarts.secret.yaml: Contains the database credentials. Note that these are currently stored in plaintext (Base64 encoded by K8s), which leads into the security discussion.
Some concerns in Setup
Some configurations prioritize ease of use over strict production security. Here are the key issues and why they exist:
| Security Issue | Learning Use Case | Production Alternative |
Plaintext Secrets in secret.yaml | Makes it easy to see how K8s Secrets work without external dependencies. | Use AWS Secrets Manager, HashiCorp Vault, or Sealed Secrets. |
Public EKS Endpoint (cluster_endpoint_public_access = true) | Allows to run kubectl from their local machine without setting up a VPN or Bastion host. | Disable public access and use a Private Endpoint with a VPN. |
| Remote Manifest Fetching | Rapidly installs ArgoCD without keeping large YAML files in the repo. | Use Helm charts with pinned versions or local, audited manifests. |
| LoadBalancer for ArgoCD UI | Provides immediate access to the UI to see the GitOps magic in action. | Use an Ingress Controller with TLS and OAuth/OIDC for authentication. |
After deployment, I got the argocd url:



The Argo CD is running on the default http port. The web application is running on custom 3000 port. So we open it:

Video reference:
Arigato!