Simplify multi-cluster management: Auto-import of hosted clusters with RHACM

Simplify the management of numerous Red Hat OpenShift HyperShift (HCP) clusters by automating their discovery and import using Red Hat Advanced Cluster Management for Kubernetes (RHACM). This automated process, which leverages RHACM's policy framework and multicluster engine for Kubernetes, is designed to replace manual, error-prone tasks with a reliable and scalable workflow for continuous governance.

 Try Red Hat Advanced Cluster Management for Kubernetes

Now it's time to configure the automated import process by defining the policy and Go template, creating the placement definition, and binding the resources to ensure automatic detection and import of discovered hosted clusters into Red Hat Advanced Cluster Management for Kubernetes (RHACM).

In this lesson, you will:

  • Configure the policy.
  • Understand the Go template.
  • Create the placement definition.
  • Bind the import policy to a placement definition.
  • Validate the policy.
  • Verify cluster discovery.
  • Check HyperShift add-on logs.
  • Import existing HCP clusters (optional).

Step 1: Configure the policy

To configure your policy to import all your discovered hosted clusters, create a YAML file for your DiscoveredCluster custom resource and edit the configuration that is referenced in the following example:

Note

You MUST create the file. Don't use "cat EOF".

cat policy-mce-hcp-autoimport.yaml

apiVersion: policy.open-cluster-management.io/v1
kind: Policy
metadata:
  name: policy-mce-hcp-autoimport
  namespace: open-cluster-management-global-set
  annotations:
    policy.open-cluster-management.io/standards: NIST SP 800-53
    policy.open-cluster-management.io/categories: CM Configuration Management
    policy.open-cluster-management.io/controls: CM-2 Baseline Configuration
    policy.open-cluster-management.io/description: Discovered clusters that are of
      type MultiClusterEngineHCP can be automatically imported into ACM as managed clusters.
      This policy configure those discovered clusters so they are automatically imported.
      Fine tuning MultiClusterEngineHCP clusters to be automatically imported
      can be done by configure filters at the configMap or add annotation to the discoverd cluster.
spec:
  disabled: false
  policy-templates:
    - objectDefinition:
        apiVersion: policy.open-cluster-management.io/v1
        kind: ConfigurationPolicy
        metadata:
          name: mce-hcp-autoimport-config
        spec:
          object-templates:
            - complianceType: musthave
              objectDefinition:
                apiVersion: v1
                kind: ConfigMap
                metadata:
                  name: discovery-config
                  namespace: open-cluster-management-global-set
                data:
                  rosa-filter: ""
          remediationAction: enforce 
          severity: low
    - objectDefinition:
        apiVersion: policy.open-cluster-management.io/v1
        kind: ConfigurationPolicy
        metadata:
          name: policy-mce-hcp-autoimport
        spec:
          remediationAction: enforce
          severity: low
          object-templates-raw: |
            {{- /* find the MultiClusterEngineHCP DiscoveredClusters */ -}}
            {{- range $dc := (lookup "discovery.open-cluster-management.io/v1" "DiscoveredCluster" "" "").items }}
              {{- /* Check for the flag that indicates the import should be skipped */ -}}
              {{- $skip := "false" -}}
              {{- range $key, $value := $dc.metadata.annotations }}
                {{- if and (eq $key "discovery.open-cluster-management.io/previously-auto-imported")
                           (eq $value "true") }}
                  {{- $skip = "true" }}
                {{- end }}
              {{- end }}
              {{- /* if the type is MultiClusterEngineHCP and the status is Active */ -}}
              {{- if and (eq $dc.spec.status "Active")
                         (contains (fromConfigMap "open-cluster-management-global-set" "discovery-config" "mce-hcp-filter") $dc.spec.displayName)
                         (eq $dc.spec.type "MultiClusterEngineHCP")
                         (eq $skip "false") }}
            - complianceType: musthave
              objectDefinition:
                apiVersion: discovery.open-cluster-management.io/v1
                kind: DiscoveredCluster
                metadata:
                  name: {{ $dc.metadata.name }}
                  namespace: {{ $dc.metadata.namespace }}
                spec:
                  importAsManagedCluster: true 
              {{- end }}
            {{- end }}

This policy seems a little scary. But don't worry. It's already set up and ready to go. Just create it.

oc apply -f policy-mce-hcp-autoimport.yaml

Understand the Go template

