Migrate from Policy Controller

This document explains how to migrate your admission policies from Policy Controller to reduce the risk of gaps in policy enforcement. Policy Controller is deprecated as of November 12, 2026, and reaches end of support on December 31, 2027. In version 1.39 and later, Google Kubernetes Engine (GKE) clusters don't support Policy Controller.

You can migrate the admission policies that you enforce by using Policy Controller to Kubernetes ValidatingAdmissionPolicies or to self-managed Open Policy Agent (OPA) Gatekeeper. Migrating doesn't affect workloads that are already running. Policy Controller evaluates requests to create or update resources, so removing it doesn't restart or change running workloads.

This document is for operators and security specialists who manage Policy Controller across a fleet. To learn more about common roles and example tasks that we reference in Google Cloud content, see Common GKE user roles and tasks.

Blocked upgrades to GKE version 1.39

GKE blocks upgrades to version 1.39 and later for clusters that have Policy Controller installed. Before you upgrade a cluster to version 1.39, migrate your policies and remove Policy Controller from that cluster. Don't remove Policy Controller just to unblock the upgrade before you migrate your policies, because doing so leaves the cluster without policy enforcement.

Choose a replacement

Evaluate each admission policy in Policy Controller and choose a replacement option for that policy from the following table. You don't have to choose one replacement for an entire cluster. You can combine options on the same cluster based on what each policy requires:

Comparison of Policy Controller replacement options
Option When to use Operation and support
ValidatingAdmissionPolicy (recommended) Policies that check a single resource when it's created or updated. Most templates in the Policy Controller template library work this way. ValidatingAdmissionPolicies are built into Kubernetes and available in GKE version 1.30 and later at no additional cost. Cloud Customer Care supports ValidatingAdmissionPolicies as part of GKE.
Pod Security Admission (recommended for Pod security bundles) Predefined Pod security controls from the pss-baseline-v2022, pss-restricted-v2022, or psp-v2022 bundle. For more information, see Use Pod Security Admission for Pod security bundles. Pod Security Admission is built into Kubernetes and enabled by default in GKE at no additional cost. Customer Care supports Pod Security Admission as part of GKE.
Self-managed OPA Gatekeeper Policies that ValidatingAdmissionPolicies can't express, such as referential policies that read other resources in the cluster, policies that you want to keep in Rego, and audits of existing resources. You can also add Common Expression Language (CEL) rules to Gatekeeper ConstraintTemplates so that Gatekeeper generates ValidatingAdmissionPolicies for API server enforcement, and its audit controller checks existing resources. You install, upgrade, and operate Gatekeeper. Customer Care doesn't support open source Gatekeeper. The Gatekeeper community provides support through GitHub.

Inventory and classify your policies

By default, the Policy Controller template library installs every template, and policy bundles install their constraints with the dryrun enforcement action. As a result, the templates and constraints on a cluster don't necessarily match the policies that you enforce. To find out what you enforce, follow these steps:

  1. Identify the clusters in your fleet that run Policy Controller:

    gcloud container fleet policycontroller describe \
        --project=PROJECT_ID
    

    Replace PROJECT_ID with the ID of your fleet host project.

  2. On each cluster that you identified, list your constraints:

    kubectl get constraints \
        -o custom-columns='KIND:.kind,NAME:.metadata.name,'\
    'ACTION:.spec.enforcementAction,VIOLATIONS:.status.totalViolations,'\
    'BUNDLE:.metadata.labels.policycontroller\.gke\.io/bundleName'
    

    The output shows each constraint's enforcement action, its violation count from the most recent audit, and any bundle that installed it. A constraint that has no value in the ACTION column uses the default deny enforcement action.

  3. Back up your ConstraintTemplates and constraints to separate files:

    kubectl get constrainttemplates -o yaml \
        > policy-controller-templates-backup.yaml
    kubectl get constraints -o yaml \
        > policy-controller-constraints-backup.yaml
    
  4. Optional: Perform the following additional backup operations:

    • If you enabled referential rules in Policy Controller, then back up your sync configuration:

      kubectl get config.config.gatekeeper.sh -n gatekeeper-system -o yaml \
          > policy-controller-config-backup.yaml
      
    • If you enabled mutation in Policy Controller, then back up your mutators:

      kubectl get assign,assignmetadata,assignimage,modifyset -o yaml \
          > policy-controller-mutators-backup.yaml
      

