~/ nmadiraju.com Amazon EKS · Argo CD capability access
arrow_back Back to nmadiraju
Amazon EKS · GitOps

Reaching a managed Argo CD on Amazon EKS: endpoints, identity and hub-and-spoke

The EKS Capability for Argo CD does not run in your cluster, so the usual playbook of an Ingress, a load balancer and a Dex config does not apply. This is how an enterprise actually reaches it: which endpoint people hit, how to take it off the internet, who can sign in, how pipelines authenticate, and how one instance reaches clusters in other accounts.

bolt TL;DR

The managed Argo CD runs in AWS-managed infrastructure, not on your worker nodes, and it has exactly one way in: an AWS-hosted HTTPS endpoint (the serverUrl) that serves both the UI and the API. That endpoint is public by default. Enterprises make it private by creating an interface VPC endpoint for eks-capabilities and attaching it to the capability, which blocks public access and makes the same hostname resolve to private IPs that on-premises users reach over Direct Connect or VPN. People sign in only through IAM Identity Center, which can federate to Okta or Entra ID; Identity Center users and groups map to three global roles, and AppProject roles decide which Applications each team sees. Pipelines cannot use argocd login and use project tokens instead. For a fleet, one capability in a platform account reaches clusters in other accounts and Regions, even fully private ones, through an EKS Access Entry for its IAM role on each target, with no IRSA, role chaining or VPC peering. You give up Dex, local users, the notifications controller and non-EKS targets in exchange for not running Argo CD at all.

1Where the managed Argo CD actually runs

With a self-managed install, Argo CD is a set of pods in your cluster, and exposing it means an Ingress, a load balancer, a certificate and a DNS record. The EKS Capability for Argo CD changes that. AWS runs the Argo CD controllers, the repo server and the API server in its own infrastructure, outside your account. Your worker nodes never run an Argo CD pod and do not need a network path to Git or to Helm registries.

What stays in your cluster is the data. The Argo CD custom resources (Application, ApplicationSet, AppProject) and the cluster registration Secrets live in a namespace you choose when you create the capability, usually argocd. EKS installs the custom resource definitions for you, and you manage these objects with kubectl or Git like any other manifest.

AWS-managed · EKS capabilitiesAWS Region · your accountCluster VPCPrivate subnetsAmazon EKS cluster1234Platform engineerbrowser or CLIGit repositorymanifestsArgo CD endpointUI · API (serverUrl)Argo CDcontrollers + repo serverKubernetes APIEKS control planeargocd namespaceApplications · SecretsWorker nodesno Argo CD pods
  • 1An engineer opens the capability's endpoint in a browser, or points the CLI at it.
  • 2Argo CD, running in AWS-managed infrastructure, pulls the desired manifests from Git.
  • 3It reads the Application resources through the cluster's Kubernetes API and applies the manifests the same way.
  • 4The Kubernetes control plane schedules the resulting pods onto worker nodes, which run no Argo CD components.
Figure 1 — Argo CD runs outside your account; only its custom resources and Secrets live in your cluster.

The endpoint has the form https://{capability-name}-{hash}-{account-id}.eks-capabilities.{region}.amazonaws.com. You can set an optional endpointPrefix for a friendlier hostname. Find the URL in the EKS console (your cluster, then the Capabilities tab, then Open Argo CD UI), or with the AWS CLI once the capability is ACTIVE:

aws eks describe-capability \
  --region us-east-1 \
  --cluster-name platform-hub \
  --capability-name argocd \
  --query 'capability.configuration.argoCd.serverUrl' \
  --output text

That one hostname is the whole access surface. The UI, the API, the CLI over gRPC-web and Git webhooks (${serverUrl}/api/webhook) all use it. Every access decision in the rest of this article is about who can reach that hostname, and who they are once they get there.

2The default: a public endpoint behind Identity Center

