Azure-Managed-Identities-Workload-Identity-Federation-Azure-Kubernetes-Services

Getting an access token inside an aks pod using Azure Managed Identities with Workload Identity Federation and powershell

 

AAD Pod Identity is deprecated (https://github.com/Azure/aad-pod-identity) , since 10/24/2022, AAD Pod Identity has been replaced with Azure Workload Identity preview (https://cloudblogs.microsoft.com/opensource/2022/01/18/announcing-azure-active-directory-azure-ad-workload-identity-for-kubernetes/)

 

In this tutorial, I will walk through the steps to  get an access token inside a pod using managed identity  with Workload Identity Federation and powershell

 

On a virtual machine, we can  assign a managed identity and then use it, since the entire VM is a single azure resource.

 

In the case of a Kubernetes cluster, since there are multiple users on the cluster, across multiple namespaces, using a managed identity in a pod is made available through a new feature called Azure Workload Identity for Kubernetes.
 
The Microsoft documentation is available here here: Use an Azure AD workload identities (preview) on Azure Kubernetes Service (AKS) – Azure Kubernetes Service | Microsoft Learn
 
We can use an Azure AD workload identities (preview) on Azure Kubernetes Service (AKS) to run jobs in an aks cluster using managed identity.

 

A pod will have several environment variables injected into it such as AZURE_CLIENT_ID, AZURE_TENANT_ID and AZURE_FEDERATED_TOKEN_FILE

Login to azure using a service principal

To deploy the infrastructure, we should connect to azure using credentials with enough priviledge usin az login , azure cloud shell or service principal

export CLIENT_ID=YOUR_SERVICE_PRINCIPAL_CLIENT_ID
export CLIENT_SECRET=YOUR_SERVICE_PRINCIPAL_CLIENT_SECRET
export TENANT_ID=YOUR_SERVICE_PRINCIPAL_TENANT_ID
export SUBSCRIPTION=YOUR_AZURE_SUBCRIPTION_ID

# login to azure using a service principal
az login --service-principal -u $CLIENT_ID -p $CLIENT_SECRET --tenant $TENANT_ID
az account set --name $SUBSCRIPTION

Install the aks-preview Azure CLI extension and Register the EnableWorkloadIdentityPreview’ feature flag

echo "install the aks-preview extension"
# install the aks-preview extension

az extension add --name aks-preview

echo "update the aks-preview extension"
# update the aks-preview extension
az extension update --name aks-preview

# Register the 'EnableWorkloadIdentityPreview' feature flag
echo "Register the 'EnableWorkloadIdentityPreview' feature flag"
az feature register --namespace "Microsoft.ContainerService" --name "EnableWorkloadIdentityPreview"

# Verify the registration status
echo "Verify the registration status"
az feature show --namespace "Microsoft.ContainerService" --name "EnableWorkloadIdentityPreview"

Deploy an AKS cluster using the Azure CLI with OpenID Connect Issuer and managed identity

login to your azure account and set your Azure default Subscription

# Create resource group
echo "Creating resource group"
az group create -l $LOCATION -n $RESOURCE_GROUP

# Deploy an AKS cluster using the Azure CLI with OpenID Connect Issuer and managed identity.
echo "Deploy an AKS cluster using the Azure CLI with OpenID Connect Issuer and managed identity."
az aks create -g $RESOURCE_GROUP -n $AKS_CLUSTER_NAME --node-count 1 --enable-oidc-issuer --enable-workload-identity --generate-ssh-keys

### Update an existing AKS cluster using the Azure CLI with OpenID Connect Issuer and managed identity.
### az aks update -n $AKS_CLUSTER_NAME -g $RESOURCE_GROUP --enable-oidc-issuer --enable-workload-identity

Create a managed identity and grant permissions to access the secret

echo "Creating a managed identity and grant permissions to access the secret"
az identity create --name "${UAID}" --resource-group "${RESOURCE_GROUP}"  \
 --location "${LOCATION}" --subscription "${SUBSCRIPTION}"

To get the OIDC Issuer URL and user assigned managed identity and save it to an environmental variable

cho -e "\033[41m Getting the OIDC Issuer URL and user assigned managed identity and save it to an environmental variable \033[0m"
export USER_ASSIGNED_CLIENT_ID="$(az identity show --resource-group "${RESOURCE_GROUP}" --name "${UAID}" --query 'clientId' -otsv)"
export AKS_OIDC_ISSUER="$(az aks show -n $AKS_CLUSTER_NAME -g $RESOURCE_GROUP --query "oidcIssuerProfile.issuerUrl" -otsv)"

echo "USER_ASSIGNED_CLIENT_ID = $USER_ASSIGNED_CLIENT_ID"
echo "AKS_OIDC_ISSUER = $AKS_OIDC_ISSUER"

Establish federated identity credential

echo -e "\033[41m Establishing federated identity credential \033[0m"
 az identity federated-credential create --name ${FICID} --identity-name ${UAID} --resource-group ${RESOURCE_GROUP} --issuer ${AKS_OIDC_ISSUER} --subject system:serviceaccount:${SERVICE_ACCOUNT_NAMESPACE}:${SERVICE_ACCOUNT_NAME}

az aks get-credentials -g $RESOURCE_GROUP -n $AKS_CLUSTER_NAME

Create an azure container registry . The name must be globally unique.

echo -e "\033[41m Creating azure container registry \033[0m"
az acr create -n $ACR_NAME -g $RESOURCE_GROUP --sku basic

while [ $(az acr show --name $ACR_NAME -g $RESOURCE_GROUP --query "provisioningState" -o tsv) != "Succeeded" ]
do
  echo "Waiting for ACR $ACR_NAME to be fully provisioned..."
  sleep 5s
done

echo "ACR $ACR_NAME is now fully provisioned."

Assigning acrpull role to azure kubernetes service

 echo "Assigning acrpull role to azure kubernetes service"
 ACR_ID=$(az acr show --name $ACR_NAME --resource-group $RESOURCE_GROUP --query "id" --output tsv)
 echo $ACR_ID
 MANAGED_IDENTITY_CLIENT_ID=$(az aks show --resource-group $RESOURCE_GROUP --name $AKS_CLUSTER_NAME --query "identityProfile.kubeletidentity.clientId" --output tsv)
 echo $MANAGED_IDENTITY_CLIENT_ID
 az role assignment create --assignee $MANAGED_IDENTITY_CLIENT_ID --role acrpull --scope $ACR_ID
 az aks check-acr --resource-group $RESOURCE_GROUP --name $AKS_CLUSTER_NAME --acr "$ACR_NAME.azurecr.io"

Creating service account

echo -e "\033[41m Creating service account \033[0m"

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    azure.workload.identity/client-id: ${USER_ASSIGNED_CLIENT_ID}
  labels:
    azure.workload.identity/use: "true"
  name: ${SERVICE_ACCOUNT_NAME}
  namespace: ${SERVICE_ACCOUNT_NAMESPACE}
EOF

# echo -e "\033[41m Checking wether service account is creates successfully \033[0m"
kubectl get ServiceAccount 
kubectl describe  ServiceAccount ${SERVICE_ACCOUNT_NAME}

Login to azure azure container registry, Build docker image and push docker image

# Login to azure azure container registry
 echo -e "\033[41m Login to azure azure container registry \033[0m"
 az acr login --name $ACR_NAME
 az acr login -n $ACR_NAME --expose-token

 # Building docker image
 docker build . -t "${ACR_NAME}.azurecr.io/$IMAGE_NAME:$IMAGE_TAG"

 docker push "${ACR_NAME}.azurecr.io/$IMAGE_NAME:$IMAGE_TAG"

Deploying a pod using powershell  image

The powershell script

# Here is the content of my-script.ps1 file
Import-Module Az.Accounts

# Retrieve the managed identity's access token
Write-Host "AZURE_AUTHORITY_HOST :  $Env:AZURE_AUTHORITY_HOST"
Write-Host "AZURE_CLIENT_ID : $Env:AZURE_CLIENT_ID"
Write-Host "AZURE_TENANT_ID :  $Env:AZURE_TENANT_ID"
Write-Host "AZURE_FEDERATED_TOKEN_FILE :  $Env:AZURE_FEDERATED_TOKEN_FILE"

$azureAdTokenExchange=Get-Content $Env:AZURE_FEDERATED_TOKEN_FILE -Raw

Write-Host " AZURE AD TOKEN EXCHANGE : $azureAdTokenExchange"

Connect-AzAccount -ApplicationId $Env:AZURE_CLIENT_ID -TenantId $Env:AZURE_TENANT_ID -FederatedToken $azureAdTokenExchange

$azureAccessToken = Get-AzAccessToken -ResourceUrl "https://management.core.windows.net/"  | ConvertTo-Json

Write-Host " jwt token with audience https://management.core.windows.net  : $azureAccessToken"

The docker file

# Use a base image with PowerShell installed
FROM mcr.microsoft.com/powershell:7.2.0-ubuntu-20.04

# Copy the PowerShell script into the image
COPY my-script.ps1 /app/my-script.ps1

# install dependencies
RUN pwsh -c "&{ Install-Module Az.Accounts -Force }"
RUN pwsh -c "&{ Get-Module -Name Az.Accounts -All }"


# Set the working directory to the location of the script
WORKDIR /app

# Set the default command to run the script
CMD [ "pwsh", "-command", "./my-script.ps1" ]

The pod deployment file

echo -e "\033[41m Deploying a pod using powershell  image \033[0m"

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: my-powershell-pod
  labels:
    azure.workload.identity/use: "true"
spec:
  serviceAccountName: ${SERVICE_ACCOUNT_NAME}
  containers:
  - name: my-powershell-container
    image: ${ACR_NAME}.azurecr.io/$IMAGE_NAME:$IMAGE_TAG
    command: [ "pwsh", "-command", "./my-script.ps1" ]
    
  restartPolicy: Never
EOF


# echo -e "\033[41m Checking wether my-powershell-pod is creates successfully \033[0m"
kubectl get pod my-powershell-pod 
kubectl describe  pod  my-powershell-pod

 #wait until pod is completed 
 kubectl wait --for=condition=complete --timeout=60s pod/my-powershell-pod

Getting logs

kubectl logs my-powershell-pod 
 

Inspecting token using jwt.ms

Code source is available here : 

Thanks for reading, if you have any feedback, feel free to post it

Gora LEYE

I'm a microsoft most valuable professional (MVP) .NET Architect and Technical Expert skills located in Paris (FRANCE). The purpose of this blog is mainly to post general .NET tips and tricks, www.masterconduite.com Gora LEYE

Support us

BMC logoBuy me a coffee