Keep these backup files in a safe place. Removing Policy Controller deletes every ConstraintTemplate, constraint, sync configuration, and mutator on the cluster, including the ones that you created. You need the backups to translate or re-create your policies.

After you back up your policies, determine your migration path for each constraint by checking the constraint type in the following table:

Migration paths by constraint type
Constraint type Migration path
Single-resource constraint If a constraint evaluates fields on only the incoming resource without reading data.inventory or belonging to a Pod security bundle, then use a ValidatingAdmissionPolicy.
Unenforced dryrun constraint If a constraint uses the dryrun enforcement action and you don't plan to enforce it, delete the constraint.
Pod security bundle constraint If a constraint comes from the pss-baseline-v2022, pss-restricted-v2022, or psp-v2022 bundle, then use Pod Security Admission. Use ValidatingAdmissionPolicies for any controls that Pod Security Admission doesn't cover.
Referential constraint If a constraint uses a referential template that reads other resources in the cluster, then use self-managed Gatekeeper. To find referential templates, search your backup file for the data.inventory reference.
Cloud Service Mesh, Istio, or Google Cloud constraint Review the constraint individually to check whether a built-in GKE feature replaces the policy, or whether the ConstraintTemplate in your backup file reads data.inventory. Most of these templates check only the incoming resource and work in ValidatingAdmissionPolicies, except the referential AsmAuthzPolicyDefaultDeny and AsmPeerAuthnMeshStrictMtls templates, which require self-managed Gatekeeper. For built-in replacements, enable Binary Authorization instead of using the K8sRequireBinAuthZ ConstraintTemplate, and configure the node image of your node pools instead of using the K8sRequireCosNodeImage ConstraintTemplate.

Migrate to ValidatingAdmissionPolicy

A ValidatingAdmissionPolicy contains the policy logic, like a ConstraintTemplate. A ValidatingAdmissionPolicyBinding applies the policy, like a constraint. A ValidatingAdmissionPolicy uses CEL expressions instead of Rego, and the API server evaluates the expressions directly, without a webhook.

Example: allowed image repositories

The following Policy Controller constraint requires Pods to use images from one Artifact Registry repository. It excludes the kube-system namespace:

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sAllowedRepos
metadata:
  name: allowed-repos
spec:
  enforcementAction: deny
  match:
    kinds:
    - apiGroups: [""]
      kinds: ["Pod"]
    excludedNamespaces: ["kube-system"]
  parameters:
    repos:
    - "us-docker.pkg.dev/example-project/example-repo/"

The following ValidatingAdmissionPolicy and ValidatingAdmissionPolicyBinding enforce the same rule. The ValidatingAdmissionPolicyBinding uses the Warn and Audit actions so that you can compare results before you enforce the ValidatingAdmissionPolicy:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: allowed-repos
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
    - apiGroups: [""]
      apiVersions: ["v1"]
      operations: ["CREATE", "UPDATE"]
      resources: ["pods", "pods/ephemeralcontainers"]
  variables:
  - name: repos
    expression: "['us-docker.pkg.dev/example-project/example-repo/']"
  - name: images
    expression: >-
      object.spec.containers.map(c, c.image) +
      (has(object.spec.initContainers) ?
        object.spec.initContainers.map(c, c.image) : []) +
      (has(object.spec.ephemeralContainers) ?
        object.spec.ephemeralContainers.map(c, c.image) : [])
  validations:
  - expression: >-
      variables.images.all(image,
        variables.repos.exists(repo, image.startsWith(repo)))
    messageExpression: >-
      'container images must come from one of these repositories: ' +
      variables.repos.join(', ')
    reason: Forbidden
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: allowed-repos
spec:
  policyName: allowed-repos
  validationActions: ["Warn", "Audit"]
  matchResources:
    namespaceSelector:
      matchExpressions:
      - key: kubernetes.io/metadata.name
        operator: NotIn
        values: ["kube-system", "gatekeeper-system"]

