---
title: CCO-based workflow for OLM-managed Operators with Microsoft Entra Workload ID
---

# CCO-based workflow for OLM-managed Operators with Microsoft Entra Workload ID {#osdk-cco-azure}

When an OpenShift Container Platform cluster running on Azure is in **Workload Identity / Federated Identity** mode, it means the cluster is utilizing features of Azure and OpenShift Container Platform to apply *user-assigned managed identities* or *app registrations* in Microsoft Entra Workload ID at an application level.

The Cloud Credential Operator (CCO) is a cluster Operator installed by default in OpenShift Container Platform clusters running on cloud providers. Starting in OpenShift Container Platform 4.14.8, the CCO supports workflows for OLM-managed Operators with Workload ID.

For the purposes of Workload ID, the CCO provides the following functions:

- Detects when it is running on an Workload ID-enabled cluster
- Checks the `CredentialsRequest` object for the presence of fields that provide the required information for granting Operators access to Azure resources

The CCO can semi-automate this process through an expanded use of `CredentialsRequest` objects, which can request the creation of `Secrets` that contain the information required for Workload ID workflows.

> [!NOTE]
> Subscriptions with automatic approvals for updates are not recommended because there might be permission changes to make before updating. Subscriptions with manual approvals for updates ensure that administrators have the opportunity to verify the permissions of the later version, take any necessary steps, and then update.

As an Operator author preparing an Operator for use alongside the updated CCO in OpenShift Container Platform 4.14 and later, you should instruct users and add code to handle the divergence from earlier CCO versions, in addition to handling Workload ID token authentication (if your Operator is not already enabled). The recommended method is to provide a `CredentialsRequest` object with the correctly filled Workload ID fields and let the CCO create the `Secret` object for you.

> [!IMPORTANT]
> If you plan to support OpenShift Container Platform clusters earlier than version 4.14, consider providing users with instructions on how to manually create a secret with the Workload ID-enabling information by using the CCO utility (`ccoctl`). Earlier CCO versions are unaware of Workload ID mode on the cluster and cannot create secrets for you.
>
> Your code should check for secrets that never appear and warn users to follow the fallback instructions you have provided.

Authentication with Workload ID requires the following information:

- `azure_client_id`
- `azure_tenant_id`
- `azure_region`
- `azure_subscription_id`
- `azure_federated_token_file`

The **Install Operator** page in the web console allows cluster administrators to provide this information at installation time. This information is then propagated to the `Subscription` object as environment variables on the Operator pod.

**Additional resources**
{._additional-resources}

