GCP ACE: Fleets and Teams — Scoping Access Across Clusters
The previous article covered what a Fleet is and the sameness model behind it. This one covers the piece that makes a Fleet usable by more than one team at once: team scopes.
The problem team scopes solve
A Fleet groups many clusters under one fleet host project. But “one project with all the clusters” isn’t automatically safe to hand to every team — the payments team shouldn’t be able to see or touch the clusters and namespaces that belong to the mobile team, even though both sit in the same fleet.
A team scope is a fleet-level grouping that gives one team access to a defined slice of the fleet: a set of member clusters, and the namespaces within them — without granting access to the rest.
Fleet namespaces: one name, many clusters
The core object inside a team scope is a fleet namespace. A fleet namespace is a single logical name that, when created, provisions a matching Kubernetes namespace in every cluster bound to that scope. If a Kubernetes namespace with that name already exists in one of those clusters, it gets onboarded into the fleet namespace instead of duplicated.
This is the same “namespace sameness” concept from the Fleets introduction article, applied deliberately: instead of sameness happening by accident because two clusters happened to use the same namespace name, a team scope is how you intentionally create one namespace that spans a defined group of clusters.
One cluster can belong to multiple team scopes within the same fleet host project — a cluster shared by two teams isn’t a contradiction.
Setting it up
Requires gcloud CLI version 419.0.0 or later.
1. Create the team scope:
gcloud container fleet scopes create SCOPE_NAME
2. Bind an already-registered fleet cluster to it:
gcloud container fleet memberships bindings create BINDING_NAME \
--membership MEMBERSHIP_NAME \
--scope SCOPE_NAME \
--location MEMBERSHIP_LOCATION
--location defaults to global if omitted.
3. Create the fleet namespace inside the scope:
gcloud container fleet scopes namespaces create NAMESPACE_NAME --scope=SCOPE_NAME
Names must follow the fleet namespace naming restrictions, so reserved system names such as kube-system are off limits. Fleet namespaces can’t be renamed later: you delete one and create a new one.
Deleting behaves differently depending on how the namespace got there. If the fleet namespace created the Kubernetes namespaces, deleting it deletes them in every cluster, workloads included. If it onboarded an existing namespace, that original namespace is left in place.
4. Grant the team access:
gcloud beta container fleet scopes add-app-operator-binding SCOPE_ID \
--role=ROLE --group=TEAM_EMAIL --project PROJECT_ID
ROLE is one of admin, edit, or view. Swap --group for --user=USER_EMAIL to grant an individual instead of a whole group (Google recommends groups). For a custom ClusterRole, first add it to the fleet’s rbacrolebindingactuation feature, then pass --custom-role=CUSTOM_ROLE instead of --role.
gcloud beta container fleet scopes list-app-operator-bindings SCOPE_NAME # who has access, and with which persona
Who can configure this, and what teams actually get
Creating and configuring scopes and namespaces at the fleet level requires roles/gkehub.admin on the fleet host project (project owners get this implicitly). Below that, four distinct personas exist, each combining a fleet-level role, a project-level role, a logging role, and an in-cluster Kubernetes role:
| Persona | Team scope role | Fleet host project role | Log access | In-cluster Kubernetes role |
|---|---|---|---|---|
| Scope Admin | roles/gkehub.scopeAdmin | roles/gkehub.scopeEditorProjectLevel | roles/logging.viewAccessor | admin |
| Scope Editor | roles/gkehub.scopeEditor | roles/gkehub.scopeEditorProjectLevel | roles/logging.viewAccessor | edit |
| Scope Viewer | roles/gkehub.scopeViewer | roles/gkehub.scopeViewerProjectLevel | roles/logging.viewAccessor | view |
| Custom Role | roles/gkehub.scopeViewer | roles/gkehub.scopeEditorProjectLevel | roles/logging.viewAccessor | A ClusterRole you define |
Notice something deliberate here: this is not plain Kubernetes RBAC applied cluster by cluster. It’s IAM-managed, at the fleet level, and it’s what actually generates the per-cluster Kubernetes role underneath. A Scope Editor automatically gets edit inside every cluster bound to their scope — you don’t separately write a RoleBinding per cluster.
Once a team member has one of these roles, they fetch credentials for a fleet member cluster:
gcloud container fleet memberships get-credentials MEMBERSHIP_NAME
This writes a kubeconfig entry that goes through the Connect gateway, a Google-hosted endpoint in front of every fleet member’s API server. Team members don’t need direct network access to the underlying clusters either; the fleet-host-project role in the table above is what grants Connect gateway access.
Exam Takeaways
- A team scope grants a team access to a defined subset of fleet clusters and namespaces — not the whole fleet.
- A fleet namespace is one name that creates (or onboards) a matching Kubernetes namespace in every cluster bound to that scope — deliberate namespace sameness, not accidental.
- A cluster can belong to multiple team scopes simultaneously.
- Deleting a fleet namespace deletes the Kubernetes namespaces (and workloads) it created, but leaves an onboarded, pre-existing namespace in place.
- Configuring scopes/namespaces at the fleet level needs
roles/gkehub.adminon the fleet host project. - Four built-in personas (Scope Admin/Editor/Viewer, or a Custom Role) combine a fleet-level IAM role with an automatically-applied in-cluster Kubernetes role — you don’t hand-write per-cluster RoleBindings for team access.
- Team members still reach clusters via
gcloud container fleet memberships get-credentials, through the Connect gateway — same mechanism as regular fleet access, scoped down by their team scope’s permissions.