In this example, the repos variable holds the value from the constraint's parameters field. The validation expression returns a true value for Pods that comply with the ValidatingAdmissionPolicy, which is the opposite of a Rego violation rule. To reuse one ValidatingAdmissionPolicy across multiple ValidatingAdmissionPolicyBindings that supply different values, use a parameter resource instead of a variable. For more information about parameter resources, see Parameter resources in the Kubernetes documentation.

The following table shows how constraint fields map to ValidatingAdmissionPolicy and ValidatingAdmissionPolicyBinding fields:

Mapping of Policy Controller constraint fields to ValidatingAdmissionPolicy and ValidatingAdmissionPolicyBinding fields
Constraint field ValidatingAdmissionPolicy or ValidatingAdmissionPolicyBinding field
spec.enforcementAction The validationActions field in the ValidatingAdmissionPolicyBinding, which maps the deny, warn, and dryrun actions to the ["Deny"], ["Warn"], and ["Audit"] values
spec.parameters The variables field in the ValidatingAdmissionPolicy, or a parameter resource that the ValidatingAdmissionPolicyBinding references in the paramRef field
spec.match.kinds The matchConstraints.resourceRules field in the ValidatingAdmissionPolicy, which uses API groups and resource names, such as the pods resource, instead of kinds
spec.match.namespaces and spec.match.excludedNamespaces The matchResources.namespaceSelector field in the ValidatingAdmissionPolicyBinding, using the In or NotIn operator on the kubernetes.io/metadata.name label
spec.match.labelSelector The matchResources.objectSelector field in the ValidatingAdmissionPolicyBinding
spec.match.namespaceSelector The matchResources.namespaceSelector field in the ValidatingAdmissionPolicyBinding
spec.match.scope The scope field of each resource rule

For more information about how to write expressions, see CEL in Kubernetes in the Kubernetes documentation.

Test policies alongside Policy Controller

To replace a constraint without a gap in enforcement, run the ValidatingAdmissionPolicy next to the constraint before you enforce the ValidatingAdmissionPolicy:

  1. Apply the ValidatingAdmissionPolicy and a ValidatingAdmissionPolicyBinding that has the validationActions: ["Warn", "Audit"] setting. Leave the Policy Controller constraint in place.
  2. Compare the results by using the audit log query in Replace violation reporting. If the ValidatingAdmissionPolicy flags requests that Policy Controller allows, or the reverse, then fix the ValidatingAdmissionPolicy. For ValidatingAdmissionPolicies that match Pods, don't rely on warnings. Controllers create the Pods for Deployments and other workloads, so the controller receives the warning.
  3. After the results match, change the ValidatingAdmissionPolicyBinding to use the validationActions: ["Deny"] setting.
  4. Delete the Policy Controller constraint.

Plan for behavior differences

ValidatingAdmissionPolicies behave differently from Policy Controller in the following ways, which can block requests or leave gaps in enforcement:

  • Failure policy: the Policy Controller webhook fails open, which means that the API server admits a request if Policy Controller doesn't respond. A ValidatingAdmissionPolicy that has the failurePolicy: Fail setting denies a request if an expression returns an error or exceeds the CEL cost limit. The ValidatingAdmissionPolicy also denies a request if a referenced parameter resource doesn't exist and the value of the paramRef.parameterNotFoundAction field in the ValidatingAdmissionPolicyBinding is Deny. If the value is Allow, then the API server skips a missing parameter resource regardless of the failurePolicy setting. Test each ValidatingAdmissionPolicy and choose values for both fields.
  • Missing fields: Rego treats a missing field as no violation, but CEL returns an error. Before you read an optional field, use the has() macro to check whether the field exists.
  • Exemptions: a ValidatingAdmissionPolicy doesn't exempt namespaces by default or use the admission.gatekeeper.sh/ignore label. On GKE Standard clusters, a ValidatingAdmissionPolicyBinding applies to every namespace in the cluster, including system namespaces such as the kube-system namespace, unless you exclude them in the matchResources.namespaceSelector field on the ValidatingAdmissionPolicyBinding or in the matchConstraints.namespaceSelector or matchConditions fields on the ValidatingAdmissionPolicy. On Autopilot clusters, GKE excludes managed namespaces automatically and rejects ValidatingAdmissionPolicies that match the nodes, persistentVolumes, certificatesigningrequests, or tokenreviews resources. For more information, see GKE Autopilot security measures.
  • Subresources: Policy Controller checks Pod updates that add ephemeral containers. To keep that coverage, include the pods/ephemeralcontainers subresource in your resource rules, as the example does.
  • No audit of existing resources: a ValidatingAdmissionPolicy evaluates only incoming API server requests. It doesn't report existing resources that violate a ValidatingAdmissionPolicy.
  • One message for each request: a ValidatingAdmissionPolicy reports the first validation that fails, not every violation.

