Create Azure IAM resources separately
Developer Preview in OCP 4.21
Self-managed Azure HostedClusters are available as a Developer Preview feature in OpenShift Container Platform 4.21.
The hypershift create iam azure command creates Azure workload identities separately from infrastructure,
following the same pattern as AWS and GCP. This enables you to manage IAM resources independently from
your cluster infrastructure lifecycle.
Overview
For self-managed Azure HyperShift clusters, workload identities authenticate cluster components to Azure services using OIDC federation. The setup consists of two steps:
- Configure an OIDC Issuer using the Cloud Credential Operator (CCO) tool
- Create workload identities using
hypershift create iam azure
You must create identities separately using create iam azure and then
consume them during infrastructure or cluster creation via the --workload-identities-file flag.
Persistent Resource Groups
When setting up workload identities and OIDC issuer for the first time, create them in a persistent resource group that will not be deleted when individual clusters are destroyed. This allows you to reuse the same workload identities and OIDC issuer across multiple HostedClusters, reducing setup time and avoiding unnecessary resource recreation.
- Use a persistent resource group like
os4-common. - This resource group should be separate from the cluster-specific resource groups that get created and deleted with each HostedCluster.
- The OIDC issuer storage account should also be created in this persistent resource group.
Prerequisites
Before creating Azure IAM resources, ensure you have:
- Azure CLI (
az) installed and configured - Cloud Credential Operator (CCO) tool (
ccoctl) installed jqcommand-line JSON processor- Appropriate Azure permissions
- An Azure credentials file with the following format:
{ "subscriptionId": "your-subscription-id", "tenantId": "your-tenant-id", "clientId": "your-client-id", "clientSecret": "your-client-secret" } - An existing resource group where the managed identities will be created
Configure OIDC Issuer
Before creating workload identities, you need an OIDC issuer URL. Use the Cloud Credential Operator (CCO) tool to create one:
# Set OIDC issuer variables
PERSISTENT_RG_NAME="os4-common" # Use persistent resource group
LOCATION="eastus"
OIDC_STORAGE_ACCOUNT_NAME="yourstorageaccount"
TENANT_ID="your-tenant-id"
SUBSCRIPTION_ID="your-subscription-id"
# Create persistent resource group (if it doesn't exist)
az group create --name $PERSISTENT_RG_NAME --location $LOCATION
# Create an RSA key pair and save the private and public key
ccoctl azure create-key-pair
SA_TOKEN_ISSUER_PRIVATE_KEY_PATH="/path/to/serviceaccount-signer.private"
SA_TOKEN_ISSUER_PUBLIC_KEY_PATH="/path/to/serviceaccount-signer.public"
# Create OIDC issuer using CCO tool in persistent resource group
ccoctl azure create-oidc-issuer \
--oidc-resource-group-name ${PERSISTENT_RG_NAME} \
--tenant-id ${TENANT_ID} \
--region ${LOCATION} \
--name ${OIDC_STORAGE_ACCOUNT_NAME} \
--subscription-id ${SUBSCRIPTION_ID} \
--public-key-file ${SA_TOKEN_ISSUER_PUBLIC_KEY_PATH}
# Set OIDC issuer URL
OIDC_ISSUER_URL="https://${OIDC_STORAGE_ACCOUNT_NAME}.blob.core.windows.net/${OIDC_STORAGE_ACCOUNT_NAME}"
Creating Workload Identities
Use the hypershift create iam azure command:
CLUSTER_NAME="my-self-managed-cluster"
INFRA_ID="${CLUSTER_NAME}-$(openssl rand -hex 4)"
AZURE_CREDS="/path/to/azure-creds.json"
hypershift create iam azure \
--name $CLUSTER_NAME \
--infra-id $INFRA_ID \
--azure-creds $AZURE_CREDS \
--location $LOCATION \
--resource-group-name $PERSISTENT_RG_NAME \
--oidc-issuer-url $OIDC_ISSUER_URL \
--output-file workload-identities.json
where:
CLUSTER_NAMEis the name of the hosted cluster you intend to create.INFRA_IDis a unique identifier used to name Azure resources. Typically this is the cluster name with a random suffix appended.AZURE_CREDSpoints to an Azure credentials file with permission to create managed identities and federated credentials.LOCATIONis the Azure region for the managed identities (e.g.,eastus,westus2).PERSISTENT_RG_NAMEis the name of an existing resource group where identities will be created.OIDC_ISSUER_URLis the URL of the OIDC identity provider created in the previous step.
Running this command creates:
- 8 User-Assigned Managed Identities (one per cluster component):
- Disk CSI driver
- File CSI driver
- Image Registry
- Ingress Operator
- Cloud Provider
- NodePool Management
- Network Operator
- Control Plane Operator
- Federated Identity Credentials for each identity, configured with the OIDC issuer
Enabling KMS Identity
To also create a KMS identity for Azure Key Vault etcd encryption at rest, add the --enable-kms flag:
hypershift create iam azure \
--name $CLUSTER_NAME \
--infra-id $INFRA_ID \
--azure-creds $AZURE_CREDS \
--location $LOCATION \
--resource-group-name $PERSISTENT_RG_NAME \
--oidc-issuer-url $OIDC_ISSUER_URL \
--output-file workload-identities.json \
--enable-kms
KMS Key Vault Role Assignment
If you use --enable-kms, you must manually assign the Key Vault Crypto User role to the KMS identity on your Key Vault. The --auto-assign-roles flag does not cover this because the Key Vault scope is user-provided. See Enabling KMS Encryption for the role assignment commands.
Private Endpoint Access
The Control Plane Operator identity is always created by create iam azure. For private
clusters, this identity is used to manage Private Endpoints, Private DNS zones, VNet links,
and DNS A records in the guest subscription.
The CPO identity is assigned the Contributor role by default, scoped to the managed
resource group, NSG resource group, and VNet resource group. When using
--assign-custom-hcp-roles, a more restrictive custom role is used instead.
Note
The private endpoint access topology is configured during cluster creation using
--endpoint-access Private on the hypershift create cluster azure command.
See Deploy Azure Private Clusters for details.
Output Format
The output file contains the workload identities in JSON format, directly consumable by the
--workload-identities-file flag in create cluster azure and create infra azure commands:
{
"disk": {
"tenantID": "...",
"clientID": "...",
"resourceID": "/subscriptions/.../providers/Microsoft.ManagedIdentity/userAssignedIdentities/my-cluster-abc123-disk"
},
"file": {
"tenantID": "...",
"clientID": "...",
"resourceID": "..."
},
"imageRegistry": { ... },
"ingress": { ... },
"cloudProvider": { ... },
"nodePoolManagement": { ... },
"network": { ... },
"controlPlaneOperator": { ... }
}
Note
The controlPlaneOperator entry is always present. For public clusters, this identity
is created but not used by the control plane operator.
Using Pre-created Identities
With Infrastructure Creation
Pass the output file to create infra azure:
hypershift create infra azure \
--name CLUSTER_NAME \
--infra-id INFRA_ID \
--azure-creds AZURE_CREDENTIALS_FILE \
--base-domain BASE_DOMAIN \
--location LOCATION \
--workload-identities-file workload-identities.json \
--output-file infra-output.yaml
With Cluster Creation
Or pass directly to create cluster azure:
hypershift create cluster azure \
--name CLUSTER_NAME \
--infra-id INFRA_ID \
--azure-creds AZURE_CREDENTIALS_FILE \
--base-domain BASE_DOMAIN \
--location LOCATION \
--pull-secret PULL_SECRET_FILE \
--workload-identities-file workload-identities.json
Verification
Verify the setup:
# List created managed identities
az identity list --resource-group $PERSISTENT_RG_NAME --output table
# Verify federated credentials for one identity
az identity federated-credential list \
--identity-name "${CLUSTER_NAME}-disk-${INFRA_ID}" \
--resource-group $PERSISTENT_RG_NAME
# Test OIDC issuer accessibility
curl -s "${OIDC_ISSUER_URL}/.well-known/openid-configuration" | jq .
Destroying Workload Identities
To destroy the workload identities that were created:
hypershift destroy iam azure \
--azure-creds AZURE_CREDENTIALS_FILE \
--workload-identities-file workload-identities.json \
--resource-group-name RESOURCE_GROUP \
--name CLUSTER_NAME \
--infra-id INFRA_ID \
--dns-zone-rg-name DNS_ZONE_RG
The destroy command reads the output file from create to identify which identities to delete. Both the managed identities and their federated credentials are removed.
Destroy Order
If you created infrastructure using these identities, destroy the infrastructure first before destroying the IAM resources.
Command Reference
Required Flags for create iam azure
| Flag | Description |
|---|---|
--name |
Name of the HostedCluster |
--infra-id |
Unique infrastructure identifier |
--azure-creds |
Path to Azure credentials JSON file |
--resource-group-name |
Resource group for identities |
--oidc-issuer-url |
OIDC issuer URL for federation |
--output-file |
Output file path |
Optional Flags for create iam azure
| Flag | Description | Default |
|---|---|---|
--location |
Azure region for identities | eastus |
--cloud |
Azure cloud environment | AzurePublicCloud |
--enable-kms |
Create KMS identity for etcd encryption | false |
Required Flags for destroy iam azure
| Flag | Description |
|---|---|
--azure-creds |
Path to Azure credentials JSON file |
--workload-identities-file |
Path to workload identities JSON file |
--resource-group-name |
Resource group containing the identities |
--name |
Name of the HostedCluster |
--infra-id |
Unique infrastructure identifier |
--dns-zone-rg-name |
Resource group containing the Azure DNS zone |
Optional Flags for destroy iam azure
| Flag | Description | Default |
|---|---|---|
--cloud |
Azure cloud environment | AzurePublicCloud |
Workflow Example
Here's a complete workflow for creating a self-managed Azure cluster with separate IAM management:
# 1. Set variables
export NAME="my-cluster"
export INFRA_ID="${NAME}-$(openssl rand -hex 4)"
export LOCATION="eastus"
export BASE_DOMAIN="example.com"
export AZURE_CREDS="/path/to/azure-creds.json"
export PERSISTENT_RG_NAME="os4-common"
export TENANT_ID="your-tenant-id"
export SUBSCRIPTION_ID="your-subscription-id"
export OIDC_STORAGE_ACCOUNT_NAME="yourstorageaccount"
export RELEASE_IMAGE="quay.io/openshift-release-dev/ocp-release:XYZ"
# 2. Create persistent resource group (if it doesn't exist)
az group create --name ${PERSISTENT_RG_NAME} --location ${LOCATION}
# 3. Create OIDC issuer
ccoctl azure create-key-pair
ccoctl azure create-oidc-issuer \
--oidc-resource-group-name ${PERSISTENT_RG_NAME} \
--tenant-id ${TENANT_ID} \
--region ${LOCATION} \
--name ${OIDC_STORAGE_ACCOUNT_NAME} \
--subscription-id ${SUBSCRIPTION_ID} \
--public-key-file /path/to/serviceaccount-signer.public
export OIDC_ISSUER_URL="https://${OIDC_STORAGE_ACCOUNT_NAME}.blob.core.windows.net/${OIDC_STORAGE_ACCOUNT_NAME}"
# 4. Create workload identities
hypershift create iam azure \
--name ${NAME} \
--infra-id ${INFRA_ID} \
--azure-creds ${AZURE_CREDS} \
--location ${LOCATION} \
--resource-group-name ${PERSISTENT_RG_NAME} \
--oidc-issuer-url ${OIDC_ISSUER_URL} \
--output-file workload-identities.json
# 5. Create infrastructure using pre-created identities
hypershift create infra azure \
--name ${NAME} \
--infra-id ${INFRA_ID} \
--azure-creds ${AZURE_CREDS} \
--base-domain ${BASE_DOMAIN} \
--location ${LOCATION} \
--workload-identities-file workload-identities.json \
--assign-identity-roles \
--dns-zone-rg-name ${PERSISTENT_RG_NAME} \
--output-file infra-output.yaml
# 6. Create the cluster
hypershift create cluster azure \
--name ${NAME} \
--infra-id ${INFRA_ID} \
--azure-creds ${AZURE_CREDS} \
--base-domain ${BASE_DOMAIN} \
--location ${LOCATION} \
--pull-secret /path/to/pull-secret \
--generate-ssh \
--release-image ${RELEASE_IMAGE} \
--sa-token-issuer-private-key-path /path/to/serviceaccount-signer.private \
--oidc-issuer-url ${OIDC_ISSUER_URL} \
--dns-zone-rg-name ${PERSISTENT_RG_NAME} \
--assign-service-principal-roles \
--infra-json infra-output.yaml \
--diagnostics-storage-account-type Managed
# --- Cleanup ---
# 7. Destroy the cluster
hypershift destroy cluster azure \
--name ${NAME} \
--azure-creds ${AZURE_CREDS} \
--dns-zone-rg-name ${PERSISTENT_RG_NAME}
# 8. Destroy infrastructure
hypershift destroy infra azure \
--name ${NAME} \
--infra-id ${INFRA_ID} \
--azure-creds ${AZURE_CREDS}
# 9. Destroy IAM resources (only if no longer needed)
hypershift destroy iam azure \
--azure-creds ${AZURE_CREDS} \
--workload-identities-file workload-identities.json \
--resource-group-name ${PERSISTENT_RG_NAME} \
--name ${NAME} \
--infra-id ${INFRA_ID} \
--dns-zone-rg-name ${PERSISTENT_RG_NAME}
See Also
- Create Azure Infrastructure Separately
- Self-Managed Azure Overview
- Deploy Azure Private Clusters — End-to-end guide for private endpoint access