soft guideINFRASTRUCTURE & ADVISORY

Note 01

Terraform to Flux through one ConfigMap

The GitOps Bridge pattern for Flux: Terraform publishes the AWS facts once, every manifest substitutes them, and no manifest names an account, a region or an ARN.

The gap between the two tools

Add-ons on EKS need facts only the infrastructure code knows: the Karpenter interruption queue, the role ARN for the load balancer controller, the VPC ID, the KMS key for node volumes. Terraform creates those things; Flux installs the charts that need them. Copying the values into Git by hand drifts on the next apply. Letting Terraform install the charts with the Helm provider makes Terraform own state that Flux then changes under it.

The GitOps Bridge project names the pattern that closes this gap: the infrastructure tool creates the cloud resources and stores their metadata where the GitOps engine can read it; the GitOps engine installs everything and passes the metadata to the charts. Its reference implementation is for Argo CD: Terraform writes the cluster secret, add-on switches become labels, values become annotations, and ApplicationSets select clusters by label and template values from the annotations. The Flux side of the project is still in progress, so this is how the platform does it for Flux.

One ConfigMap, written once

Terraform writes a single ConfigMap, cell-metadata, into flux-system. It holds identifiers only, never a secret value:

resource "kubernetes_config_map_v1" "cell_metadata" {
  metadata {
    name      = "cell-metadata"
    namespace = "flux-system"
  }
  data = {
    cell_name                    = var.name
    aws_region                   = local.region
    cluster_name                 = module.eks.cluster_name
    karpenter_interruption_queue = module.karpenter.queue_name
    karpenter_node_role          = module.karpenter.node_iam_role_name
    app_role_arn                 = aws_iam_role.app_abac.arn
    admission_action             = var.environment == "dev" ? "Audit" : "Deny"
  }
}

Every Flux Kustomization substitutes from it after building, so a manifest simply writes ${cluster_name}:

apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
  name: controllers
  namespace: flux-system
spec:
  path: ./infrastructure/controllers
  postBuild:
    substituteFrom:
      - { kind: ConfigMap, name: cell-metadata }

What differs from the Argo CD bridge

Rules that keep it honest

The result is one GitOps tree for every cell. A new region is a new Terraform stack and a new directory that lists what the cell runs; no manifest is copied.

Start a conversation

Bring your infrastructure
into focus.

Tell me what you are building, operating, or trying to improve.

Let’s talk