Use Pod Security Admission for Pod security bundles

If you use the pss-baseline-v2022 or pss-restricted-v2022 bundles, then replace the bundle with Pod Security Admission instead of translating each constraint. Pod Security Admission uses the built-in Kubernetes PodSecurity admission controller to enforce predefined rules from the Kubernetes Pod Security Standards based on namespace labels. The pss-baseline-v2022 bundle corresponds to the baseline level, and the pss-restricted-v2022 bundle corresponds to the restricted level.

Pod Security Admission doesn't cover every control in the psp-v2022 bundle, such as allowlists for host paths, users, capabilities, and sysctls. Use ValidatingAdmissionPolicies for those controls. Pod Security Admission also can't exempt specific images, like Policy Controller constraints that use the exemptImages parameter. GKE doesn't support configuring Pod Security Admission exemptions. If you need exceptions, then use ValidatingAdmissionPolicies for the affected policies.

For more information about how to configure Pod Security Admission, see Apply predefined Pod-level security policies using PodSecurity.

Migrate to self-managed OPA Gatekeeper

Choose Gatekeeper for the policies that ValidatingAdmissionPolicies can't express. Policy Controller is based on Gatekeeper, so your ConstraintTemplates and constraints use the same APIs in Gatekeeper. All templates in the Policy Controller template library work in Gatekeeper without changes to their logic. However, Gatekeeper uses a newer Rego engine and stricter validation that can reject a custom ConstraintTemplate.

When you use self-managed Gatekeeper, you become responsible for the tasks that Google Cloud performs for Policy Controller, such as installing, upgrading, scaling, and monitoring Gatekeeper on each cluster. Customer Care doesn't support open source Gatekeeper. For help, use the Gatekeeper GitHub issue tracker.

Gatekeeper doesn't include the Policy Controller template library or policy bundles. Your backup files contain the ConstraintTemplates and constraints from your clusters. For templates that originated in the open source Gatekeeper library, you can use the templates in the Gatekeeper policy library. Template names stay the same, but parameters and messages can differ between versions.

To move policies to Gatekeeper, follow these steps:

  1. Optional: If you can't accept a gap in enforcement while you replace Policy Controller, then migrate your most important policies to ValidatingAdmissionPolicies first. Make sure that those ValidatingAdmissionPolicyBindings exclude the gatekeeper-system namespace so that they don't block the Gatekeeper installation Jobs. After you remove Policy Controller, your cluster stops enforcing Gatekeeper policies until you apply the corresponding Gatekeeper constraints. ValidatingAdmissionPolicies keep enforcing because they don't depend on Policy Controller or Gatekeeper.
  2. If you use custom ConstraintTemplates, then test each custom template manifest by using the gator command-line tool from the Gatekeeper version that you plan to install:

    gator test --filename=TEMPLATE_FILE
    

    Replace TEMPLATE_FILE with the path to your ConstraintTemplate manifest file. Before you remove Policy Controller, fix any custom ConstraintTemplate that Gatekeeper rejects.

  3. If you deploy constraints from a Git repository by using a GitOps tool, then prepare the ConstraintTemplates that your constraints use and add them to the repository when you install Gatekeeper. Otherwise, your constraints fail to apply after you remove Policy Controller because their ConstraintTemplates no longer exist.

  4. Remove Policy Controller by following the steps in Remove Policy Controller. Don't install Gatekeeper on a cluster that still runs Policy Controller. They both use the gatekeeper-system namespace, the same CustomResourceDefinitions, and the same webhook names.

  5. Install Gatekeeper by using Helm, using the settings that you choose from the table in the next section, Gatekeeper settings that differ from Policy Controller. For more information about how to install Gatekeeper, see Installation in the Gatekeeper documentation.

    On Autopilot clusters, the default Helm chart installation fails because GKE rejects wildcard rules in mutating webhooks. Set the disableMutation: true Helm value, or set the mutatingWebhookCustomRules Helm value to list only the specific resources that your mutators target. Don't include the nodes, persistentvolumes, certificatesigningrequests, or tokenreviews resources.

  6. In your backup files, remove the following fields, labels, and annotations from each resource that you plan to reapply:

    • The status field.
    • The resourceVersion, uid, creationTimestamp, generation, managedFields, and ownerReferences fields in the metadata section.
    • The kubectl.kubernetes.io/last-applied-configuration annotation.
    • The app.kubernetes.io/managed-by: anthospolicycontroller.googleapis.com label. If Policy Controller is enabled on the cluster again, such as by a fleet-level default configuration, then it manages resources that have this label and can modify or delete them.
    • Labels and annotations whose keys have the policycontroller. prefix.
  7. Apply the ConstraintTemplates that your constraints use, and then check that Gatekeeper created a CustomResourceDefinition for each template:

    kubectl get constrainttemplates \
        -o custom-columns='NAME:.metadata.name,CREATED:.status.created'
    

    When every template shows a true value in the CREATED column, apply your constraints.

