Community
crossplane-configuration-azure-workload-identity
By livewyer-ops
Last changed 5 months ago
Compatibility
Crossplane (2.0+)
Upbound Crossplane (UXP) (2.0+)
Languages
Source Code

Notice something off about this package? Help us keep the marketplace safe and trustworthy by reporting inappropriate content or behavior.

Report this package
Overview
Crossplane Composite Resource Definitions (XRDs) package for creating Workload Identity in Azure.

Crossplane Configuration for Azure Workload Identity

A Crossplane Configuration package that simplifies the creation and management of Azure Workload Identity resources, enabling secure authentication between Kubernetes workloads and Azure services using OIDC federation.

Overview

This configuration automates the setup of Azure Workload Identity by creating and managing:

  • Azure User Assigned Managed Identity - The identity that will be used by your Kubernetes workloads
  • Federated Identity Credentials - OIDC federation configuration linking the managed identity to Kubernetes service accounts
  • Azure RBAC Role Assignments - Permissions granted to the managed identity
  • Custom Role Definitions - Support for creating custom Azure roles when needed
  • Kubernetes Service Accounts - Optionally creates service accounts with proper annotations

Prerequisites

Before using this configuration, ensure you have:

  1. Kubernetes Cluster (v1.24+) with:
  • Crossplane v2.0.2 or higher installed
  • OIDC issuer enabled (for AKS, EKS, or self-managed clusters)
  1. Azure Subscription with:
  • Appropriate permissions to create managed identities and role assignments
  • Resource Group where resources will be created
  1. Required Crossplane Providers:
  • provider-family-azure v2+
  • provider-kubernetes v1+
  1. Required Crossplane Functions:
  • function-go-templating v0.10.0+
  • function-auto-ready v0.5.0+

Installation

Step 1: Install Crossplane

If you haven't already installed Crossplane:

kubectl create namespace crossplane-system
helm repo add crossplane-stable https://charts.crossplane.io/stable
helm repo update
helm install crossplane \
  --namespace crossplane-system \
  crossplane-stable/crossplane \
  --version 2.0.2

Step 2: Install Required Providers and Functions

# Install providers
kubectl apply -f examples/providers.yaml

# Install functions
kubectl apply -f examples/functions.yaml

# Wait for providers to become healthy
kubectl wait --for=condition=Healthy \
  --timeout=300s \
  provider.pkg.crossplane.io --all

Step 3: Configure Azure Provider Credentials

Create a secret with your Azure service principal credentials:

# Create a JSON file with your Azure credentials
cat > azure-credentials.json <<EOF
{
  "clientId": "YOUR_CLIENT_ID",
  "clientSecret": "YOUR_CLIENT_SECRET",
  "subscriptionId": "YOUR_SUBSCRIPTION_ID",
  "tenantId": "YOUR_TENANT_ID"
}
EOF

# Create the secret
kubectl create secret generic provider-azure \
  --from-file=credential=./azure-credentials.json \
  --namespace crossplane-system

# Clean up the credentials file
rm azure-credentials.json

Step 4: Install the Configuration

Build and push the configuration package:

# Build the package
crossplane xpkg build

# Login to your registry (if using Upbound)
crossplane xpkg login

# Push the package
crossplane xpkg push -f *.xpkg livewyer-ops/crossplane-configuration-azure-workload-identity:v1.0.0

Or install directly from the registry:

cat <<EOF | kubectl apply -f -
apiVersion: pkg.crossplane.io/v1
kind: Configuration
metadata:
  name: configuration-azure-workload-identity
spec:
  package: xpkg.upbound.io/livewyer-ops/crossplane-configuration-azure-workload-identity:v1.0.0
EOF

Usage

Basic Example

Create a WorkloadIdentity resource with basic role assignments:

apiVersion: azure.livewyer.io/v1alpha1
kind: WorkloadIdentity
metadata:
  name: my-app-identity
  namespace: default
spec:
  forProvider:
    location: eastus
    resourceGroupName: my-resource-group
    serviceAccountName: my-app-sa
    oidcIssuerURL: https://eastus.oic.prod-aks.azure.com/00000000-0000-0000-0000-000000000000/11111111-1111-1111-1111-111111111111/
    roleAssignments:
      - roleDefinitionName: Reader
        scope: /subscriptions/YOUR_SUBSCRIPTION_ID
    tags:
      environment: production
      app: my-app

Advanced Example with Custom Roles

apiVersion: azure.livewyer.io/v1alpha1
kind: WorkloadIdentity
metadata:
  name: storage-operator
  namespace: storage-system
spec:
  forProvider:
    location: westeurope
    resourceGroupName: storage-rg
    serviceAccountName: storage-operator-sa
    serviceAccountTokenExpiration: 3600 # 1 hour
    oidcIssuerURL: https://oidc.eks.eu-west-1.amazonaws.com/id/EXAMPLED539D4633E53DE1B71EXAMPLE
    roleAssignments:
      # Built-in role
      - roleDefinitionName: "Storage Account Contributor"
        scope: /subscriptions/SUB_ID/resourceGroups/storage-rg
        description: "Manage storage accounts in storage-rg"

      # Custom role with specific permissions
      - roleDefinitionName: "CustomBlobReader"
        scope: /subscriptions/SUB_ID
        permissions:
          - actions:
              - "Microsoft.Storage/storageAccounts/listKeys/action"
              - "Microsoft.Storage/storageAccounts/read"
            dataActions:
              - "Microsoft.Storage/storageAccounts/blobServices/containers/blobs/read"
              - "Microsoft.Storage/storageAccounts/blobServices/containers/blobs/write"
            notActions:
              - "Microsoft.Storage/storageAccounts/delete"
    tags:
      team: platform
      cost-center: engineering

