Skip to content

Module Structure

terraform-aws-arc-argocd

Latest Release Last Updated Terraform GitHub Actions

Quality gate

Overview

SourceFuse AWS Reference Architecture (ARC) Terraform module for deploying and managing ArgoCD on Amazon EKS clusters. This module provides a production-ready ArgoCD installation with comprehensive AWS service integrations, following security and operational best practices.

Key Features

  • GitOps Continuous Delivery: Declarative application deployment and lifecycle management
  • AWS Load Balancer Integration: Automated ALB provisioning with HTTPS termination
  • Certificate Management: Automatic ACM certificate creation with DNS validation
  • DNS Automation: Route53 record creation and management
  • Secure AWS Access: IAM Roles for Service Accounts (IRSA) for pod-level permissions
  • High Availability: Multi-replica deployment with Redis HA support
  • Image Automation: ArgoCD Image Updater for automated container updates
  • Multi-Cluster Support: ApplicationSet controller for managing applications across clusters
  • Enterprise SSO: Dex integration for GitHub, OIDC, SAML authentication
  • Event Notifications: Integration with Slack, email, and webhook services

Architecture

The module deploys the following components:

  1. ArgoCD Core Components

    • Application Controller: Monitors applications and synchronizes desired state
    • API Server: Provides API and UI for ArgoCD
    • Repository Server: Manages Git repository connections and manifests
    • Redis: Caching layer for improved performance
  2. AWS Infrastructure

    • Application Load Balancer (ALB) for external access
    • ACM Certificate for HTTPS encryption
    • Route53 DNS records for domain resolution
    • IAM roles and policies for AWS service access
  3. Optional Components

    • ArgoCD Image Updater for automated image updates
    • Notifications Controller for event-driven alerts
    • Dex for SSO authentication

Prerequisites

  • Terraform >= 1.6.0
  • AWS CLI configured with appropriate credentials
  • Existing EKS cluster with OIDC provider enabled
  • VPC with public subnets (for internet-facing ALB)
  • Route53 hosted zone (optional, for automatic DNS management)

Usage

External Access (ALB + HTTPS)

module "argocd" {
  source = "sourcefuse/arc-argocd/aws"

  namespace   = "myapp"
  environment = "prod"

  eks_cluster_name = "my-eks-cluster"

  argocd_config = {
    enable  = true
    version = "7.8.13"

    helm_release_set_values = [
      {
        name  = "configs.cm.url"
        value = "https://argocd.example.com"
      },
      {
        name  = "configs.params.server\\.insecure"
        value = "true"
      }
    ]
  }

  ingress_config = {
    enable                     = true
    host                       = "argocd.example.com"
    ingress_class_name         = "alb"
    create_acm_certificate     = true
    auto_create_route53_record = true
    route53_zone_name          = "example.com"
    alb_subnets                = ["subnet-xxxxx", "subnet-yyyyy"]
  }

  irsa_config = {
    enable = true
  }

  tags = {
    Project   = "myapp"
    ManagedBy = "terraform"
  }
}

With Existing Certificate

module "argocd" {
  source = "sourcefuse/arc-argocd/aws"

  # ... other configuration ...

  ingress_config = {
    enable              = true
    host                = "argocd.example.com"
    acm_certificate_arn = "arn:aws:acm:us-east-1:123456789:certificate/xxxxx"
  }
}

High Availability Configuration

module "argocd" {
  source = "sourcefuse/arc-argocd/aws"

  # ... other configuration ...

  ha_config = {
    enable               = true
    server_replicas      = 3
    repo_server_replicas = 2
    controller_replicas  = 1
  }
}

With ArgoCD Resources (Repositories, Projects, Applications)

module "argocd" {
  source = "sourcefuse/arc-argocd/aws"

  namespace   = "myapp"
  environment = "prod"

  eks_cluster_name = "my-eks-cluster"

  argocd_config = {
    enable  = true
    version = "7.8.13"
  }

  # Define repositories
  repositories = {
    "my-private-repo" = {
      url      = "https://github.com/myorg/myrepo"
      type     = "git"
      username = "git"
      password = var.github_token
    }
    "my-helm-repo" = {
      url  = "https://charts.example.com"
      type = "helm"
    }
  }

  # Define projects
  projects = {
    "production" = {
      description  = "Production applications"
      source_repos = ["https://github.com/myorg/*"]
      destinations = [
        {
          namespace = "prod-*"
          server    = "https://kubernetes.default.svc"
        }
      ]
    }
  }