Gatekeeper settings that differ from Policy Controller

Before you install Gatekeeper, review the settings in the following table where the Gatekeeper Helm chart defaults differ from Policy Controller:

Comparison of Policy Controller defaults and Gatekeeper Helm chart defaults
Setting Policy Controller default Gatekeeper Helm chart default Recommendation
Mutation Off unless you turned it on On If you don't use mutation, then set the disableMutation: true Helm value. On Autopilot clusters, you must either set the disableMutation: true Helm value or configure the mutatingWebhookCustomRules Helm value without wildcards or restricted resources. If you don't specify one of these values, then GKE rejects the installation.
Exempt namespaces The gatekeeper-system namespace and the namespaces in your exemptableNamespaces list The namespace where Gatekeeper is installed Add your exempt namespaces to the controllerManager.exemptNamespaces Helm value. Gatekeeper rejects changes to namespaces that have the admission.gatekeeper.sh/ignore label unless those namespaces are exempt.
Referential data Synced automatically for referential bundles Not synced Create a Gatekeeper Config or SyncSet that lists the kinds that your referential templates read. Until those kinds sync into the data.inventory object, conflict checks allow violating requests, and existence checks, such as the K8sRequireNamespaceNetworkPolicies ConstraintTemplate, deny every matching request even when the required resource exists.
ValidatingAdmissionPolicy generation Not available On for templates that contain CEL code Policy Controller templates contain only Rego, so Gatekeeper generates ValidatingAdmissionPolicies only if you add CEL to your ConstraintTemplates. To keep enforcement in the webhook, set the defaultCreateVAPForTemplates: false and defaultCreateVAPBindingForConstraints: false Helm values. For more information, see Integration with Kubernetes Validating Admission Policy in the Gatekeeper documentation.
Replicas and resources One replica for each component, with a 1,024 MiB memory limit Three webhook replicas, with different resource limits Configure the replica count and resource limits based on your current usage.
Images Pulled from a Google-managed registry Pulled from Docker Hub (the openpolicyagent/gatekeeper, openpolicyagent/gatekeeper-crds, and curlimages/curl images) For private clusters or clusters that enforce Binary Authorization, mirror all three images to Artifact Registry and override their image values in the chart. If you don't mirror the curlimages/curl image, then disable the webhook probe Job.

By default, Gatekeeper exposes only Prometheus metrics, and its metric names differ from the names that Policy Controller sends to Cloud Monitoring. Update any dashboards and alerts that use Policy Controller metrics.

Migrate mutation policies

If you enabled mutation in Policy Controller, then list your mutators:

kubectl get assign,assignmetadata,assignimage,modifyset