Out of the box, the endpoint is reachable from the internet. Anyone can reach the sign-in page, but nobody gets past it without an IAM Identity Center identity that you have mapped to an Argo CD role. For a proof of concept, or for an organization whose access model is "the identity provider is the perimeter", that can be enough.

Public internetAWS-managed1234EngineerbrowserCI pipelineproject tokenArgo CD endpointpublic HTTPSIAM IdentityCenterCorporate IdPOkta · Entra ID
  • 1An engineer opens the public endpoint from any network.
  • 2Argo CD redirects the browser to IAM Identity Center to sign in.
  • 3Identity Center hands authentication to the corporate identity provider, where MFA and conditional access apply.
  • 4A CI pipeline calls the same endpoint with a project token, not with SSO.
Figure 2 — The default. The endpoint is public; the identity provider is the only gate.

Two things push most enterprises past this default. Security reviews usually want deployment tooling unreachable from the internet, whatever the authentication. And the public path is also the path a stolen token would use: a token works from anywhere the endpoint is reachable.

3Private-only access from the corporate network

To take the endpoint off the internet, create an interface VPC endpoint (AWS PrivateLink) for the service com.amazonaws.{region}.eks-capabilities and attach its ID to the capability. Three things change at once:

In an enterprise, that VPC is usually a shared-services or network VPC already attached to a Transit Gateway, so on-premises users ride the existing Direct Connect or VPN path:

Corporate networkAWS RegionNetwork VPC (shared services)Private subnets · AZ a + AZ bAWS-managed12345blockedEngineercorporate LANCorporate DNSconditional forwarderCustomerrouterDirect Connector Site-to-Site VPNTransitGatewayResolverinbound endpointInterface endpointeks-capabilitiesArgo CDcapabilityPublic internet
  • 1An engineer's machine asks the corporate DNS for the Argo CD hostname.
  • 2A conditional forwarder sends the query to a Route 53 Resolver inbound endpoint in the network VPC, which answers with the VPC endpoint's private IPs. This step is optional if your resolvers already get that answer.
  • 3The HTTPS connection leaves the corporate network through the customer router, over Direct Connect or Site-to-Site VPN.
  • 4The Transit Gateway routes it to the interface endpoint, whose security group allows port 443 from corporate address ranges.
  • 5PrivateLink carries it to the capability. A request from the public internet is refused.
Figure 3 — Private-only access. The hostname does not change; it resolves to private IPs, reached over the existing hybrid network.

Steps 1 and 2 are the standard way to resolve private AWS names from on premises, not something the capability docs prescribe. Those docs say the hostname resolves to the endpoint's private IPs, and tell you to verify it with nslookup "from any network", which suggests public DNS already returns the private answer. Check what your corporate resolvers return first. If they already get the private IPs, you can skip the forwarder.

content_copycopy
# Subnets in at least two Availability Zones.
# The security group must allow inbound 443 from every client network.
aws ec2 create-vpc-endpoint \
  --region us-east-1 \
  --vpc-endpoint-type Interface \
  --service-name com.amazonaws.us-east-1.eks-capabilities \
  --vpc-id vpc-0a1b2c3d4e5f60718 \
  --subnet-ids subnet-0aa11bb22cc33dd44 subnet-0ee55ff66aa77bb88 \
  --security-group-ids sg-0123abcd4567ef890
# Wait for the endpoint state to be "available" first.
aws eks update-capability \
  --region us-east-1 \
  --cluster-name platform-hub \
  --capability-name argocd \
  --configuration '{
    "argoCd": {
      "networkAccess": { "vpceIds": ["vpce-0123456789abcdef0"] }
    }
  }'
# Status returns to ACTIVE when the update completes.
aws eks describe-capability --region us-east-1 \
  --cluster-name platform-hub --capability-name argocd \
  --query 'capability.status' --output text