Using the Created Service Account in Your Pods

Once the WorkloadIdentity is created, you can use the service account in your pods:

apiVersion: v1
kind: Pod
metadata:
  name: my-app
  namespace: default
spec:
  serviceAccountName: my-app-sa # The service account created by WorkloadIdentity
  containers:
    - name: app
      image: myapp:latest
      env:
        - name: AZURE_CLIENT_ID
          valueFrom:
            fieldRef:
              fieldPath: metadata.annotations['azure.workload.identity/client-id']

Configuration Reference

WorkloadIdentity Spec

FieldTypeRequiredDescription
forProvider.locationstringYesAzure region (e.g., eastus, westeurope)
forProvider.resourceGroupNamestringYesName of the Azure Resource Group
forProvider.oidcIssuerURLstringYesOIDC issuer URL of your Kubernetes cluster
forProvider.roleAssignmentsarrayYesList of role assignments to grant
forProvider.serviceAccountNamestringNoKubernetes service account name to create
forProvider.serviceAccountTokenExpirationintegerNoToken expiration in seconds (default: 86400)
forProvider.tagsobjectNoAzure resource tags
providerConfigRefobjectNoReference to provider configuration

Role Assignment Options

FieldTypeDescription
roleDefinitionNamestringName of built-in or custom role
scopestringAzure resource scope for the assignment
permissionsarrayPermissions for custom role (optional)
permissions[].actionsarrayAllowed management operations
permissions[].dataActionsarrayAllowed data operations
permissions[].notActionsarrayDenied management operations
permissions[].notDataActionsarrayDenied data operations
conditionstringCondition expression for conditional access
conditionVersionstringVersion of condition syntax
principalTypestringType of principal (default: ServicePrincipal)

Testing

Verify Installation

  1. Check that the configuration is installed:
kubectl get configurations
kubectl get xrds | grep workloadidentity
kubectl get compositions | grep workloadidentity
  1. Create a test WorkloadIdentity:
kubectl apply -f examples/workload-identity.yaml
  1. Verify resources are created:
# Check the composite resource
kubectl get workloadidentity -n test

# Check the managed resources
kubectl get managed -n test

# Check Azure resources
kubectl get userassignedidentity -n test
kubectl get federatedidentitycredential -n test
kubectl get roleassignment -n test

Validate Azure Resources

Using Azure CLI:

# List managed identities
az identity list --resource-group example-group

# Check role assignments
az role assignment list --assignee <PRINCIPAL_ID>

# Verify federated credentials
az identity federated-credential list \
  --resource-group example-group \
  --identity-name simple-example

Test Workload Authentication

Deploy a test pod to verify authentication:

apiVersion: v1
kind: Pod
metadata:
  name: test-workload-identity
  namespace: test
spec:
  serviceAccountName: simple-example
  containers:
    - name: azure-cli
      image: mcr.microsoft.com/azure-cli:latest
      command: ["/bin/bash", "-c", "--"]
      args:
        - |
          # Login using workload identity
          az login --federated-token $(cat $AZURE_FEDERATED_TOKEN_FILE) \
            --service-principal -u $AZURE_CLIENT_ID -t $AZURE_TENANT_ID

          # Test access
          az account show
          az group list

Observability

Check Resource Status

# Get detailed status
kubectl describe workloadidentity -n test simple-example

# Check conditions
kubectl get workloadidentity -n test simple-example -o jsonpath='{.status.conditions}'

View Logs

# Crossplane pod logs
kubectl logs -n crossplane-system deployment/crossplane -f

# Provider logs
kubectl logs -n crossplane-system -l pkg.crossplane.io/provider=provider-azure-managedidentity

Troubleshooting

Common Issues

  1. OIDC Issuer URL Incorrect
  • Verify the OIDC issuer URL matches your cluster configuration
  • For AKS: az aks show --name <cluster> --resource-group <rg> --query "oidcIssuerProfile.issuerUrl"
  • For EKS: aws eks describe-cluster --name <cluster> --query "cluster.identity.oidc.issuer"
  1. Role Assignment Failures
  • Ensure the service principal has permissions to create role assignments
  • Check that the scope exists and is correctly formatted
  • Verify custom role permissions are valid
  1. Service Account Not Created
  • Check that the Kubernetes provider is healthy
  • Verify provider configuration references are correct
  • Check namespace exists
  1. Resources Stuck in Creating State
  • Review provider pod logs for errors
  • Check Azure API quotas and limits
  • Verify network connectivity to Azure

Debug Commands

# Get all events
kubectl get events -n test --sort-by='.lastTimestamp'

# Check composition pipeline
kubectl get composite -n test simple-example -o yaml | less

# View rendered resources
kubectl get managed -n test -o yaml | grep -A 5 "forProvider"

Cleanup

To remove a WorkloadIdentity and all its resources:

kubectl delete workloadidentity -n test simple-example

To uninstall the configuration:

kubectl delete configuration configuration-azure-workload-identity

Contributing

We welcome contributions! Please see our Contributing Guide for details.

Development

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Update examples and documentation
  5. Test your changes
  6. Submit a pull request

Testing Locally

# Build the package locally
crossplane xpkg build

# Install for testing
kubectl apply -f apis/definition.yaml
kubectl apply -f apis/composition.yaml

# Test with examples
kubectl apply -f examples/workload-identity.yaml

Support

License

This project is licensed under the MIT License - see the LICENSE file for details.

Maintainers

Acknowledgments

  • Crossplane for the amazing framework
  • Upbound for provider implementations
  • Azure Workload Identity documentation and examples
Discover the building blocks for your internal cloud platform.
© 2026 Upbound, Inc.
Solutions
Learn
Company
Community
More