For each mutator, choose one of the following options:

  • MutatingAdmissionPolicy: you can use a MutatingAdmissionPolicy in GKE version 1.36 and later. Like a ValidatingAdmissionPolicy, a MutatingAdmissionPolicy uses CEL expressions and runs in the API server without a webhook. Kubernetes runs all MutatingAdmissionPolicies before ValidatingAdmissionPolicies, so test them together to make sure that a mutation doesn't cause a ValidatingAdmissionPolicy to reject a valid manifest. On Autopilot clusters, GKE excludes managed namespaces from MutatingAdmissionPolicies. For more information about mutating policies, see Mutating Admission Policy in the Kubernetes documentation.
  • Self-managed Gatekeeper: on Standard clusters, your mutators work without changes because Gatekeeper turns on mutation by default. On Autopilot clusters, you must set the mutatingWebhookCustomRules Helm value in the Gatekeeper chart to list the specific resources that your mutators target instead of wildcard rules. For more information, see GKE Autopilot security measures. After you install Gatekeeper, remove the Policy Controller metadata from your mutator backup file and reapply your mutators, as you do for constraints.

Update policy checks in build pipelines

Your build pipeline might check manifests against your Policy Controller constraints before deployment. For example, the pipeline might use the gatekeeper function in kpt or the gator command-line tool. For more information, see Validate apps against company policies in a CI pipeline.

If your pipeline checks manifests, then update it for your replacement:

  • If you use Gatekeeper, then you can keep your pipeline. Update the file or directory paths in your gator or kpt command to reference the repository or directory that contains your cleaned Gatekeeper ConstraintTemplate and constraint manifests.
  • If you use ValidatingAdmissionPolicies, then those tools don't evaluate your policies. Instead, run a server-side dry run against a test cluster that has your ValidatingAdmissionPolicies and ValidatingAdmissionPolicyBindings that use the Deny action:

    kubectl apply --dry-run=server -f MANIFEST_DIRECTORY
    

    Replace MANIFEST_DIRECTORY with the directory that contains your manifests. The command fails if a ValidatingAdmissionPolicy rejects a manifest.

Replace violation reporting

A ValidatingAdmissionPolicy doesn't include a dashboard, and it doesn't audit resources that already exist in your cluster. Use the following methods instead:

  • Warnings: if a ValidatingAdmissionPolicyBinding uses the Warn action, then the API server returns a warning to the client that sent the request, such as the kubectl command-line tool. For Pods that a controller creates, such as the Pods in a Deployment, the controller receives the warning.
  • Audit logs: if a ValidatingAdmissionPolicyBinding uses the Audit action, then the API server annotates the audit log entry for each failed validation. GKE writes create, update, and delete requests to Admin Activity audit logs in Cloud Logging. To find failed validations, run the following query in the Logs Explorer:

    resource.type="k8s_cluster"
    logName:"cloudaudit.googleapis.com%2Factivity"
    labels."validation.policy.admission.k8s.io/validation_failure":*
    

If you use constraints that have the dryrun enforcement action as a compliance report on existing resources, then ValidatingAdmissionPolicies can't replace that report. Keep those policies on self-managed Gatekeeper, whose audit controller checks existing resources on a schedule.

Remove Policy Controller

Before you remove Policy Controller from a cluster, complete the following tasks:

  • Confirm that any policies that you migrated to ValidatingAdmissionPolicies or Pod Security Admission are enforced and that you have a backup of your ConstraintTemplates, constraints, and mutators. Removing Policy Controller deletes its CustomResourceDefinitions, which deletes every ConstraintTemplate, constraint, and mutator on the cluster, including the ones that you created.
  • If you deploy constraints from a Git repository by using a GitOps tool such as Config Sync or Argo CD, then remove the replaced constraints from the repository. Confirm that the constraints are removed from your clusters.
  • Optional: To confirm that nothing depends on the Policy Controller webhook, suspend Policy Controller first. Suspending removes only the webhook configurations, and you can restore them.

