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