# Should return RFC 1918 addresses (10.x, 172.16-31.x, 192.168.x).
nslookup argocd-a1b2c3d4-111122223333.eks-capabilities.us-east-1.amazonaws.com
# An empty list restores public access.
aws eks update-capability \
  --region us-east-1 \
  --cluster-name platform-hub \
  --capability-name argocd \
  --configuration '{ "argoCd": { "networkAccess": { "vpceIds": [] } } }'

A few rules from the docs shape the design:

Several Argo CD instances behind one endpoint

Large organizations rarely stop at one Argo CD. A platform team runs one for shared services, and a regulated business unit runs its own. Each instance is a separate capability on its own cluster, but they do not each need their own VPC endpoint. The endpoint is for the Region-wide eks-capabilities service, not for one capability, and each capability simply lists its ID in vpceIds.

DNS is what keeps the instances apart. Every capability has its own hostname: {capability-name}-{hash}-{account-id} by default, or {prefix}-{account-id} if you set an endpoint prefix, which must be unique in the account and Region. When private access is on, AWS publishes a record for each hostname that points at the private IPs of the endpoint it lists. The docs tell you to verify this with nslookup "from any network", so these are AWS-managed records, not a private hosted zone you create.

AWS RegionNetwork VPCPrivate subnets · AZ a + AZ bAWS-managed1234Engineerin a connected networkAWS-managed DNSone record per instanceShared interface endpoint10.0.1.25 · 10.0.2.31Argo CD · paymentspayments-1111…Argo CD · platformplatform-1111…
  • 1The engineer's machine looks up a hostname, such as payments-111122223333.eks-capabilities.us-east-1.amazonaws.com. AWS-managed DNS returns the shared endpoint's private IPs. The platform hostname returns the same IPs.
  • 2The browser opens a TLS connection to one of those IPs and sends the hostname it looked up in the TLS handshake (SNI) and the Host header.
  • 3A request for the payments hostname reaches the payments Argo CD.
  • 4A request for the platform hostname, through the same IPs, reaches the platform Argo CD.
Figure 4 — One endpoint, many instances. The IPs are shared; the hostname decides which Argo CD answers.

Two caveats. First, the AWS documentation does not state outright that two capabilities can list the same endpoint, or that routing is by hostname. Steps 2 to 4 are how other shared AWS interface endpoints behave, such as private API Gateway, and what the per-instance hostnames imply. Confirm it with two capabilities before you depend on it. Second, the documented multi-endpoint case is the reverse: one capability listing several endpoints, "to enable access from multiple VPCs". Each hostname has one public record, and the docs do not say which endpoint's IPs it returns when it lists endpoints in two VPCs. One central endpoint in a network VPC that every other network routes to sidesteps both questions: one set of IPs, one security group, and any number of Argo CD instances behind it.

warning

No load balancer, Ingress, ACM certificate or Route 53 private hosted zone appears anywhere in this design. If a design review asks where the ALB goes, the answer is that there isn't one. Those pieces only exist for a self-managed Argo CD running in the cluster.

4Identity: Identity Center is the only way in

The capability authenticates people only through IAM Identity Center. The awsIdc block (the Identity Center instance ARN, plus an optional Region if the instance lives in a different Region from the cluster) is required when you create the capability. Local Argo CD users, Dex and custom OIDC or SAML providers are not supported. Corporate identity providers such as Okta, Entra ID or ADFS work only by federating into Identity Center, which most AWS organizations already do.

Authorization has two levels, and both have to say yes:

1234no matchCorporate IdPOkta · Entra IDIAM Identity Centerusers · groupsrbacRoleMappingsby Identity Center IDGlobal roleADMIN · EDITOR · VIEWERAppProject roleteam's ApplicationsNot mappedcannot sign in
  • 1The user authenticates at the corporate identity provider, which federates into IAM Identity Center.
  • 2Identity Center passes the user's ID and group IDs to Argo CD, which looks them up in the capability's rbacRoleMappings.
  • 3A match grants one global role: ADMIN, EDITOR or VIEWER. No match means no access, whatever else is configured.
  • 4AppProject roles, keyed on Identity Center group IDs, decide which Applications the user can see, sync or delete.