  # Define applications
  applications = {
    "my-app" = {
      repo_url        = "https://github.com/myorg/myrepo"
      target_revision = "main"
      path            = "k8s/overlays/prod"
      project         = "production"
      destination = {
        namespace = "prod-apps"
        server    = "https://kubernetes.default.svc"
      }
      sync_policy = {
        automated = {
          prune     = true
          self_heal = true
        }
        sync_options = ["CreateNamespace=true"]
      }
    }
    "my-helm-app" = {
      repo_url        = "https://charts.example.com"
      target_revision = "1.0.0"
      path            = "my-chart"
      project         = "production"
      destination = {
        namespace = "prod-apps"
      }
      helm = {
        release_name = "my-release"
        parameters = [
          {
            name  = "image.tag"
            value = "v1.0.0"
          }
        ]
      }
      sync_policy = {
        automated = {
          prune     = true
          self_heal = true
        }
      }
    }
  }

  tags = {
    Project   = "myapp"
    ManagedBy = "terraform"
  }
}

Examples

Requirements

Name Version
terraform >= 1.6.0
aws >= 5.0, < 7.0
helm >= 2.12.1
http >= 3.0.0
kubectl >= 2.0.0
kubernetes >= 2.24.0
time >= 0.9.0

Providers

Name Version
aws 6.32.1
helm 3.1.1
kubectl 2.1.3
kubernetes 3.0.1
time 0.13.1

Modules

No modules.

Resources

Name Type
aws_acm_certificate.argocd resource
aws_acm_certificate_validation.argocd resource
aws_iam_policy.ecr_readonly resource
aws_iam_role.argocd_repo_server resource
aws_iam_role.argocd_server resource
aws_iam_role_policy_attachment.argocd_repo_server resource
aws_iam_role_policy_attachment.argocd_server resource
aws_iam_role_policy_attachment.repo_server_ecr resource
aws_route53_record.acm_validation resource
aws_route53_record.argocd resource
helm_release.argocd resource
helm_release.argocd_image_updater resource
kubectl_manifest.argocd_application resource
kubectl_manifest.argocd_project resource
kubectl_manifest.argocd_repository resource
time_sleep.wait_for_argocd_crds resource
aws_acm_certificate.this data source
aws_acm_certificate.wildcard data source
aws_caller_identity.current data source
aws_eks_cluster.this data source
aws_iam_policy_document.argocd_assume_role data source
aws_iam_policy_document.ecr_readonly data source
aws_region.current data source
aws_route53_zone.this data source
kubernetes_ingress_v1.argocd data source

Inputs

Name Description Type Default Required
admin_config ArgoCD admin account configuration.

- disable_admin : Disable the built-in admin user (recommended for prod with SSO).
- bcrypt_hash : Bcrypt hash of the admin password. Leave empty to auto-generate.
object({
disable_admin = optional(bool, false)
bcrypt_hash = optional(string, "")
})
{} no
applications Map of ArgoCD applications to create.

Each application requires:
- repo_url : Git repository URL
- target_revision : Git revision (branch, tag, commit)
- path : Path within repository
- project : ArgoCD project name
- destination : Destination cluster and namespace
- sync_policy : Sync policy configuration
- helm : Helm-specific configuration
map(object({
repo_url = string
target_revision = optional(string, "HEAD")
path = optional(string)
project = optional(string, "default")
finalizers = optional(list(string))
destination = optional(object({
server = optional(string, "https://kubernetes.default.svc")
namespace = optional(string, "default")
}))
sync_policy = optional(object({
automated = optional(object({
prune = optional(bool, false)
self_heal = optional(bool, false)
}))
sync_options = optional(list(string))
retry = optional(object({
limit = optional(number, 5)
backoff = optional(object({
duration = optional(string, "5s")
factor = optional(number, 2)
max_duration = optional(string, "3m")
}))
}))
}))
helm = optional(object({
release_name = optional(string)
values = optional(string)
value_files = optional(list(string))
parameters = optional(list(object({
name = string
value = string
})))
}))
}))
{} no
applicationset_config ApplicationSet controller configuration.

- enable : Enable ApplicationSet controller (default: true — ships with ArgoCD chart).
- replicas : Number of ApplicationSet controller replicas.
object({
enable = optional(bool, true)
replicas = optional(number, 1)
})
{} no
argocd_config Configuration for the ArgoCD Helm release.

- enable : Toggle ArgoCD installation (default: true).
- name : Helm release name (default: "argocd").
- chart : Helm chart name (default: "argo-cd").
- helm_repository : Helm chart repository URL.
- version : Helm chart version to install.
- namespace : Kubernetes namespace for ArgoCD.
- create_namespace : Whether to create the namespace if it does not exist.
- timeout : Helm install/upgrade timeout in seconds.
- wait : Wait for all resources to be ready.
- atomic : Rollback on failure.
- cleanup_on_fail : Delete new resources on failed install.
- helm_release_values : List of YAML value strings for the Helm release.
- helm_release_set_values : List of individual set values [{name, value}].
object({
enable = optional(bool, true)
name = optional(string, "argocd")
chart = optional(string, "argo-cd")
helm_repository = optional(string, "https://argoproj.github.io/argo-helm")
version = optional(string, "7.8.13")
namespace = optional(string, "argocd")
create_namespace = optional(bool, true)
timeout = optional(number, 600)
wait = optional(bool, true)
atomic = optional(bool, true)
cleanup_on_fail = optional(bool, true)
helm_release_values = optional(list(string), [])
helm_release_set_values = optional(list(object({
name = string
value = string
})), [])
})
{} no
eks_cluster_name Name of the existing EKS cluster where ArgoCD will be installed. string n/a yes
environment ID element for the deployment environment (e.g., prod, staging, dev). string n/a yes
ha_config High availability configuration for ArgoCD.

