Home Blog GCP Architecture Series #27 — Config Connector: Kubernetes as the Control Plane…
GCP Architecture GCP Architecture Series

GCP Architecture Series #27 — Config Connector: Kubernetes as the Control Plane

Terraform describes infrastructure and applies the description when you ask. Config Connector makes the description live in a cluster and keeps it true continuously — which is a genuinely different model, not a different syntax. The consequences that matter are the ones nobody reads about first: what a delete does, and what happens to a change somebody made in the console.

Verified against current vendor documentation on 9 September 2026. Pricing, limits and API behaviour were checked against the official docs on that date. Cloud services change fast — if you are reading this much later, treat the specifics as a starting point and re-check the linked sources.

Business Challenge

Every one of these follows from a single design choice: the Kubernetes object is not a description of the resource, it is the resource's controller.

1
"We cleaned up the namespace and lost the database"

That is the documented default. When you delete a Config Connector resource from the cluster, the associated Google Cloud resource is deleted by default. Not orphaned, not left behind — deleted. Which means kubectl delete namespace, an operation whose entire cultural history is that it destroys cluster state, now reaches production infrastructure.

Correct approach

Set cnrm.cloud.google.com/deletion-policy: abandon on anything stateful before it ever reaches a cluster, and treat namespace deletion as a change with a blast radius rather than a cleanup.

2
"Someone changed it in the console and it changed back"

Working as designed. Config Connector reconciles continuously, so a console edit is drift and drift gets corrected. This is the behaviour Terraform users expect only at apply time, arriving on its own schedule instead.

Correct approach

Decide deliberately whether you want that. Where you do not, cnrm.cloud.google.com/reconcile-interval-in-seconds set to zero halts drift correction while still applying changes you make to the spec.

3
"I changed the field and got UpdateFailed"

Some fields cannot be changed on a live resource, and Config Connector will not work around it: resources are not recreated when modifying immutable fields. Terraform would plan a destroy-and-recreate; this stops and tells you.

Correct approach

Abandon the resource, change the field, and re-acquire it. That is the documented route, and it is why the abandon annotation is an everyday tool here rather than an emergency one.

4
"The cluster can create anything in the project"

It can, if you installed it that way. In cluster mode one identity serves everything; namespaced mode lets you divide permissions based on the respective concerns of different IAM service accounts, with a service account bound per namespace.

Correct approach

Choose namespaced mode unless you have a reason not to. It is the difference between one credential for the whole estate and one per team.

Architecture

Config Connector is an open source Kubernetes add-on that lets you manage Google Cloud resources through Kubernetes. A Cloud SQL instance becomes a custom resource; a controller in the cluster makes the API calls; the object's status reports what happened.

Diagram: how Config Connector turns Google Cloud resources into Kubernetes objects, why deleting the object deletes the resource, the abandon annotation, continuous drift correction, and the per-namespace identity model
The object is not a description of the resource. It is the thing that owns it.

The difference from the previous two posts

Terraform, self-managed or via Infrastructure Manager, is a batch process: it reads a description, compares it to a state file, and converges once, when invoked. Config Connector is a control loop. The desired state lives in the cluster's own database, and a controller reconciles it against the Google Cloud APIs asynchronously, on its own schedule, forever.

Almost everything surprising follows from that. There is no plan step to read before it acts. There is no state file, because etcd is the state. And there is no moment when reconciliation is finished, because it never is.