Figure 5 — Two gates. A global role lets a user in; a project role lets them see Applications.

The pattern the AWS docs recommend for teams: the platform team maps to ADMIN, team leads to EDITOR, and all developers to VIEWER. ADMIN creates one AppProject per team, and each team lead grants their developers project roles inside it.

content_copycopy
aws eks update-capability \
  --region us-east-1 \
  --cluster-name platform-hub \
  --capability-name argocd \
  --configuration '{
    "argoCd": {
      "rbacRoleMappings": {
        "addOrUpdateRoleMappings": [
          { "role": "ADMIN",
            "identities": [{ "id": "<platform-team-group-id>", "type": "SSO_GROUP" }] },
          { "role": "EDITOR",
            "identities": [{ "id": "<team-leads-group-id>", "type": "SSO_GROUP" }] },
          { "role": "VIEWER",
            "identities": [{ "id": "<all-developers-group-id>", "type": "SSO_GROUP" }] }
        ]
      }
    }
  }'
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: payments
  namespace: argocd
spec:
  sourceNamespaces:            # required by the managed capability
    - argocd
  sourceRepos:
    - https://github.com/acme/payments-deploy
  destinations:
    - server: arn:aws:eks:us-east-1:222233334444:cluster/prod-a
      namespace: 'payments-*'
  roles:
    - name: developer
      policies:
        - p, proj:payments:developer, applications, *, payments/*, allow
        - p, proj:payments:developer, clusters, get, *, allow   # show cluster names in the UI
      groups:
        - <payments-devs-group-id>   # Identity Center group ID

The identity type is SSO_GROUP for groups and SSO_USER for individual users. One more point from the docs: Argo CD permissions are not Kubernetes permissions. Anyone who can create an Application can deploy to any cluster Argo CD can reach, within their project's limits. Keep each project's destinations narrow.

5The CLI and CI/CD without argocd login

The Argo CD CLI works against the managed endpoint, but not through SSO. argocd login is not supported, and neither are argocd admin commands, which need direct pod access. You set three environment variables instead:

export ARGOCD_SERVER=$(aws eks describe-capability \
  --region us-east-1 --cluster-name platform-hub --capability-name argocd \
  --query 'capability.configuration.argoCd.serverUrl' --output text | sed 's|^https://||')
export ARGOCD_AUTH_TOKEN="<token generated in the Argo CD UI>"
export ARGOCD_OPTS="--grpc-web"

argocd app list
argocd app sync payments/checkout-api

6Hub-and-spoke across accounts and Regions

The fleet design is one capability on a hub cluster in a platform account, deploying to workload clusters in other accounts. The capability does not set up this topology, or even the local cluster, for you. You register every target, including the hub itself, and grant access on each one.

Platform account (hub)AWS-managedHub EKS clusterWorkload account AEKS prod-a · private endpointWorkload account B · another RegionEKS prod-b · private endpoint12345authorizesauthorizesGit repositoryfleet manifestsArgo CDcapabilityCapability roleargocd-cap-roleargocd namespacecluster Secrets · ARNsApplicationSetcluster generatorKubernetes APIprod-aAccess Entry+ access policyKubernetes APIprod-bAccess Entry+ access policy
  • 1Argo CD pulls the fleet manifests from Git.
  • 2It reads the cluster Secrets in the hub's argocd namespace. Each one names a target by its EKS cluster ARN, and ApplicationSets generate one Application per cluster from them.
  • 3It acts as the capability's IAM role in the platform account.
  • 4It calls the prod-a Kubernetes API in account A. The Access Entry for that role, with its access policy, decides what it may change.
  • 5It does the same for prod-b in account B, in another Region, through the same process.
Figure 6 — Hub-and-spoke. One IAM role, one Access Entry per target cluster, and no network path to build between accounts.

Compared with a self-managed hub, three parts of the usual design disappear:

content_copycopy
# Run in the workload account, against the target cluster.
aws eks create-access-entry \
  --region us-east-1 \
  --cluster-name prod-a \
  --principal-arn arn:aws:iam::111122223333:role/ArgoCDCapabilityRole \
  --type STANDARD

# Write access only where this cluster's apps live.
aws eks associate-access-policy \
  --region us-east-1 \
  --cluster-name prod-a \
  --principal-arn arn:aws:iam::111122223333:role/ArgoCDCapabilityRole \
  --policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSEditPolicy \
  --access-scope type=namespace,namespaces=payments-prod
# Argo CD must READ every resource type cluster-wide for health checks
# and drift detection, even if it only WRITES to a few namespaces.
aws eks associate-access-policy \
  --region us-east-1 \
  --cluster-name prod-a \
  --principal-arn arn:aws:iam::111122223333:role/ArgoCDCapabilityRole \
  --policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSAdminViewPolicy \
  --access-scope type=cluster
# Committed to Git, or applied to the hub cluster.
apiVersion: v1
kind: Secret
metadata:
  name: prod-a
  namespace: argocd
  labels:
    argocd.argoproj.io/secret-type: cluster
    env: prod                      # ApplicationSet cluster generators select on labels
stringData:
  name: prod-a
  server: arn:aws:eks:us-east-1:222233334444:cluster/prod-a   # the ARN, never the API URL
  project: payments

7Managed or self-managed

Almost every access decision above is simpler than its self-managed equivalent. The price is a list of things you cannot change.

ConcernEKS Capability for Argo CDSelf-managed (Helm on EKS)
How people reach the UIAWS-hosted endpoint; private through an eks-capabilities VPC endpointYou build it: Ingress, ALB or NLB, certificate, DNS
Network filterEndpoint security group only (no endpoint policies)Security groups, WAF, internal load balancer, anything you put in front
Sign-inIAM Identity Center only; other IdPs through federationDex, any OIDC provider, local users
AuthorizationThree fixed global roles, AppProject roles, 1,000 identitiesFull argocd-rbac-cm policy language
CLITokens over gRPC-web; no argocd login or argocd adminEverything, including SSO login
Reaching spoke clustersEKS Access Entries; AWS handles private connectivityIRSA or Pod Identity, cross-account roles, a network path to each API server
TargetsEKS clusters, by ARNAny conformant Kubernetes cluster
ExtensibilityNo Config Management Plugins, notifications controller or UI extensions; sync timeout fixed at 120 secondsAll upstream features
OperationsAWS installs, upgrades and scales itYou patch, scale, shard and keep it highly available

Choose the managed capability when every target is EKS, Identity Center is already how people get into AWS, and nobody depends on notifications, plugins or custom RBAC rules. Stay self-managed when you need non-EKS targets, a sign-in path that cannot go through Identity Center, Argo CD notifications, Config Management Plugins, or fine-grained global policies. Moving across is supported. AWS documents a migration path: scale the old controllers to zero, export Applications, ApplicationSets and AppProjects, and rewrite destination.server to cluster ARNs.

8What the documentation does not answer yet

The capability launched in late 2025, and some questions an enterprise review will ask are not covered in the AWS documentation used here. Test these yourself before you commit:

Conclusion

Access to a managed Argo CD is a short list of decisions, none of which involve Kubernetes networking. Decide whether the endpoint stays public or goes behind an eks-capabilities VPC endpoint in a network VPC that your hybrid links already reach. Decide which Identity Center groups get ADMIN, EDITOR and VIEWER, and give each team an AppProject with narrow destinations. Give pipelines project tokens and a runner that can reach the endpoint. Then grant one Access Entry per target cluster, with read access everywhere and write access only where that team deploys. The load balancers, certificates, Dex configuration and cross-account trust chains that a self-managed hub needs are simply gone. What remains is what you cannot customize, and that list decides whether the managed capability fits.

References