Cluster
Cluster is an order for one EKS cluster. Applying it in a team namespace vends a cluster into a workload account: the Composition renders provider-opentofu Workspaces over the landing-zone substrate, and the cluster's endpoint, CA, OIDC issuer and network identifiers are written back to status as they converge. The substrate stays the source of truth — this is the ordering API on top of it.
The order. Placement (which account and region), the cluster's shape (version, networking, node pools), and the cross-account vend credentials, mirroring the fleet/aws/cluster-stack entrypoint's inputs field-for-field.
What kubectl get shows
| Column | Type | Meaning |
|---|---|---|
REGION | string | Region the cluster was vended into. |
ACCOUNT | string | Workload account the cluster was vended into. |
ENDPOINT | string | EKS API endpoint — empty until the vend converges. |
Spec
accountstringrequiredWorkload AWS account id. Records the placement and feeds resource tags. It does not select the account — spec.vendRoleArn does, being the role the entrypoint's provider assumes — so the two must agree, and admission requires this id to appear in that ARN. Leave vendRoleArn empty for a same-account vend, where the hub's own credentials are used and this field stands alone.
clusterNamestringrequiredCluster base name; the module prefixes it with environment to form the EKS cluster name <environment>-<clusterName>. Required and unique per (account, region, environment) — there is no default, because a shared default collides the moment a second cluster is vended into one account and environment. Must not equal the environment token. Capped at 12 characters so the derived <environment>-<clusterName> keeps the tightest cluster-scoped S3 name (agent-iam's account+region-qualified model-artifacts bucket) within S3's 63-char limit for a 9-character region token like us-east-1.
regionstringrequiredAWS region the cluster is vended into. Any region is valid — the tofu state backend is decoupled from it: state lives in the single hub-account bucket named by spec.stateBucket, in spec.stateRegion, never in a per-region bucket that would have to track this value. So a vend into us-west-1 builds the cluster there but still stores its state in the one hub bucket. A region-lock SCP on the target account's OU can deny actions outside a permitted set, in which case the region named here must be one that SCP permits.
stateBucketstringrequiredS3 bucket holding every vended cluster's tofu state. One bucket in the hub account for the whole fleet, provisioned by landing-zone's fleet-hub component alongside the hub IRSA role — not by a vend, so it must exist before the first Cluster is applied. The per-cluster state key is derived (fleet/<namespace>/<name>/terraform.tfstate), so this names only the bucket. Required and deliberately undefaulted: an S3 bucket name is a global-namespace identifier belonging to one account, so no value here could be correct for a second deployment. Declare the hub's bucket rather than inheriting someone else's.
stateRegionstringrequiredRegion the stateBucket lives in. This is the bucket's own region, deliberately NOT spec.region — the S3 backend validates the bucket against it, and decoupling the two lets a vend into any region still init against the one real state bucket. Required alongside stateBucket because the two name one thing: where this fleet's state lives. A region that merely looks plausible is worse than no value — it inits against the wrong place instead of failing.
teamstringrequiredOwning team (tagging + ownership).
bootstrapAccessRoleArnstringdefault""For cross-account vends, the hub's Crossplane role ARN — granted cluster-admin via an EKS access entry so the bootstrap Workspace's ambient get-token can reach this cluster's API (get-token can't present the fleet-vend external_id). Empty = same-account, where the creator (the hub) is already admin.
clusterPermissionsBoundaryArnstringdefault""Permissions-boundary ARN cluster-stack mints the cluster's IAM roles with — the boundary of whichever fleet role runs the vend, published to SSM by landing-zone: fleet-vend's /eks-fleet/<env>/fleet-vend/vend_permissions_boundary_arn for cross-account (set alongside vendRoleArn), fleet-hub's /eks-fleet/<env>/fleet-hub/hub_permissions_boundary_arn for a same-account hub vend. Enforcement lives in IAM, not here: the fleet roles' CreateRole/AttachRolePolicy gate only allows role writes carrying their exact boundary, so a weaker or missing value can't mint a weaker role — the vend just fails. Empty = ungated (e.g. the local kind hub on user credentials).
clusterVersionstringdefault"1.36"Kubernetes version.
dataKmsKeyArnstringdefault""The spoke's secrets-component data CMK ARN. The bootstrap Workspace's agent-iam module encrypts the model-artifacts + eval-reports buckets at rest with it (SSE-KMS). A spoke prerequisite referenced by ARN — like vendRoleArn and the permissions boundaries — not something the vend creates: cluster-stack provisions only network + cluster (no secrets module), so the order desk supplies the key the spoke's baseline already published to SSM (/platform/<env>/secrets/kms-key-arn). Empty = ungated local/hub validation, where no real artifacts bucket is minted.
enableAgentPlatformbooleandefaulttrueInstall the eks-agent-platform operator via GitOps: when true, the bootstrap labels the in-cluster ArgoCD Secret so eks-gitops reconciles the operator from the per-cluster OIDC/role annotations. False installs the operator out of band (e.g. the e2e harness, to avoid GitOps racing a locally-built image). Either way the cluster is still bootstrapped (Cilium + ArgoCD + the addon catalog): this flag only toggles the operator's GitOps label on the in-cluster ArgoCD Secret — not whether the bootstrap Workspace runs (it always renders once the cluster is Ready).
endpointPublicAccessbooleandefaultfalseExpose the EKS API on a public endpoint. Clusters vend private by default; public access is explicit opt-in and must carry a non-empty endpointPublicAccessCidrs allowlist (a CEL rule on spec rejects public-with-empty at admission).
endpointPublicAccessCidrs[]stringdefault[]CIDRs allowed to reach the public API endpoint. Required (non-empty) when endpointPublicAccess is true; irrelevant while the endpoint is private.
environmentstringdefault"development"Environment tier (development | staging | production) — prefixes resource names + tags.
gitopsRepoBranchstringdefault"main"Branch of the addon-catalog GitOps repo the spoke tracks. Defaults to main.
gitopsRepoUrlstringdefault"https://github.com/nanohype/eks-gitops.git"Git URL of the addon-catalog GitOps repo the spoke's app-of-apps points at — this org's eks-gitops (or a fork of it). Defaults to the org catalog; override only to vend against a fork. Required non-empty and validated as a git URL by the substrate, so it is always sent (unlike tenantsRepoUrl, which is optional).
moduleSourcestringdefault"git::https://github.com/nanohype/landing-zone.git?ref=1b5714dd25b9d12d53aafafaee4c2cc851fa178e"Git source for the landing-zone repo the Workspace fetches; tofu runs in the fleet/aws/cluster-stack subdir (set by the composition's entrypoint), which needs the whole repo present. Must be https and pinned to a full 40-character commit sha: the Workspace pod holds no SSH key, and provider-opentofu caches a module by its ref and never re-pulls, so a branch or tag resolves once and every later vend reuses that copy — two clusters ordered from the same spec would run different substrate. Point this at your own fork by changing the host; what is constrained is the shape of the source, not who publishes it. See docs/architecture.md.
networkobjectdefault{"mode":"create","create":{"vpcCidr":"10.0.0.0/16","maxAzs":3,"natGateways":1}}The VPC the cluster lands in. mode: create (default) provisions a self-owned VPC first; mode: adopt participates in a VPC provisioned elsewhere (a same-account shared VPC, or one shared cross-account via AWS RAM), referencing it by ID. Populate the sub-object matching mode.
adoptobjectdefault{"vpcId":"","subnetIds":{"private":[],"public":[]}}Adopt-mode inputs — used when mode is adopt.
2 fields under
adoptsubnetIdsobjectdefault{"private":[],"public":[]}Subnet IDs in the adopted VPC.
2 fields under
subnetIdsprivate[]stringdefault[]Private subnet IDs (required, non-empty, when mode is adopt).
public[]stringdefault[]Public subnet IDs (empty is valid for a private-only cluster).
vpcIdstringdefault""VPC ID to adopt. Required (non-empty) when mode is adopt.
createobjectdefault{"vpcCidr":"10.0.0.0/16","maxAzs":3,"natGateways":1}Create-mode inputs — used when mode is create.
7 fields under
createcentralizedEgressbooleandefaultfalseRoute private egress through the transit gateway instead of a local NAT gateway (zero NAT gateways). Requires transitGatewayId.
ipamNetmaskLengthintegerdefault0Netmask length of the CIDR to allocate from ipamPoolId (16-20; subnets are carved 8 bits smaller than the VPC block, so a base longer than /20 would drop below AWS's /28 minimum). 0 (default) = literal allocation.
ipamPoolIdstringdefault""IPAM pool the VPC CIDR is drawn from. Empty (default) = literal allocation from vpcCidr. Cross-account this is the org IPAM env sub-pool shared in over RAM.
maxAzsintegerdefault3natGatewaysintegerdefault1transitGatewayIdstringdefault""Transit gateway the VPC attaches to. Empty (default) = local NAT egress only. Requires an IPAM-allocated CIDR (set ipamPoolId), so attached prefixes stay non-overlapping.
vpcCidrstringdefault"10.0.0.0/16"CIDR for the VPC (literal allocation). Mutually exclusive with ipamPoolId.
modestringdefault"create"createadoptcreate — the stack owns a disposable VPC (subnets, endpoints, egress, and the ELB role tags). adopt — the stack participates in a VPC it does not own; the owner runs the VPC and the subnet tagging, and the cluster references it by adopt.* IDs.
observabilityTierstringdefault"floor"floorfullWhich observability substrate this cluster runs. floor is CloudWatch alone: the amazon-cloudwatch-observability addon publishes Container Insights metrics, and the cluster's OpenTelemetry gateway exports application metrics as CloudWatch EMF and logs to CloudWatch Logs. full adds the in-cluster LGTM stack (Loki, Tempo, kube-state-metrics, grafana-operator) and Amazon Managed Prometheus / Grafana, which also requires the managed-monitoring substrate to exist for this cluster. Published as the observability/tier cluster-Secret label the eks-gitops generators select on. Both tiers run the same OpenTelemetry node agent and a gateway on the same telemetry.monitoring.svc endpoint, so a tenant workload is identical either way and only the destinations differ. Defaults to floor — a vended cluster is born light, and opting up is a deliberate act with a substrate prerequisite.
operatorPermissionsBoundaryArnstringdefault""Permissions-boundary ARN the bootstrap Workspace mints the agent-platform operator role with. Same value + SSM source as clusterPermissionsBoundaryArn (one fleet role runs both Workspaces, and its IAM gate covers both); a separate field because it feeds the cluster-bootstrap entrypoint's operator_permissions_boundary_arn var. Empty = ungated.
portalAccessRoleArnstringdefault""The portal worker's per-account spoke role ARN — granted a read EKS access entry so portal reaches this cluster's API (mint tokens, watch tenants) with the same role it uses for eks:DescribeCluster. Empty = portal not wired for this cluster.
systemNodesobjectdefault{"instanceTypes":["m7g.xlarge","m6g.xlarge"],"minSize":2,"maxSize":6,"desiredSize":2,"diskSize":100}System node group (Bottlerocket / Graviton arm64).
desiredSizeintegerdefault2diskSizeintegerdefault100instanceTypes[]stringdefault["m7g.xlarge","m6g.xlarge"]maxSizeintegerdefault6minSizeintegerdefault2
tenantsRepoUrlstringdefault""SSH URL of the private tenants GitOps repo. When set, the bootstrap registers a read-only deploy key + ArgoCD repo credential so ArgoCD can pull portal-committed tenant manifests.
ttlDaysintegerdefault0Days-to-live for an ephemeral spoke. 0 (default) = persistent: tagged Lifecycle=persistent and never auto-reaped. >0 tags the spoke Lifecycle=ephemeral + Expiry=<vend date + ttlDays> and the hub reaper deletes this Cluster (a clean Crossplane teardown) ttlDays after creation.
vendRoleArnstringdefault""Cross-account role the entrypoint's provider assumes to provision into a workload account. Empty = provision with the hub's own credentials (same-account — e.g. the local kind-hub validation).