Skip to content

Latest commit

 

History

History
231 lines (181 loc) · 5.29 KB

File metadata and controls

231 lines (181 loc) · 5.29 KB

Azure Key Vault + AKS + Python App using Microsoft Entra Workload Identity

This guide demonstrates how to access Azure Key Vault secrets from a Python app running in AKS, using Microsoft Entra Workload Identity for secure identity federation.


✨ Features

  • Uses Microsoft Entra Workload Identity
  • Pod assumes identity securely without needing node identity
  • Access Azure Key Vault using DefaultAzureCredential
  • Least-privilege RBAC using federated credentials

👀 Prerequisites

  • Azure CLI (2.45.0+)
  • kubectl
  • OIDC-enabled AKS cluster
  • Azure subscription with Owner/Contributor rights

⚙️ Step-by-Step Setup

1. Define Environment Variables

cat <<EOF > env.sh
export LOCATION="southeastasia"
export RESOURCE_GROUP="aks-wi-demo-rg"
export AKS_NAME="akswi$(openssl rand -hex 3 | tr -dc 'a-z0-9' | cut -c1-10)"
export KEYVAULT_NAME="kvwi$(openssl rand -hex 3 | tr -dc 'a-z0-9' | cut -c1-20)"
export SECRET_NAME="DemoSecret"
export SECRET_VALUE="SuperSecret123"
export APP_NAME="kv-reader"
export IMAGE_NAME="kv-reader"
export ACR_NAME="acrwi$(openssl rand -hex 3 | tr -dc 'a-z0-9' | cut -c1-20)"
export IDENTITY_NAME="uami-kv-access"
export SERVICE_ACCOUNT_NAME="workload-identity-sa"
export NAMESPACE="default"
EOF
source env.sh

2. Create Resource Group, AKS and Enable OIDC Issuer

az group create --name $RESOURCE_GROUP --location $LOCATION
az aks create \
  --name $AKS_NAME \
  --resource-group $RESOURCE_GROUP \
  --enable-oidc-issuer \
  --enable-workload-identity \
  --node-count 1 \
  --generate-ssh-keys
az aks get-credentials --name $AKS_NAME --resource-group $RESOURCE_GROUP

3. Create Azure Key Vault and Secret

az keyvault create \
  --name $KEYVAULT_NAME \
  --resource-group $RESOURCE_GROUP \
  --location $LOCATION \
  --enable-rbac-authorization true

az keyvault secret set \
  --vault-name $KEYVAULT_NAME \
  --name $SECRET_NAME \
  --value "$SECRET_VALUE"

4. Create User Assigned Managed Identity

az identity create \
  --name $IDENTITY_NAME \
  --resource-group $RESOURCE_GROUP \
  --location $LOCATION

Store client ID:

IDENTITY_CLIENT_ID=$(az identity show --name $IDENTITY_NAME --resource-group $RESOURCE_GROUP --query clientId -o tsv)

5. Grant Identity Access to Key Vault

IDENTITY_PRINCIPAL_ID=$(az identity show --name $IDENTITY_NAME --resource-group $RESOURCE_GROUP --query principalId -o tsv)

az role assignment create \
  --assignee-object-id $IDENTITY_PRINCIPAL_ID \
  --role "Key Vault Secrets User" \
  --scope $(az keyvault show --name $KEYVAULT_NAME --query id -o tsv)

6. Create Federated Identity Credential

AKS_OIDC_ISSUER=$(az aks show -g $RESOURCE_GROUP -n $AKS_NAME --query "oidcIssuerProfile.issuerUrl" -o tsv)

az identity federated-credential create \
  --name federated-cred \
  --identity-name $IDENTITY_NAME \
  --resource-group $RESOURCE_GROUP \
  --issuer $AKS_OIDC_ISSUER \
  --subject "system:serviceaccount:$NAMESPACE:$SERVICE_ACCOUNT_NAME" \
  --audiences api://AzureADTokenExchange

🐍 Python App

app.py

from azure.identity import DefaultAzureCredential
from azure.keyvault.secrets import SecretClient
import os
import time

vault_name = os.getenv("KEY_VAULT_NAME")
secret_name = os.getenv("SECRET_NAME")
vault_url = f"https://{vault_name}.vault.azure.net"

credential = DefaultAzureCredential()
client = SecretClient(vault_url=vault_url, credential=credential)

retrieved = client.get_secret(secret_name)
print(f"Secret value: {retrieved.value}")

# Wait indefinitely
print("App is running. Press Ctrl+C to exit.")
while True:
    time.sleep(60)

Dockerfile

FROM python:3.10-slim
WORKDIR /app
COPY app.py .
RUN pip install azure-identity azure-keyvault-secrets
CMD ["python", "app.py"]

📦 Build and Push Docker Image

az acr create --resource-group $RESOURCE_GROUP --name $ACR_NAME --sku Basic
az aks update -n $AKS_NAME -g $RESOURCE_GROUP --attach-acr $ACR_NAME
az acr login --name $ACR_NAME

docker build -t $ACR_NAME.azurecr.io/$IMAGE_NAME:v1 .
docker push $ACR_NAME.azurecr.io/$IMAGE_NAME:v1

☸️ Kubernetes Deployment

Create Service Account and Deployment

apiVersion: v1
kind: ServiceAccount
metadata:
  name: $SERVICE_ACCOUNT_NAME
  annotations:
    azure.workload.identity/client-id: "$IDENTITY_CLIENT_ID"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: kv-reader
spec:
  replicas: 1
  selector:
    matchLabels:
      app: kv-reader
  template:
    metadata:
      labels:
        app: kv-reader
    spec:
      serviceAccountName: $SERVICE_ACCOUNT_NAME
      containers:
      - name: kv-reader
        image: $ACR_NAME.azurecr.io/$IMAGE_NAME:v1
        env:
        - name: KEY_VAULT_NAME
          value: "$KEYVAULT_NAME"
        - name: SECRET_NAME
          value: "$SECRET_NAME"

Apply it:

envsubst < deployment.yaml | kubectl apply -f -

🔍 Verify Output

kubectl get pods
kubectl logs $(kubectl get pods -l app=kv-reader -o jsonpath="{.items[0].metadata.name}")

Expected output:

Secret value: SuperSecret123

🧹 Cleanup

az group delete --name $RESOURCE_GROUP --yes --no-wait

You have now successfully set up AKS to access Azure Key Vault using Microsoft Entra Workload Identity.