- enable : Enable HA mode with multiple replicas (default: false).
- server_replicas : Number of ArgoCD server replicas.
- repo_server_replicas : Number of ArgoCD repo-server replicas.
- controller_replicas : Number of application controller replicas.
object({
enable = optional(bool, false)
server_replicas = optional(number, 2)
repo_server_replicas = optional(number, 2)
controller_replicas = optional(number, 1)
})
{} no
image_updater_config ArgoCD Image Updater configuration for automatic image update detection.

- enable : Deploy ArgoCD Image Updater alongside ArgoCD.
- chart : Helm chart name (default: "argocd-image-updater").
- version : Helm chart version for image updater.
- helm_release_values : List of YAML value strings for image updater Helm release.
- registries : List of container registries to monitor.
object({
enable = optional(bool, false)
chart = optional(string, "argocd-image-updater")
version = optional(string, "0.12.0")
helm_release_values = optional(list(string), [])
registries = optional(list(object({
name = string
api_url = string
prefix = string
})), [])
})
{} no
ingress_config Ingress configuration for exposing ArgoCD UI.

- enable : Enable ingress resource creation via Helm values.
- host : Hostname for ArgoCD UI (e.g., argocd.example.com).
- ingress_class_name : Kubernetes ingress class (default: "alb").
- annotations : Additional annotations for the ingress resource.
- tls_enabled : Enable TLS termination.
- acm_certificate_arn: ACM certificate ARN for ALB HTTPS listener.
- auto_create_route53_record : Automatically create Route53 A record for the ingress.
- route53_zone_name : Route53 hosted zone name (e.g., example.com) for automatic DNS record creation.
object({
enable = optional(bool, false)
host = optional(string, "")
ingress_class_name = optional(string, "alb")
annotations = optional(map(string), {})
tls_enabled = optional(bool, true)
acm_certificate_arn = optional(string, "")
create_acm_certificate = optional(bool, false)
auto_create_route53_record = optional(bool, false)
route53_zone_name = optional(string, "")
alb_subnets = optional(list(string), [])
})
{} no
irsa_config IAM Roles for Service Accounts (IRSA) configuration.

- enable : Create IRSA roles for ArgoCD components.
- server_role_name : IAM role name for ArgoCD server.
- server_policy_arns : Additional policy ARNs to attach to server role.
- repo_server_role_name : IAM role name for repo-server (for private ECR Helm charts, etc.).
- repo_server_policy_arns : Additional policy ARNs for repo-server role.
object({
enable = optional(bool, false)
server_role_name = optional(string, "")
server_policy_arns = optional(list(string), [])
repo_server_role_name = optional(string, "")
repo_server_policy_arns = optional(list(string), [])
})
{} no
namespace Namespace for the resources. Used as a prefix for resource names. string n/a yes
notifications_config ArgoCD Notifications controller configuration.

- enable : Enable the notifications controller.
- triggers : Map of notification triggers.
- templates: Map of notification templates.
- services : Map of notification services (slack, webhook, email, etc.).
object({
enable = optional(bool, false)
triggers = optional(map(string), {})
templates = optional(map(string), {})
services = optional(map(string), {})
})
{} no
projects Map of ArgoCD projects to create.

Each project can have:
- description : Project description
- source_repos : List of allowed source repositories
- destinations : List of allowed destinations
- cluster_resource_whitelist : Cluster-scoped resources whitelist
- namespace_resource_whitelist : Namespace-scoped resources whitelist
map(object({
description = optional(string)
source_repos = optional(list(string))
destinations = optional(list(object({
namespace = string
server = string
})))
cluster_resource_whitelist = optional(list(object({
group = string
kind = string
})))
namespace_resource_whitelist = optional(list(object({
group = string
kind = string
})))
}))
{} no
repositories Map of ArgoCD repositories to create.

Each repository can have:
- url : Repository URL (required)
- type : Repository type (git, helm) (default: git)
- username : Username for authentication
- password : Password for authentication
- ssh_private_key : SSH private key for authentication
- insecure : Skip TLS verification
- enable_lfs : Enable Git LFS
map(object({
url = string
type = optional(string, "git")
username = optional(string)
password = optional(string)
ssh_private_key = optional(string)
insecure = optional(bool)
enable_lfs = optional(bool)
}))
{} no
sso_config SSO / Dex configuration for ArgoCD authentication.