- [OLM-managed Operator support for authentication with Microsoft Entra Workload ID](/openshift-docs-markdown/authentication/managing_cloud_provider_credentials/cco-short-term-creds#cco-short-term-creds-azure-olm_cco-short-term-creds)
- [Installing from OperatorHub using the web console](/openshift-docs-markdown/operators/admin/olm-adding-operators-to-cluster#olm-installing-from-software-catalog-using-web-console_olm-adding-operators-to-a-cluster)
- [Installing from OperatorHub using the CLI](/openshift-docs-markdown/operators/admin/olm-adding-operators-to-cluster#olm-installing-operator-from-software-catalog-using-cli_olm-adding-operators-to-a-cluster)

## Enabling Operators to support CCO-based workflows with Microsoft Entra Workload ID {#osdk-cco-azure-enabling_osdk-cco-azure}

As an Operator author designing your project to run on Operator Lifecycle Manager (OLM), you can enable your Operator to authenticate against Microsoft Entra Workload ID-enabled OpenShift Container Platform clusters by customizing your project to support the Cloud Credential Operator (CCO).

With this method, the Operator is responsible for and requires RBAC permissions for creating the `CredentialsRequest` object and reading the resulting `Secret` object.

> [!NOTE]
> By default, pods related to the Operator deployment mount a `serviceAccountToken` volume so that the service account token can be referenced in the resulting `Secret` object.

**Prerequisites**

- OpenShift Container Platform 4.14 or later
- Cluster in Workload ID mode
- OLM-based Operator project

**Procedure**

1. Update your Operator project’s `ClusterServiceVersion` (CSV) object:

   1. Ensure your Operator has RBAC permission to create `CredentialsRequests` objects:

      ```yaml {title="Example clusterPermissions list"}
      # ...
      install:
        spec:
          clusterPermissions:
          - rules:
            - apiGroups:
              - "cloudcredential.openshift.io"
              resources:
              - credentialsrequests
              verbs:
              - create
              - delete
              - get
              - list
              - patch
              - update
              - watch
      ```
   2. Add the following annotation to claim support for this method of CCO-based workflow with Workload ID:

      ```yaml
      # ...
      metadata:
       annotations:
         features.operators.openshift.io/token-auth-azure: "true"
      ```
2. Update your Operator project code:

   1. Get the client ID, tenant ID, and subscription ID from the environment variables set on the pod by the `Subscription` object. For example:

      ```go
      // Get ENV var
      clientID := os.Getenv("CLIENTID")
      tenantID := os.Getenv("TENANTID")
      subscriptionID := os.Getenv("SUBSCRIPTIONID")
      azureFederatedTokenFile := "/var/run/secrets/openshift/serviceaccount/token"
      ```
   2. Ensure you have a `CredentialsRequest` object ready to be patched and applied.

      > [!NOTE]
      > Adding a `CredentialsRequest` object to the Operator bundle is not currently supported.
   3. Add the Azure credentials information and web identity token path to the credentials request and apply it during Operator initialization:

      ```go {title="Example applying CredentialsRequest object during Operator initialization"}
      // apply CredentialsRequest on install
      credReqTemplate.Spec.AzureProviderSpec.AzureClientID = clientID
      credReqTemplate.Spec.AzureProviderSpec.AzureTenantID = tenantID
      credReqTemplate.Spec.AzureProviderSpec.AzureRegion = "centralus"
      credReqTemplate.Spec.AzureProviderSpec.AzureSubscriptionID = subscriptionID
      credReqTemplate.CloudTokenPath = azureFederatedTokenFile

      c := mgr.GetClient()
      if err := c.Create(context.TODO(), credReq); err != nil {
          if !errors.IsAlreadyExists(err) {
              setupLog.Error(err, "unable to create CredRequest")
              os.Exit(1)
          }
      }
      ```
   4. Ensure your Operator can wait for a `Secret` object to show up from the CCO, as shown in the following example, which is called along with the other items you are reconciling in your Operator:

      ```go {title="Example wait for Secret object"}
      // WaitForSecret is a function that takes a Kubernetes client, a namespace, and a v1 "k8s.io/api/core/v1" name as arguments
      // It waits until the secret object with the given name exists in the given namespace
      // It returns the secret object or an error if the timeout is exceeded
      func WaitForSecret(client kubernetes.Interface, namespace, name string) (*v1.Secret, error) {
        // set a timeout of 10 minutes
        timeout := time.After(10 * time.Minute)

        // set a polling interval of 10 seconds
        ticker := time.NewTicker(10 * time.Second)

        // loop until the timeout or the secret is found
        for {
           select {
           case <-timeout:
              // timeout is exceeded, return an error
              return nil, fmt.Errorf("timed out waiting for secret %s in namespace %s", name, namespace)
                 // add to this error with a pointer to instructions for following a manual path to a Secret that will work on STS
           case <-ticker.C:
              // polling interval is reached, try to get the secret
              secret, err := client.CoreV1().Secrets(namespace).Get(context.Background(), name, metav1.GetOptions{})
              if err != nil {
                 if errors.IsNotFound(err) {
                    // secret does not exist yet, continue waiting
                    continue
                 } else {
                    // some other error occurred, return it
                    return nil, err
                 }
              } else {
                 // secret is found, return it
                 return secret, nil
              }
           }
        }
      }
      ```

      The `timeout` value is based on an estimate of how fast the CCO might detect an added `CredentialsRequest` object and generate a `Secret` object. You might consider lowering the time or creating custom feedback for cluster administrators that could be wondering why the Operator is not yet accessing the cloud resources.
   5. Read the secret created by the CCO from the `CredentialsRequest` object to authenticate with Azure and receive the necessary credentials.