Let's dissect the template to understand each part:

  1. Loop over all DiscoveredClusters:

    {{- range $dc := (lookup "discovery.open-cluster-management.io/v1" "DiscoveredCluster" "" "").items }}
    1. lookup queries all DiscoveredClusters in the cluster.
    2. $dc is the iteration variable.
  2. Previous import check:

    {{- $skip := "false" -}}
    {{- range $key, $value := $dc.metadata.annotations }}
      {{- if and (eq $key "discovery.open-cluster-management.io/previously-auto-imported")
                 (eq $value "true") }}
        {{- $skip = "true" }}
      {{- end }}
    {{- end }}
    1. Avoid re-importing already imported clusters.
    2. Use annotation to mark processed clusters.
  3. Import conditions:

    {{- if and (eq $dc.spec.status "Active")
               (contains (fromConfigMap "..." "mce-hcp-filter") $dc.spec.displayName)
               (eq $dc.spec.type "MultiClusterEngineHCP")
               (eq $skip "false") }}

    Cluster will be imported IF:

    1. Status is Active.
    2. DisplayName contains the filter from ConfigMap.
    3. Type is MultiClusterEngineHCP.
    4. Not previously imported.
  4. Import action:

    - complianceType: musthave
      objectDefinition:
        apiVersion: discovery.open-cluster-management.io/v1
        kind: DiscoveredCluster
        metadata:
          name: {{ $dc.metadata.name }}
          namespace: {{ $dc.metadata.namespace }}
        spec:
          importAsManagedCluster: true
    1. Sets importAsManagedCluster: true on the DiscoveredCluster.
    2. Triggers RHACM's import process.

Step 2: Placement definition

Create the placement definition that selects only the local-cluster, which is a managed hub cluster. Use the following YAML sample:

cat <<EOF | oc apply -f -
apiVersion: cluster.open-cluster-management.io/v1beta1
kind: Placement
metadata:
  name: policy-mce-hcp-autoimport-placement
  namespace: open-cluster-management-global-set
spec:
  tolerations:
    - key: cluster.open-cluster-management.io/unreachable
      operator: Exists
    - key: cluster.open-cluster-management.io/unavailable
      operator: Exists
  clusterSets:
    - global
  predicates:
    - requiredClusterSelector:
        labelSelector:
          matchExpressions:
            - key: local-cluster
              operator: In
              values:
                - "true"
EOF

Why local-cluster? DiscoveredClusters are created on the hub, so the policy needs to run there to query them.

Step 3: Bind the import policy to a placement definition

Connect the resources by using a PlacementBinding resource. See the following example where placementRef references the placement that you created, and subjects references the policy that you created:

cat <<EOF | oc apply -f -
apiVersion: policy.open-cluster-management.io/v1
kind: PlacementBinding
metadata:
  name: policy-mce-hcp-autoimport-placement-binding
  namespace: open-cluster-management-global-set
placementRef:
  name: policy-mce-hcp-autoimport-placement
  apiGroup: cluster.open-cluster-management.io
  kind: Placement
subjects:
  - name: policy-mce-hcp-autoimport
    apiGroup: policy.open-cluster-management.io
    kind: Policy
EOF

Step 4: Policy validation

Verify if the policy was created and is compliant. 

  1. Go to Governance > Policies, click policy-mce-hcp-autoimport then Results. If everything is correct, there will be no violations.

    Screenshot of MCE HCP Autoimport policy results in ACM hub
    Figure 1: MCE HCP Autoimport policy results.
  2. Or use the CLI:

    oc get policies.policy.open-cluster-management.io policy-mce-hcp-autoimport -n open-cluster-management-global-set
  3. Expected output:

    NAME                        REMEDIATION ACTION   COMPLIANCE STATE   AGE
    policy-mce-hcp-autoimport   enforce              Compliant          2m

    You're now ready to go! All new hosted clusters that you create in your imported MCE cluster will be automatically detected and imported into RHACM!
     

    Screenshot of ACM hub showing autoimporting in place
    Figure 2: Hosted cluster autoimporting.

Step 4: Check discovery in RHACM

Wait a few minutes and verify if the cluster was discovered:

oc get discoveredcluster -A

Expected output:

NAMESPACE      NAME                                   DISPLAY NAME                          CLOUD PROVIDER   STATUS   AGE
hc-site1-lab   f74a752d-6209-4bd2-8978-9374e437445b   hc-site1-lab-cluster-workload-linux   agent            Active   15m

Inspect the discovered cluster details:

oc get discoveredcluster -n hc-site1-lab f74a752d-6209-4bd2-8978-9374e437445b -o yaml