- enable : Enable Dex SSO integration.
- provider : SSO provider type (e.g., "github", "oidc", "saml").
- config : Provider-specific configuration map (clientID, clientSecret, org, etc.).
object({
enable = optional(bool, false)
provider = optional(string, "")
config = optional(map(string), {})
})
{} no
tags Tags to apply to all resources. map(string) {} no

Outputs

Name Description
acm_certificate_arn ACM certificate ARN used for ArgoCD ingress.
alb_dns_name DNS name of the ALB created for ArgoCD ingress.
applications Map of created ArgoCD applications.
argocd_server_url The ArgoCD server URL (based on ingress host or cluster-internal service).
chart_version The ArgoCD Helm chart version deployed.
image_updater_status Status of the ArgoCD Image Updater Helm release.
name The name of the ArgoCD Helm release.
namespace The Kubernetes namespace where ArgoCD is installed.
projects Map of created ArgoCD projects.
repo_server_iam_role_arn ARN of the IAM role for the ArgoCD repo-server service account.
repositories Map of created ArgoCD repositories.
route53_record_fqdn FQDN of the Route53 record created for ArgoCD.
server_iam_role_arn ARN of the IAM role for the ArgoCD server service account.
status Status of the ArgoCD Helm release.

Security Considerations

  1. IRSA: Always enable IRSA for production deployments to follow least-privilege principles
  2. TLS Termination: Use ACM certificates for HTTPS encryption
  3. Network Security: Deploy ALB in public subnets, ArgoCD pods in private subnets
  4. Admin Account: Disable the default admin account when using SSO
  5. RBAC: Configure proper role-based access control for users and applications

Troubleshooting

ArgoCD UI Returns 504 Gateway Timeout

Cause: ALB cannot connect to ArgoCD pods or health checks are failing.

Solution: 1. Verify ArgoCD is running in insecure mode: configs.params.server.insecure = true 2. Check ALB backend protocol is HTTP (not HTTPS) 3. Ensure security groups allow traffic from ALB to pods on port 8080 4. Verify target group health: aws elbv2 describe-target-health --target-group-arn <arn>

Certificate Validation Stuck

Cause: DNS validation records not created or Route53 zone not accessible.

Solution: 1. Verify Route53 hosted zone exists and is in the same AWS account 2. Check DNS validation records were created: aws acm describe-certificate --certificate-arn <arn> 3. Ensure auto_create_route53_record = true if using automatic validation 4. Wait 5-10 minutes for DNS propagation

Best Practices

  1. Use IRSA: Enable IRSA for secure AWS service access without long-lived credentials
  2. Enable HA: For production workloads, enable high availability mode
  3. Separate Environments: Use different namespaces and clusters for dev/staging/prod
  4. Version Pinning: Pin ArgoCD chart version to avoid unexpected upgrades
  5. Backup Configuration: Regularly backup ArgoCD configuration and application definitions
  6. Monitor Resources: Set up CloudWatch alarms for ArgoCD pod health and ALB metrics
  7. Use SSO: Configure SSO instead of relying on the default admin account
  8. Network Segmentation: Deploy ArgoCD in private subnets, expose only via ALB

Upgrade Guide

Upgrading ArgoCD Version

  1. Review ArgoCD release notes for breaking changes
  2. Update the version in your configuration:
    1
    2
    3
    argocd_config = {
      version = "7.9.0"  # New version
    }
    
  3. Run terraform plan to review changes
  4. Apply during maintenance window: terraform apply
  5. Verify all applications sync successfully after upgrade

Upgrading Module Version

  1. Review module changelog for breaking changes
  2. Update module version:
    1
    2
    3
    4
    module "argocd" {
      source  = "sourcefuse/arc-argocd/aws"
      version = "2.0.0"  # New version
    }
    
  3. Run terraform init -upgrade
  4. Review plan carefully: terraform plan
  5. Apply changes: terraform apply

Development

Prerequisites

Configurations

  • Configure pre-commit hooks
    pre-commit install
    
  • Configure golang deps for tests
    > go get github.com/gruntwork-io/terratest/modules/terraform
    > go get github.com/stretchr/testify/assert
    

Git commits

while Contributing or doing git commit please specify the breaking change in your commit message whether its major,minor or patch

For Example

git commit -m "your commit message #major"
By specifying this , it will bump the version and if you dont specify this in your commit message then by default it will consider patch and will bump that accordingly

Tests

  • Tests are available in test directory
  • In the test directory, run the below command
    go test
    

Authors

This project is authored by below people

  • SourceFuse ARC Team