Terraform (#25, #26)Config Connector
When it convergesWhen you run apply.Continuously.
StateA file in a bucket.The cluster.
Console changeReported as drift at next plan.Corrected without asking.
DeleteExplicit, from a plan you read.Whatever deletes the object.
IdentityYour credentials, or a deploy service account.A service account per namespace.

Delete is the one to internalise

The associated Google Cloud resource is deleted by default. The annotation that changes it is one line:

metadata:
  annotations:
    cnrm.cloud.google.com/deletion-policy: abandon

Compare the equivalent in post #26. Infrastructure Manager also lets you delete the deployment and keep the resources, but there the default is a considered choice at delete time and the abandoned resources become invisible to the tool. Here the default runs the other way: the resource goes unless you said otherwise, in advance, in a file.

The blast radius now includes things Kubernetes never used to touch

Every safeguard a team has around kubectl delete was built when the worst outcome was a restarted pod. Namespace deletion is routine in most clusters — it is how you clean up a failed install, retire an environment, or reset a test. With Config Connector in namespaced mode, the namespace holds the objects that own your databases, buckets and networks. The habit and the consequence were formed in different worlds, and the annotation is the only thing between them.

Identity: the same question, a third time

Post #25 asked who can read the state bucket. Post #26 asked who can act as the deployment service account. Here the question is who can create objects in a namespace — because in namespaced mode each namespace is bound to its own IAM service account, and a dedicated controller pod impersonates it.

That is a genuinely good model. It maps cloud permissions onto Kubernetes RBAC, which teams already operate, and it makes the boundary a namespace rather than a credential file. The catch is the translation: a developer with create on a namespace has, transitively, whatever that namespace's service account can do in Google Cloud. RBAC that was scoped to "can deploy workloads" now also means "can provision infrastructure", and nothing in the RBAC binding says so.

Why This Architecture Holds Up

Drift correction is the feature and the hazard

A console change being reverted is exactly what you want for a security control and exactly what you do not want during an incident. At 3am somebody widens a firewall rule to restore service; the controller closes it again on its own schedule, and the person who made the change has no reason to suspect the cluster.

The lever is per-resource: cnrm.cloud.google.com/reconcile-interval-in-seconds, and setting it to zero halts drift correction while still applying changes made to the spec. Worth knowing before the incident rather than discovering it during one.

Two things reconciliation cannot see

Immutable fields. Resources are not recreated when modifying immutable fields — the controller raises UpdateFailed and stops, where Terraform would plan a replacement. Safer, and it means abandon-and-reacquire is a routine procedure rather than a last resort. Unreadable fields. Mutable but unreadable fields are updated only when the custom resource is modified, because the controller cannot read them back to compare. So for that subset, drift is invisible and uncorrected — the guarantee is narrower than "the cluster is the truth" suggests.

Where this model earns its complexity

  • You already run GKE and mean it. The cluster becomes a dependency of your infrastructure, not just a consumer of it. If the cluster is not treated as production, neither is your infrastructure.
  • You want application and infrastructure in one manifest. A team shipping a service plus its bucket and database in one kubectl apply is the case this was built for.
  • You want drift closed, not reported. Nothing in the Terraform posts does this. If unauthorised console changes are the problem you actually have, this is the tool that solves it.

Where it does not

  • The estate is larger than the cluster's remit. Organization policies, folders, billing — the things posts #19 to #22 covered — sit oddly inside a workload cluster.
  • You need a plan to review. There is no preview step here of the kind post #26 described. Approval has to happen in Git, before the object reaches the cluster, which makes the pull request the only gate.
  • Your Kubernetes RBAC is not tight. The model inherits your cluster's access control. If that is loose, this makes it loose over your infrastructure too.

Key Architecture Decisions

DecisionChoose thisBecause
Stateful resources deletion-policy: abandon, from the start The associated Google Cloud resource is deleted by default.
Namespace deletion Treat as a production change The namespace holds the objects that own the infrastructure.
Installation mode Namespaced, unless you have a reason It divides permissions across different IAM service accounts.
Cluster RBAC Re-audit it as infrastructure access Create in a namespace transitively means provision in Google Cloud.
Incident changes Know the reconcile-interval annotation first Setting it to zero halts drift correction while spec updates still apply.
Immutable field changes Abandon, edit, re-acquire Resources are not recreated when modifying immutable fields.
Trusting drift correction Not for unreadable fields Those update only when the custom resource is modified.
Change approval In the pull request There is no plan step between apply and action.
Organization-level resources Keep them in Terraform A workload cluster is the wrong owner for the estate's scaffolding.
Cluster criticality Rate it as production infrastructure Your infrastructure's control loop now lives in it.

Closing Thought

Three posts, three tools, and the same question underneath each: what has the authority to change your infrastructure, and who has the authority to use it. A state bucket, a deployment service account, a Kubernetes namespace. The mechanism changes completely and the question does not move at all.

What is distinctive here is how far the authority travels from anything that looks like it. In post #25 the danger lived in a bucket, which at least looks like storage worth protecting. In #26 it lived in an IAM binding, which looks like nothing. Here it lives in a habit — the ordinary, unremarkable act of deleting a namespace, learned in an environment where the worst case was a rescheduled pod. Config Connector is a good model, and the drift correction is something neither Terraform post can offer. But it asks a team to unlearn something, and unlearning is the slowest kind of change there is.

Next in this series

#28 goes underneath all three: gcloud, the REST API, and what the console quietly does for you — the calls a click actually makes, and why a console-built resource is so hard to reproduce.

Comments

How was your experience?
Your feedback helps improve this site.
PoorExcellent