To remove Policy Controller, follow these steps:

  1. Uninstall Policy Controller from your clusters:

    gcloud container fleet policycontroller disable \
        --memberships=MEMBERSHIP_NAME \
        --project=PROJECT_ID
    

    Replace the following:

    • MEMBERSHIP_NAME: the membership name of the registered cluster that you want to uninstall Policy Controller from. To uninstall Policy Controller from multiple clusters, specify a comma-separated list of membership names, or replace the --memberships=MEMBERSHIP_NAME flag with the --all-memberships flag.
    • PROJECT_ID: the ID of your fleet host project.
  2. Confirm that Policy Controller has finished uninstalling from your clusters:

    gcloud container fleet policycontroller describe \
        --memberships=MEMBERSHIP_NAME \
        --project=PROJECT_ID \
        --format="yaml(membershipStates)"
    

    Wait until the value of the top-level policycontroller.state field for each membership is NOT_INSTALLED, not DECOMMISSIONING. Individual componentStates entries reach the NOT_INSTALLED state before CustomResourceDefinition cleanup finishes.

    If you plan to install self-managed Gatekeeper, then confirm that the gatekeeper-system namespace no longer exists:

    kubectl get namespace gatekeeper-system
    

    Wait until the output reports that the gatekeeper-system namespace isn't found.

  3. Optional: To prevent clusters that you register later from getting Policy Controller, remove the fleet-level default configuration:

    gcloud container fleet policycontroller enable \
        --no-fleet-default-member-config \
        --project=PROJECT_ID
    

    This command removes only the default configuration. It doesn't install Policy Controller on any cluster.

Remove Policy Controller from clusters outside a fleet

The gcloud container fleet policycontroller disable command removes Policy Controller only from clusters that the fleet manages. A cluster can run Policy Controller without fleet management in the following cases:

  • You installed Policy Controller by using the ConfigManagement Operator.
  • You unregistered the cluster from its fleet, or ran the gcloud container fleet policycontroller detach command, before Policy Controller finished uninstalling.

To remove Policy Controller from these clusters, bring the cluster under fleet management, and then uninstall Policy Controller:

  1. Back up your ConstraintTemplates, constraints, sync configuration, and mutators, as described in Inventory and classify your policies. When you clean metadata from backups taken from a ConfigManagement Operator installation, also remove the configmanagement.gke.io/* and k8s-app: kubernetes-config-management labels in addition to the ownerReferences field and policycontroller.* metadata.
  2. If you installed Policy Controller by using the ConfigManagement Operator, then remove the policyController field from the ConfigManagement custom resource on the cluster. Wait until reconciliation completes. Reconciliation can take about two minutes. Then, check whether the gatekeeper-system namespace still exists:

    kubectl get namespace gatekeeper-system
    

    In ConfigManagement Operator version 1.17 and earlier, removing the policyController field uninstalls Policy Controller automatically. If the gatekeeper-system namespace isn't found, then skip the remaining steps in this section. In ConfigManagement Operator version 1.18 and later, the gatekeeper-system namespace remains on the cluster, so continue to the next step.

  3. If the cluster isn't registered to a fleet, then register it. For more information, see Register a cluster on Google Cloud to your fleet.

  4. Check whether mutation is enabled on the cluster:

    kubectl get mutatingwebhookconfiguration \
        gatekeeper-mutating-webhook-configuration
    
  5. Enable Policy Controller on the cluster's membership so that the fleet takes over management of the existing installation. If the preceding command returned the gatekeeper-mutating-webhook-configuration resource, then include the --mutation flag so that the fleet adopts and later removes cluster-scoped mutation resources:

    gcloud container fleet policycontroller enable \
        --memberships=MEMBERSHIP_NAME \
        --project=PROJECT_ID
    

    Replace the following:

    • MEMBERSHIP_NAME: the membership name of your registered cluster.
    • PROJECT_ID: the ID of your fleet host project.
  6. Wait until the value of the policycontroller.state field for the membership is ACTIVE:

    gcloud container fleet policycontroller describe \
        --memberships=MEMBERSHIP_NAME \
        --project=PROJECT_ID \
        --format="yaml(membershipStates)"
    
  7. Uninstall Policy Controller by following the steps in Remove Policy Controller. If you aren't installing self-managed Gatekeeper, then delete any remaining constraint CustomResourceDefinitions after the gatekeeper-system namespace is removed:

    kubectl get crd -o name \
        | grep '\.constraints\.gatekeeper\.sh$' \
        | xargs -r kubectl delete
    
  8. Optional: If you registered the cluster only to complete this procedure, then unregister it after the describe command shows the NOT_INSTALLED state for the membership. For clusters that you registered by using the --enable-fleet flag, run the gcloud container clusters update command with the --clear-fleet-project flag instead of the gcloud container fleet memberships unregister command. For more information, see Unregistering a cluster.

What's next