Overview

Amazon EKS Capabilities is a set of fully managed cluster features that accelerate developer velocity and offload the complexity of building and scaling with Kubernetes. Among these capabilities, the ArgoCD Capability provides a fully managed GitOps continuous deployment solution—eliminating the operational overhead of installing, maintaining, and scaling Argo CD controllers on your clusters.

Argo CD is a declarative, GitOps continuous delivery tool for Kubernetes. It follows the GitOps pattern where Git repositories serve as the single source of truth for defining the desired application state. Argo CD continuously monitors these repositories and automatically syncs the cluster state to match the desired configuration.

With the EKS ArgoCD Capability, AWS handles scaling, upgrades, and inter-cluster communications, while providing native integrations with services like Amazon ECR, AWS Secrets Manager, and AWS CodeConnections.

Hub-and-Spoke Architecture

A common deployment pattern is the hub-and-spoke topology: the ArgoCD Capability runs on a dedicated central EKS cluster (the hub) that serves as the control plane for GitOps operations, managing deployments to multiple workload clusters (spokes). This provides a single pane of glass to orchestrate deployments across clusters—whether they’re in different regions, accounts, or have private API endpoints.

Hub-and-Spoke Architecture for EKS ArgoCD Capability Figure: Hub-and-spoke topology for EKS ArgoCD Capability (Source: AWS Containers Blog)

This post focuses on registering a spoke cluster to an existing hub cluster with ArgoCD Capability enabled.

Prerequisites

  • Hub cluster with ArgoCD Capability enabled
  • ArgoCD Capability IAM role ARN (e.g., ArgoCDCapabilityRole)
  • kubectl configured for hub cluster

Step 1: Grant ArgoCD Access to Spoke Cluster

Create an access entry and associate cluster admin policy on the spoke cluster:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# Create access entry
aws eks create-access-entry \
  --cluster-name <spoke-cluster> \
  --region <region> \
  --principal-arn arn:aws:iam::<account-id>:role/ArgoCDCapabilityRole

# Associate cluster admin policy
aws eks associate-access-policy \
  --cluster-name <spoke-cluster> \
  --region <region> \
  --principal-arn arn:aws:iam::<account-id>:role/ArgoCDCapabilityRole \
  --policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy \
  --access-scope type=cluster

Step 2: Create ArgoCD Project

Create a dedicated project for spoke workloads (skip if already exists):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: spoke-workloads
  namespace: argocd
spec:
  destinations:
    - namespace: '*'
      name: <spoke-cluster>  # Add each spoke cluster
  sourceRepos:
    - '*'
  clusterResourceWhitelist:
    - group: '*'
      kind: '*'
  sourceNamespaces:
    - argocd  # Required for apps in argocd namespace

To add more clusters to an existing project:

1
2
kubectl patch appproject spoke-workloads -n argocd --type=json \
  -p='[{"op": "add", "path": "/spec/destinations/-", "value": {"namespace": "*", "name": "<new-spoke-cluster>"}}]'

Step 3: Register Cluster via Secret

Create a Kubernetes secret to register the spoke cluster:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
apiVersion: v1
kind: Secret
metadata:
  name: <spoke-cluster>
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: cluster
    environment: dev  # Optional: for ApplicationSet selectors
  annotations:
    region: <region>  # Optional: metadata
stringData:
  name: <spoke-cluster>
  server: arn:aws:eks:<region>:<account-id>:cluster/<spoke-cluster>
  project: spoke-workloads

Important: The server field must be the EKS cluster ARN, not the Kubernetes API URL or IAM role ARN.

Step 4: Deploy Application

Create an ArgoCD Application targeting the spoke cluster:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app
  namespace: argocd
spec:
  project: spoke-workloads
  source:
    repoURL: https://github.com/org/repo
    targetRevision: HEAD
    path: .
  destination:
    name: <spoke-cluster>  # Matches the secret name
    namespace: default
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true

Quick Reference

FieldValueNote
server in SecretEKS cluster ARNarn:aws:eks:<region>:<account>:cluster/<name>
destination.nameCluster secret nameNot the server URL
sourceNamespacesargocdRequired in AppProject

Troubleshooting

Application stuck in Unknown status

Check if the project allows the application:

1
kubectl get application <app-name> -n argocd -o jsonpath='{.status.conditions[*].message}'

If you see “not permitted to use project”, ensure sourceNamespaces: [argocd] is set in the AppProject.

Cluster not reachable

Verify access entry exists:

1
aws eks list-access-entries --cluster-name <spoke-cluster> --region <region>

Verify access policy is associated:

1
2
3
4
aws eks list-associated-access-policies \
  --cluster-name <spoke-cluster> \
  --region <region> \
  --principal-arn arn:aws:iam::<account-id>:role/ArgoCDCapabilityRole

Force refresh application

1
2
kubectl patch application <app-name> -n argocd --type merge \
  -p '{"metadata":{"annotations":{"argocd.argoproj.io/refresh":"hard"}}}'