apiVersion: discovery.open-cluster-management.io/v1
kind: DiscoveredCluster
metadata:
  creationTimestamp: "2025-10-30T15:58:48Z"
  generation: 2
  labels:
    hypershift.open-cluster-management.io/hc-name: cluster-workload-linux
    hypershift.open-cluster-management.io/hc-namespace: hc-site1-lab
  name: f74a752d-6209-4bd2-8978-9374e437445b
  namespace: hc-site1-lab
  resourceVersion: "145526"
  uid: 900f11b2-e7b6-4e88-9204-b8c33229675c
spec:
  apiUrl: https://X.X.X.X:6443
  cloudProvider: agent
  creationTimestamp: "2025-10-30T15:57:05Z"
  credential: {}
  displayName: hc-site1-lab-cluster-workload-linux
  importAsManagedCluster: true
  isManagedCluster: false
  name: f74a752d-6209-4bd2-8978-9374e437445b
  openshiftVersion: 0.0.0
  status: Active
  type: MultiClusterEngineHCP

Step 5: Check HyperShift add-on logs

Confirm the add-on is working correctly:

oc logs -n open-cluster-management-agent-addon-discovery \
  deployment/hypershift-addon-agent -f

You should see something like this:

INFO  agent.controller-manager-setup  agent/agent.go:257  starting manager
INFO  agent.agent-reconciler  agent/agent_helper.go:48  local cluster name is local-cluster
INFO  agent.agent-reconciler.addon-status-controller  agent/addon_status_controller.go:66  reconciling Deployment hypershift/operator
INFO  agent.agent-reconciler.addon-status-controller  agent/addon_status_controller.go:104  done reconcile Deployment hypershift/operator
INFO  agent.agent-reconciler  install/upgrade.go:75  hypershift operator management is disabled. Skip installing or upgrading the hypershift operator

Perfect! The add-on is configured to discover clusters, but not manage the operator, which already exists on MCE.

Screenshot of ACM hub showing hosted cluster autoimport
Figure 3: Hosted cluster autoimported.

Bonus step: Import existing HCP clusters

  1. To import existing HCP clusters, go to Infrastructure > Clusters > Import Cluster

  2. Enable YAML view, and customize your manifest as indicated.

  3. For this learning path, we are using the following cluster names:

    1. Hosting Cluster Name (your MCE cluster): hc-site1-lab
    2. Hosted Cluster Agent Nodes: cluster-workload-linux
  4. The imported hosted cluster name must be a concatenation of <managed-cluster-name>-<hosted-cluster-name>, as stated in Lesson 3.

    Example name: hc-site1-lab-cluster-workload-linux

    Screenshot of YAML view and hosted cluster manual import
    Figure 4: Hosted cluster manual import.

    For reference, here is the example YAML used in this lab.

    apiVersion: cluster.open-cluster-management.io/v1
    kind: ManagedCluster
    metadata:
      name: hc-site1-lab-cluster-workload-linux
      labels:
        name: hc-site1-lab-cluster-workload-linux
        cloud: auto-detect
        vendor: OpenShift
        cluster.open-cluster-management.io/clusterset: default
      annotations:
        import.open-cluster-management.io/hosting-cluster-name: local-cluster
        import.open-cluster-management.io/klusterlet-deploy-mode: Hosted
        open-cluster-management/created-via: hypershift
    spec:
      hubAcceptsClient: true
      leaseDurationSeconds: 60  
    ---
    apiVersion: v1
    kind: Secret
    metadata:
      name: auto-import-secret
      namespace: hc-site1-lab-cluster-workload-linux
    stringData:
      kubeconfig: "********************"
    type: Opaque
    ---
    apiVersion: agent.open-cluster-management.io/v1
    kind: KlusterletAddonConfig
    metadata:
      name: hc-site1-lab-cluster-workload-linux
      namespace: hc-site1-lab-cluster-workload-linux
    spec:
      clusterName: hc-site1-lab-cluster-workload-linux
      clusterNamespace: hc-site1-lab-cluster-workload-linux
      clusterLabels:
        name: hc-site1-lab-cluster-workload-linux
        cloud: auto-detect
        vendor: OpenShift
        cluster.open-cluster-management.io/clusterset: default
      applicationManager:
        enabled: true
      policyController:
        enabled: true
      searchCollector:
        enabled: true
      certPolicyController:
        enabled: true

     

    Screenshot of successful manual import
    Figure 5: ACM hub showing successful manual import.
    Figure 5: RHACM hub showing successful manual import.

Success! The hosted cluster has been manually imported into RHACM.

Previous resource
Discover hosted clusters