From ae56ac9b60d5363fe453509d1d42097f7dc08c4f Mon Sep 17 00:00:00 2001 From: Paolo Salvatori Date: Wed, 24 Jun 2026 16:29:01 +0200 Subject: [PATCH] Add Samples for Azure Key Vault Provider for Secrets Store CSI Driver --- README.md | 1 + samples/key-vault-csi-driver/00-variables.sh | 25 ++ samples/key-vault-csi-driver/README.md | 83 +++++++ .../00-variables.sh | 11 + .../01-enable-addon.sh | 28 +++ .../02-create-key-vault-and-secrets.sh | 175 ++++++++++++++ .../03-create-role-assignment.sh | 99 ++++++++ .../04-create-secret-provider-class.sh | 66 +++++ .../05-create-demo-pod.sh | 39 +++ .../06-list-secrets.sh | 23 ++ .../workload-identity/00-variables.sh | 12 + .../workload-identity/01-enable-addon.sh | 28 +++ .../02-create-key-vault-and-secrets.sh | 175 ++++++++++++++ .../03-create-managed-identity.sh | 227 ++++++++++++++++++ .../04-create-secret-provider-class.sh | 50 ++++ .../workload-identity/05-create-demo-pod.sh | 42 ++++ .../workload-identity/06-list-secrets.sh | 23 ++ .../scripts/01-deploy-resources.sh | 8 +- .../01-system-assigned-managed-identity.sh | 146 ++++++++++- scripts/01-user-assigned-managed-identity.sh | 55 ++++- 20 files changed, 1302 insertions(+), 14 deletions(-) create mode 100644 samples/key-vault-csi-driver/00-variables.sh create mode 100644 samples/key-vault-csi-driver/README.md create mode 100755 samples/key-vault-csi-driver/user-assigned-managed-identity/00-variables.sh create mode 100755 samples/key-vault-csi-driver/user-assigned-managed-identity/01-enable-addon.sh create mode 100755 samples/key-vault-csi-driver/user-assigned-managed-identity/02-create-key-vault-and-secrets.sh create mode 100755 samples/key-vault-csi-driver/user-assigned-managed-identity/03-create-role-assignment.sh create mode 100755 samples/key-vault-csi-driver/user-assigned-managed-identity/04-create-secret-provider-class.sh create mode 100755 samples/key-vault-csi-driver/user-assigned-managed-identity/05-create-demo-pod.sh create mode 100755 samples/key-vault-csi-driver/user-assigned-managed-identity/06-list-secrets.sh create mode 100755 samples/key-vault-csi-driver/workload-identity/00-variables.sh create mode 100755 samples/key-vault-csi-driver/workload-identity/01-enable-addon.sh create mode 100755 samples/key-vault-csi-driver/workload-identity/02-create-key-vault-and-secrets.sh create mode 100755 samples/key-vault-csi-driver/workload-identity/03-create-managed-identity.sh create mode 100755 samples/key-vault-csi-driver/workload-identity/04-create-secret-provider-class.sh create mode 100755 samples/key-vault-csi-driver/workload-identity/05-create-demo-pod.sh create mode 100755 samples/key-vault-csi-driver/workload-identity/06-list-secrets.sh diff --git a/README.md b/README.md index e75535c..8dffe01 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,7 @@ All samples implement the same Vacation Planner web app. They only vary the unde | [web-app-cosmosdb-nosql-api](samples/web-app-cosmosdb-nosql-api/) | Stores activities in a container of an [Azure Cosmos DB for NoSQL](https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/) account. | | [web-app-storage-account](samples/web-app-storage-account/) | Stores activities in an [Azure Blob Storage](https://learn.microsoft.com/en-us/azure/storage/blobs/storage-blobs-introduction) container, using a connection string. | | [web-app-managed-identity](samples/web-app-managed-identity/) | Stores activities in an Azure Blob Storage container, authenticating with [Microsoft Entra Workload ID](https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview) (federated credential plus workload identity) instead of a secret, and optionally exposes the app through the Gateway API with a managed TLS certificate. | +| [key-vault-csi-driver](samples/key-vault-csi-driver/) | Mounts secrets from [Azure Key Vault](https://learn.microsoft.com/en-us/azure/key-vault/general/overview) into a pod with the [Azure Key Vault provider for Secrets Store CSI Driver](https://learn.microsoft.com/en-us/azure/aks/csi-secrets-store-driver), demonstrating both the Microsoft Entra Workload ID and the user-assigned managed identity access modes. | Each sample folder follows the same layout: diff --git a/samples/key-vault-csi-driver/00-variables.sh b/samples/key-vault-csi-driver/00-variables.sh new file mode 100644 index 0000000..df7f9c8 --- /dev/null +++ b/samples/key-vault-csi-driver/00-variables.sh @@ -0,0 +1,25 @@ +# Azure Kubernetes Service (AKS) +PREFIX="local" +SUFFIX="test" +AKS_NAME="${PREFIX}-aks-${SUFFIX}" +AKS_RESOURCE_GROUP_NAME="${PREFIX}-rg" + +# Azure Key Vault +KEY_VAULT_NAME="${PREFIX}-kv-${SUFFIX}" +KEY_VAULT_RESOURCE_GROUP_NAME="${PREFIX}-rg" +KEY_VAULT_SKU="Standard" +LOCATION="WestEurope" # Choose a location + +# Secrets and Values +SECRETS=("username" "password") +VALUES=("admin" "trustno1!") + +# Azure Subscription and Tenant +TENANT_ID=$(az account show --query tenantId --output tsv --only-show-errors) +SUBSCRIPTION_NAME=$(az account show --query name --output tsv --only-show-errors) +SUBSCRIPTION_ID=$(az account show --query id --output tsv --only-show-errors) +ENVIRONMENT_NAME=$(az account show --query environmentName --output tsv --only-show-errors) + +# Others +RETRY_COUNT=5 +SLEEP=3 \ No newline at end of file diff --git a/samples/key-vault-csi-driver/README.md b/samples/key-vault-csi-driver/README.md new file mode 100644 index 0000000..3e902d2 --- /dev/null +++ b/samples/key-vault-csi-driver/README.md @@ -0,0 +1,83 @@ +## Azure Key Vault Provider for Secrets Store CSI Driver in AKS + +The [Azure Key Vault provider for Secrets Store CSI Driver](https://learn.microsoft.com/en-us/azure/aks/csi-secrets-store-driver) enables retrieving secrets, keys, and certificates stored in Azure Key Vault and accessing them as files from mounted volumes in an AKS cluster. This method eliminates the need for Azure-specific libraries to access the secrets. + +This [Secret Store CSI Driver for Key Vault](https://github.com/Azure/secrets-store-csi-driver-provider-azure) offers the following features: + +- Mounts secrets, keys, and certificates to a pod using a CSI volume. +- Supports CSI inline volumes. +- Allows the mounting of multiple secrets store objects as a single volume. +- Offers pod portability with the SecretProviderClass CRD. +- Compatible with Windows containers. +- Keeps in sync with Kubernetes secrets. +- Supports auto-rotation of mounted contents and synced Kubernetes secrets. + +When auto-rotation is enabled for the Azure Key Vault Secrets Provider, it automatically updates both the pod mount and the corresponding Kubernetes secret defined in the **secretObjects** field of SecretProviderClass. It continuously polls for changes based on the rotation poll interval (default is two minutes). + +If a secret in an external secrets store is updated after the initial deployment of the pod, both the Kubernetes Secret and the pod mount will periodically update, depending on how the application consumes the secret data. Here are the recommended approaches for different scenarios: + +1. Mount the Kubernetes Secret as a volume: Utilize the auto-rotation and sync K8s secrets features of Secrets Store CSI Driver. The application should monitor changes from the mounted Kubernetes Secret volume. When the CSI Driver updates the Kubernetes Secret, the volume contents will be automatically updated. +2. Application reads data from the container filesystem: Take advantage of the rotation feature of Secrets Store CSI Driver. The application should monitor file changes from the volume mounted by the CSI driver. +3. Use the Kubernetes Secret for an environment variable: Restart the pod to acquire the latest secret as an environment variable. You can use tools like Reloader to watch for changes on the synced Kubernetes Secret and perform rolling upgrades on pods. + +### Advantages + +- Secrets, keys, and certificates can be accessed as files from mounted volumes. +- Optionally, Kubernetes secrets can be created to store keys, secrets, and certificates from Key Vault. +- No need for Azure-specific libraries to access secrets. +- Simplifies secret management with transparent integration. + +### Disadvantages + +- Still requires accessing managed services such as Azure Service Bus or Azure Storage using their own connection strings from Azure Key Vault. +- Cannot utilize Microsoft Entra ID integrated security and managed identities for accessing managed services. + +## Identity Access Modes + +The Azure Key Vault provider for Secrets Store CSI Driver supports more than one way to authenticate to Azure Key Vault. This folder contains two samples, each demonstrating a different identity access mode. Both samples mount the same `username` and `password` secrets into a demo nginx pod, but they differ in how the CSI driver obtains the credentials used to read the secrets from Key Vault. + +| Sample | Identity Access Mode | Identity Used | +| --- | --- | --- | +| [`workload-identity`](./workload-identity) | Microsoft Entra Workload ID | A customer-created user-assigned managed identity federated with the pod's Kubernetes service account | +| [`user-assigned-managed-identity`](./user-assigned-managed-identity) | User-assigned managed identity | The user-assigned managed identity created by the resource provider in the node resource group when enabling the addon | + +### Workload Identity + +The [`workload-identity`](./workload-identity) sample uses a customer-created user-assigned managed identity that is consumed by the pod through [Microsoft Entra Workload ID with Azure Kubernetes Service (AKS)](https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview?tabs=dotnet), as described in [Configure Workload Identity to access Key Vault](https://github.com/Azure/secrets-store-csi-driver-provider-azure/blob/d5f7cf5b598c2eede99ad3683de0ba10f7a8736b/website/content/en/configurations/identity-access-modes/workload-identity-mode.md). + +With this approach, a managed identity is created and a federated identity credential establishes trust between the AKS OIDC issuer and a Kubernetes service account. The service account is annotated with the managed identity client ID and labeled to use workload identity, and the demo pod references this service account. At runtime, the pod exchanges its projected service account token for an Entra ID token, which the CSI driver uses to read the secrets from Key Vault. This is the recommended, more secure, and portable approach. + +### User-assigned Managed Identity + +The [`user-assigned-managed-identity`](./user-assigned-managed-identity) sample uses the user-assigned managed identity that the resource provider automatically creates in the **node resource group** when the Azure Key Vault Secrets Provider addon is enabled on the cluster, as described in [Configure User-assigned Managed Identity to access Key Vault](https://github.com/Azure/secrets-store-csi-driver-provider-azure/blob/d5f7cf5b598c2eede99ad3683de0ba10f7a8736b/website/content/en/configurations/identity-access-modes/user-assigned-msi-mode.md). + +With this approach, no additional managed identity or federated credential is created. Instead, the addon's built-in `azureKeyvaultSecretsProvider` identity is granted access to the Key Vault, and the `SecretProviderClass` references it via `useVMManagedIdentity` and `userAssignedIdentityID`. The demo pod does not require a workload identity service account or labels. + +## Scripts + +### `user-assigned-managed-identity` + +- `00-variables.sh`: Sources parent variables and defines Kubernetes namespace, SecretProviderClass name, and pod name for this authentication approach. +- `01-enable-addon.sh`: Enables the Azure Key Vault Secrets Provider addon on the AKS cluster with secret rotation enabled (if not already active). +- `02-create-key-vault-and-secrets.sh`: Creates the resource group and Azure Key Vault, then populates it with test secrets (username and password). +- `03-create-role-assignment.sh`: Creates RBAC role assignments to grant the Key Vault Secrets Provider system-managed identity permission to read secrets from the Key Vault. +- `04-create-secret-provider-class.sh`: Creates the SecretProviderClass resource that configures the CSI driver to retrieve secrets from Key Vault using the system-managed identity. +- `05-create-demo-pod.sh`: Creates a demo nginx pod that mounts the secrets as a volume using the CSI driver. +- `06-list-secrets.sh`: Lists and displays the contents of secrets that were successfully mounted in the pod. + +### `workload-identity` + +- `00-variables.sh`: Sources parent variables and defines Kubernetes namespace, service account, SecretProviderClass name, and pod name for this authentication approach. +- `01-enable-addon.sh`: Enables the Azure Key Vault Secrets Provider addon on the AKS cluster with secret rotation enabled (if not already active). +- `02-create-key-vault-and-secrets.sh`: Creates the resource group and Azure Key Vault, then populates it with test secrets (username and password). +- `03-create-managed-identity.sh`: Creates the resource group and an Azure managed identity configured for Workload Identity Federation with the AKS cluster. +- `04-create-secret-provider-class.sh`: Creates the SecretProviderClass resource that configures the CSI driver to retrieve secrets using Workload Identity Federation. +- `05-create-demo-pod.sh`: Creates a demo nginx pod with workload identity labels and service account that mounts secrets via the CSI driver volume. +- `06-list-secrets.sh`: Lists and displays the contents of secrets that were successfully mounted in the pod. + +### Resources + +- [Using the Azure Key Vault Provider for Secrets Store CSI Driver in AKS](https://learn.microsoft.com/en-us/azure/aks/csi-secrets-store-driver) +- [Access Azure Key Vault with the CSI Driver Identity Provider](https://learn.microsoft.com/en-us/azure/aks/csi-secrets-store-identity-access?tabs=azure-portal&pivots=access-with-service-connector) +- [Configuration and Troubleshooting Options for Azure Key Vault Provider in AKS](https://learn.microsoft.com/en-us/azure/aks/csi-secrets-store-configuration-options) +- [Azure Key Vault Provider for Secrets Store CSI Driver](https://github.com/Azure/secrets-store-csi-driver-provider-azure) diff --git a/samples/key-vault-csi-driver/user-assigned-managed-identity/00-variables.sh b/samples/key-vault-csi-driver/user-assigned-managed-identity/00-variables.sh new file mode 100755 index 0000000..a5b8416 --- /dev/null +++ b/samples/key-vault-csi-driver/user-assigned-managed-identity/00-variables.sh @@ -0,0 +1,11 @@ +# Variables +source ../00-variables.sh + +# Azure Managed Identity +MANAGED_IDENTITY_NAME="${PREFIX}-identity-${SUFFIX}" +FEDERATED_IDENTITY_NAME="federated-identity" + +# Kubernetes +NAMESPACE="mi-secret-store-test" +SECRET_PROVIDER_CLASS_NAME="demo-secret-provider-class" +POD_NAME="demo-pod" \ No newline at end of file diff --git a/samples/key-vault-csi-driver/user-assigned-managed-identity/01-enable-addon.sh b/samples/key-vault-csi-driver/user-assigned-managed-identity/01-enable-addon.sh new file mode 100755 index 0000000..8b543ad --- /dev/null +++ b/samples/key-vault-csi-driver/user-assigned-managed-identity/01-enable-addon.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# For more information, see: +# https://learn.microsoft.com/en-us/azure/aks/csi-secrets-store-driver +# https://learn.microsoft.com/en-us/azure/aks/csi-secrets-store-identity-access + +# Variables +source ./00-variables.sh + +# Enable Addon +echo "Checking if the [azure-keyvault-secrets-provider] addon is enabled in the [$AKS_NAME] AKS cluster..." +az aks addon show \ + --addon azure-keyvault-secrets-provider \ + --name $AKS_NAME \ + --resource-group $AKS_RESOURCE_GROUP_NAME &>/dev/null + +if [[ $? != 0 ]]; then + echo "The [azure-keyvault-secrets-provider] addon is not enabled in the [$AKS_NAME] AKS cluster" + echo "Enabling the [azure-keyvault-secrets-provider] addon in the [$AKS_NAME] AKS cluster..." + + az aks addon enable \ + --addon azure-keyvault-secrets-provider \ + --enable-secret-rotation \ + --name $AKS_NAME \ + --resource-group $AKS_RESOURCE_GROUP_NAME +else + echo "The [azure-keyvault-secrets-provider] addon is already enabled in the [$AKS_NAME] AKS cluster" +fi \ No newline at end of file diff --git a/samples/key-vault-csi-driver/user-assigned-managed-identity/02-create-key-vault-and-secrets.sh b/samples/key-vault-csi-driver/user-assigned-managed-identity/02-create-key-vault-and-secrets.sh new file mode 100755 index 0000000..5e6c896 --- /dev/null +++ b/samples/key-vault-csi-driver/user-assigned-managed-identity/02-create-key-vault-and-secrets.sh @@ -0,0 +1,175 @@ +#!/bin/bash + +# Variables +source ./00-variables.sh + +# Check if the resource group already exists +echo "Checking if [$KEY_VAULT_RESOURCE_GROUP_NAME] resource group actually exists in the [$SUBSCRIPTION_NAME] subscription..." + +az group show --name $KEY_VAULT_RESOURCE_GROUP_NAME &>/dev/null + +if [[ $? != 0 ]]; then + echo "No [$KEY_VAULT_RESOURCE_GROUP_NAME] resource group actually exists in the [$SUBSCRIPTION_NAME] subscription" + echo "Creating [$KEY_VAULT_RESOURCE_GROUP_NAME] resource group in the [$SUBSCRIPTION_NAME] subscription..." + + # create the resource group + az group create --name $KEY_VAULT_RESOURCE_GROUP_NAME --location $LOCATION 1>/dev/null + + if [[ $? == 0 ]]; then + echo "[$KEY_VAULT_RESOURCE_GROUP_NAME] resource group successfully created in the [$SUBSCRIPTION_NAME] subscription" + else + echo "Failed to create [$KEY_VAULT_RESOURCE_GROUP_NAME] resource group in the [$SUBSCRIPTION_NAME] subscription" + exit + fi +else + echo "[$KEY_VAULT_RESOURCE_GROUP_NAME] resource group already exists in the [$SUBSCRIPTION_NAME] subscription" +fi + +# Check if the key vault already exists +echo "Checking if [$KEY_VAULT_NAME] key vault actually exists in the [$SUBSCRIPTION_NAME] subscription..." + +az keyvault show --name $KEY_VAULT_NAME --resource-group $KEY_VAULT_RESOURCE_GROUP_NAME &>/dev/null + +if [[ $? != 0 ]]; then + echo "No [$KEY_VAULT_NAME] key vault actually exists in the [$SUBSCRIPTION_NAME] subscription" + echo "Creating [$KEY_VAULT_NAME] key vault in the [$SUBSCRIPTION_NAME] subscription..." + + # create the key vault + az keyvault create \ + --name $KEY_VAULT_NAME \ + --resource-group $KEY_VAULT_RESOURCE_GROUP_NAME \ + --location $LOCATION \ + --enabled-for-deployment \ + --enabled-for-disk-encryption \ + --enabled-for-template-deployment \ + --sku $KEY_VAULT_SKU 1>/dev/null + + if [[ $? == 0 ]]; then + echo "[$KEY_VAULT_NAME] key vault successfully created in the [$SUBSCRIPTION_NAME] subscription" + else + echo "Failed to create [$KEY_VAULT_NAME] key vault in the [$SUBSCRIPTION_NAME] subscription" + exit + fi +else + echo "[$KEY_VAULT_NAME] key vault already exists in the [$SUBSCRIPTION_NAME] subscription" +fi + +# Retrieve the resource id of the Key Vault resource +echo "Retrieving the resource id for the [$KEY_VAULT_NAME] key vault..." +KEY_VAULT_ID=$(az keyvault show \ + --name $KEY_VAULT_NAME \ + --resource-group $KEY_VAULT_RESOURCE_GROUP_NAME \ + --query id \ + --output tsv) + +if [[ -n $KEY_VAULT_ID ]]; then + echo "[$KEY_VAULT_ID] resource id for the [$KEY_VAULT_NAME] key vault successfully retrieved" +else + echo "Failed to retrieve the resource id for the [$KEY_VAULT_NAME] key vault" + exit +fi + +if [[ "$ENVIRONMENT_NAME" == "AzureCloud" ]]; then + # Get the signed-in user object id + LOGGED_IN_USER_ID=$(az ad signed-in-user show --query id --output tsv) + LOGGED_IN_USER_DISPLAY_NAME=$(az ad signed-in-user show --query displayName --output tsv) + + # Assign the Key Vault Administrator role to the user on the key vault + ROLE="Key Vault Administrator" + USER_DISPLAY_NAME="$LOGGED_IN_USER_DISPLAY_NAME" + PRINCIPAL_ID="$LOGGED_IN_USER_ID" + SCOPE_ID="$KEY_VAULT_ID" + SCOPE_NAME="$KEY_VAULT_NAME" + SCOPE_TYPE="key vault" + echo "Checking if the [$USER_DISPLAY_NAME] user has the [$ROLE] role assignment on the [$SCOPE_NAME] $SCOPE_TYPE..." + current=$(az role assignment list \ + --assignee "$PRINCIPAL_ID" \ + --scope "$SCOPE_ID" \ + --query "[?roleDefinitionName=='$ROLE'].roleDefinitionName" \ + --output tsv 2>/dev/null) + + if [[ $current == "$ROLE" ]]; then + echo "User [$USER_DISPLAY_NAME] already has the [$ROLE] role assignment on the [$SCOPE_NAME] $SCOPE_TYPE" + else + echo "User [$USER_DISPLAY_NAME] does not have the [$ROLE] role assignment on the [$SCOPE_NAME] $SCOPE_TYPE" + echo "Creating role assignment: assigning [$ROLE] role to user [$USER_DISPLAY_NAME] on the [$SCOPE_NAME] $SCOPE_TYPE..." + ATTEMPT=1 + while [ $ATTEMPT -le $RETRY_COUNT ]; do + echo "Attempt $ATTEMPT of $RETRY_COUNT to assign role..." + az role assignment create \ + --assignee "$PRINCIPAL_ID" \ + --role "$ROLE" \ + --scope "$SCOPE_ID" 1>/dev/null + + if [[ $? == 0 ]]; then + break + else + if [ $ATTEMPT -lt $RETRY_COUNT ]; then + echo "Role assignment failed. Waiting [$SLEEP] seconds before retry..." + sleep $SLEEP + fi + ATTEMPT=$((ATTEMPT + 1)) + fi + done + + if [[ $? == 0 ]]; then + echo "Successfully assigned [$ROLE] role to user [$USER_DISPLAY_NAME] on the [$SCOPE_NAME] $SCOPE_TYPE" + else + echo "Failed to assign [$ROLE] role to user [$USER_DISPLAY_NAME] on the [$SCOPE_NAME] $SCOPE_TYPE" + exit 1 + fi + fi +fi + +# Create secrets +for INDEX in ${!SECRETS[@]}; do + # Check if the secret already exists + echo "Checking if [${SECRETS[$INDEX]}] secret actually exists in the [$KEY_VAULT_NAME] key vault..." + + az keyvault secret show --name ${SECRETS[$INDEX]} --vault-name $KEY_VAULT_NAME &>/dev/null + + if [[ $? != 0 ]]; then + echo "No [${SECRETS[$INDEX]}] secret actually exists in the [$KEY_VAULT_NAME] key vault" + ATTEMPT=1 + while [ $ATTEMPT -le $RETRY_COUNT ]; do + echo "Attempt $ATTEMPT of $RETRY_COUNT to create [${SECRETS[$INDEX]}] secret in the [$KEY_VAULT_NAME] key vault..." + + # Create the secret + az keyvault secret set \ + --name ${SECRETS[$INDEX]} \ + --vault-name $KEY_VAULT_NAME \ + --value ${VALUES[$INDEX]} 1>/dev/null + + if [[ $? == 0 ]]; then + echo "[${SECRETS[$INDEX]}] secret successfully created in the [$KEY_VAULT_NAME] key vault" + break + else + if [ $ATTEMPT -lt $RETRY_COUNT ]; then + echo "Failed to create [${SECRETS[$INDEX]}] secret in the [$KEY_VAULT_NAME] key vault. Waiting [$SLEEP] seconds before retry..." + sleep $SLEEP + fi + ATTEMPT=$((ATTEMPT + 1)) + fi + done + else + echo "[${SECRETS[$INDEX]}] secret already exists in the [$KEY_VAULT_NAME] key vault" + fi +done + +# Show secrets +for INDEX in ${!SECRETS[@]}; do + # retrieve the secret + echo "Retrieving [${SECRETS[$INDEX]}] secret from the [$KEY_VAULT_NAME] key vault..." + VALUE=$(az keyvault secret show \ + --name ${SECRETS[$INDEX]} \ + --vault-name $KEY_VAULT_NAME \ + --query value \ + --output tsv) + + if [[ -n $VALUE ]]; then + echo "[$VALUE] value for [${SECRETS[$INDEX]}] secret successfully retrieved from the [$KEY_VAULT_NAME] key vault" + else + echo "Failed to retrieve [${SECRETS[$INDEX]}] secret from the [$KEY_VAULT_NAME] key vault" + exit + fi +done diff --git a/samples/key-vault-csi-driver/user-assigned-managed-identity/03-create-role-assignment.sh b/samples/key-vault-csi-driver/user-assigned-managed-identity/03-create-role-assignment.sh new file mode 100755 index 0000000..b3e803e --- /dev/null +++ b/samples/key-vault-csi-driver/user-assigned-managed-identity/03-create-role-assignment.sh @@ -0,0 +1,99 @@ +#/bin/bash + +# Variables +source ./00-variables.sh + +# Retrieve the resource id of the Key Vault resource +echo "Retrieving the resource id for the [$KEY_VAULT_NAME] key vault..." +KEY_VAULT_ID=$(az keyvault show \ + --name $KEY_VAULT_NAME \ + --resource-group $KEY_VAULT_RESOURCE_GROUP_NAME \ + --query id \ + --output tsv \ + --only-show-errors) + +if [[ -n $KEY_VAULT_ID ]]; then + echo "[$KEY_VAULT_ID] resource id for the [$KEY_VAULT_NAME] key vault successfully retrieved" +else + echo "Failed to retrieve the resource id for the [$KEY_VAULT_NAME] key vault" + exit +fi + +# Get the objectId of the Azure Key Vault Secrets Provider identity +KV_IDENTITY_OBJECT_ID=$(az aks show \ + --resource-group $AKS_RESOURCE_GROUP_NAME \ + --name $AKS_NAME \ + --query addonProfiles.azureKeyvaultSecretsProvider.identity.objectId \ + --output tsv \ + --only-show-errors) + +if [[ -n $KV_IDENTITY_OBJECT_ID ]]; then + echo "Successfully retrieved the objectId for the Azure Key Vault Secrets Provider identity in the [$AKS_NAME] AKS cluster" +else + echo "Failed to retrieve the objectId for the Azure Key Vault Secrets Provider identity in the [$AKS_NAME] AKS cluster" + exit +fi + +# Get the resourceId of the Azure Key Vault Secrets Provider identity +KV_IDENTITY_RESOURCE_ID=$(az aks show \ + --resource-group $AKS_RESOURCE_GROUP_NAME \ + --name $AKS_NAME \ + --query addonProfiles.azureKeyvaultSecretsProvider.identity.resourceId \ + --output tsv \ + --only-show-errors) + +if [[ -n $KV_IDENTITY_RESOURCE_ID ]]; then + echo "Successfully retrieved the resourceId for the Azure Key Vault Secrets Provider identity in the [$AKS_NAME] AKS cluster" +else + echo "Failed to retrieve the resourceId for the Azure Key Vault Secrets Provider identity in the [$AKS_NAME] AKS cluster" + exit +fi + +# Get the name of the Azure Key Vault Secrets Provider identity from the resourceId +KV_IDENTITY_NAME=$(basename $KV_IDENTITY_RESOURCE_ID) + +# Assign the Key Vault Administrator role to the managed identity on the node resource group +ROLE="Key Vault Administrator" +MANAGED_IDENTITY_NAME="$KV_IDENTITY_NAME" +PRINCIPAL_ID="$KV_IDENTITY_OBJECT_ID" +SCOPE_ID="$KEY_VAULT_ID" +SCOPE_NAME="$KEY_VAULT_NAME" +SCOPE_TYPE="key vault" +echo "Checking if the [$MANAGED_IDENTITY_NAME] managed identity has the [$ROLE] role assignment on the [$SCOPE_NAME] $SCOPE_TYPE..." +current=$(az role assignment list \ + --assignee "$PRINCIPAL_ID" \ + --scope "$SCOPE_ID" \ + --query "[?roleDefinitionName=='$ROLE'].roleDefinitionName" \ + --output tsv 2>/dev/null) + +if [[ $current == "$ROLE" ]]; then + echo "Managed identity [$MANAGED_IDENTITY_NAME] already has the [$ROLE] role assignment on the [$SCOPE_NAME] $SCOPE_TYPE" +else + echo "Managed identity [$MANAGED_IDENTITY_NAME] does not have the [$ROLE] role assignment on the [$SCOPE_NAME] $SCOPE_TYPE" + echo "Creating role assignment: assigning [$ROLE] role to managed identity [$MANAGED_IDENTITY_NAME] on the [$SCOPE_NAME] $SCOPE_TYPE..." + ATTEMPT=1 + while [ $ATTEMPT -le $RETRY_COUNT ]; do + echo "Attempt $ATTEMPT of $RETRY_COUNT to assign role..." + az role assignment create \ + --assignee "$PRINCIPAL_ID" \ + --role "$ROLE" \ + --scope "$SCOPE_ID" 1>/dev/null + + if [[ $? == 0 ]]; then + break + else + if [ $ATTEMPT -lt $RETRY_COUNT ]; then + echo "Role assignment failed. Waiting [$SLEEP] seconds before retry..." + sleep $SLEEP + fi + ATTEMPT=$((ATTEMPT + 1)) + fi + done + + if [[ $? == 0 ]]; then + echo "Successfully assigned [$ROLE] role to managed identity [$MANAGED_IDENTITY_NAME] on the [$SCOPE_NAME] $SCOPE_TYPE" + else + echo "Failed to assign [$ROLE] role to managed identity [$MANAGED_IDENTITY_NAME] on the [$SCOPE_NAME] $SCOPE_TYPE" + exit 1 + fi +fi diff --git a/samples/key-vault-csi-driver/user-assigned-managed-identity/04-create-secret-provider-class.sh b/samples/key-vault-csi-driver/user-assigned-managed-identity/04-create-secret-provider-class.sh new file mode 100755 index 0000000..4b5dd44 --- /dev/null +++ b/samples/key-vault-csi-driver/user-assigned-managed-identity/04-create-secret-provider-class.sh @@ -0,0 +1,66 @@ +#/bin/bash + +# For more information, see: +# https://learn.microsoft.com/en-us/azure/aks/csi-secrets-store-driver +# https://learn.microsoft.com/en-us/azure/aks/csi-secrets-store-identity-access + +# Variables +source ./00-variables.sh + +# Get the clientId of the Azure Key Vault Secrets Provider identity +KV_IDENTITY_CLIENT_ID=$(az aks show \ + --resource-group $AKS_RESOURCE_GROUP_NAME \ + --name $AKS_NAME \ + --query addonProfiles.azureKeyvaultSecretsProvider.identity.clientId \ + --output tsv \ + --only-show-errors) + +if [[ -n $KV_IDENTITY_CLIENT_ID ]]; then + echo "Successfully retrieved the clientId for the Azure Key Vault Secrets Provider identity in the [$AKS_NAME] AKS cluster" +else + echo "Failed to retrieve the clientId for the Azure Key Vault Secrets Provider identity in the [$AKS_NAME] AKS cluster" + exit +fi + +echo "KV_IDENTITY_CLIENT_ID: $KV_IDENTITY_CLIENT_ID" +echo "KEY_VAULT_NAME: $KEY_VAULT_NAME" +echo "TENANT_ID: $TENANT_ID" + +# Check if the namespace exists in the cluster +RESULT=$(kubectl get namespace -o 'jsonpath={.items[?(@.metadata.name=="'$NAMESPACE'")].metadata.name'}) + +if [[ -n $RESULT ]]; then + echo "[$NAMESPACE] namespace already exists in the cluster" +else + echo "[$NAMESPACE] namespace does not exist in the cluster" + echo "Creating [$NAMESPACE] namespace in the cluster..." + kubectl create namespace $NAMESPACE +fi + +# Create the SecretProviderClass for the secret store CSI driver with Azure Key Vault provider +echo "Creating the SecretProviderClass for the secret store CSI driver with Azure Key Vault provider..." +cat </dev/null + +if [[ $? != 0 ]]; then + echo "The [azure-keyvault-secrets-provider] addon is not enabled in the [$AKS_NAME] AKS cluster" + echo "Enabling the [azure-keyvault-secrets-provider] addon in the [$AKS_NAME] AKS cluster..." + + az aks addon enable \ + --addon azure-keyvault-secrets-provider \ + --enable-secret-rotation \ + --name $AKS_NAME \ + --resource-group $AKS_RESOURCE_GROUP_NAME +else + echo "The [azure-keyvault-secrets-provider] addon is already enabled in the [$AKS_NAME] AKS cluster" +fi \ No newline at end of file diff --git a/samples/key-vault-csi-driver/workload-identity/02-create-key-vault-and-secrets.sh b/samples/key-vault-csi-driver/workload-identity/02-create-key-vault-and-secrets.sh new file mode 100755 index 0000000..fe96afb --- /dev/null +++ b/samples/key-vault-csi-driver/workload-identity/02-create-key-vault-and-secrets.sh @@ -0,0 +1,175 @@ +#!/bin/bash + +# Variables +source ./00-variables.sh + +# Check if the resource group already exists +echo "Checking if [$KEY_VAULT_RESOURCE_GROUP_NAME] resource group actually exists in the [$SUBSCRIPTION_NAME] subscription..." + +az group show --name $KEY_VAULT_RESOURCE_GROUP_NAME &>/dev/null + +if [[ $? != 0 ]]; then + echo "No [$KEY_VAULT_RESOURCE_GROUP_NAME] resource group actually exists in the [$SUBSCRIPTION_NAME] subscription" + echo "Creating [$KEY_VAULT_RESOURCE_GROUP_NAME] resource group in the [$SUBSCRIPTION_NAME] subscription..." + + # create the resource group + az group create --name $KEY_VAULT_RESOURCE_GROUP_NAME --location $LOCATION 1>/dev/null + + if [[ $? == 0 ]]; then + echo "[$KEY_VAULT_RESOURCE_GROUP_NAME] resource group successfully created in the [$SUBSCRIPTION_NAME] subscription" + else + echo "Failed to create [$KEY_VAULT_RESOURCE_GROUP_NAME] resource group in the [$SUBSCRIPTION_NAME] subscription" + exit + fi +else + echo "[$KEY_VAULT_RESOURCE_GROUP_NAME] resource group already exists in the [$SUBSCRIPTION_NAME] subscription" +fi + +# Check if the key vault already exists +echo "Checking if [$KEY_VAULT_NAME] key vault actually exists in the [$SUBSCRIPTION_NAME] subscription..." + +az keyvault show --name $KEY_VAULT_NAME --resource-group $KEY_VAULT_RESOURCE_GROUP_NAME &>/dev/null + +if [[ $? != 0 ]]; then + echo "No [$KEY_VAULT_NAME] key vault actually exists in the [$SUBSCRIPTION_NAME] subscription" + echo "Creating [$KEY_VAULT_NAME] key vault in the [$SUBSCRIPTION_NAME] subscription..." + + # create the key vault + az keyvault create \ + --name $KEY_VAULT_NAME \ + --resource-group $KEY_VAULT_RESOURCE_GROUP_NAME \ + --location $LOCATION \ + --enabled-for-deployment \ + --enabled-for-disk-encryption \ + --enabled-for-template-deployment \ + --sku $KEY_VAULT_SKU 1>/dev/null + + if [[ $? == 0 ]]; then + echo "[$KEY_VAULT_NAME] key vault successfully created in the [$SUBSCRIPTION_NAME] subscription" + else + echo "Failed to create [$KEY_VAULT_NAME] key vault in the [$SUBSCRIPTION_NAME] subscription" + exit + fi +else + echo "[$KEY_VAULT_NAME] key vault already exists in the [$SUBSCRIPTION_NAME] subscription" +fi + +# Retrieve the resource id of the Key Vault resource +echo "Retrieving the resource id for the [$KEY_VAULT_NAME] key vault..." +KEY_VAULT_ID=$(az keyvault show \ + --name $KEY_VAULT_NAME \ + --resource-group $KEY_VAULT_RESOURCE_GROUP_NAME \ + --query id \ + --output tsv) + +if [[ -n $KEY_VAULT_ID ]]; then + echo "[$KEY_VAULT_ID] resource id for the [$KEY_VAULT_NAME] key vault successfully retrieved" +else + echo "Failed to retrieve the resource id for the [$KEY_VAULT_NAME] key vault" + exit +fi + +if [[ "$ENVIRONMENT_NAME" == "AzureCloud" ]]; then + # Get the signed-in user object id + LOGGED_IN_USER_ID=$(az ad signed-in-user show --query id --output tsv) + LOGGED_IN_USER_DISPLAY_NAME=$(az ad signed-in-user show --query displayName --output tsv) + + # Assign the Key Vault Administrator role to the user on the key vault + ROLE="Key Vault Administrator" + USER_DISPLAY_NAME="$LOGGED_IN_USER_DISPLAY_NAME" + PRINCIPAL_ID="$LOGGED_IN_USER_ID" + SCOPE_ID="$KEY_VAULT_ID" + SCOPE_NAME="$KEY_VAULT_NAME" + SCOPE_TYPE="key vault" + echo "Checking if the [$USER_DISPLAY_NAME] user has the [$ROLE] role assignment on the [$SCOPE_NAME] $SCOPE_TYPE..." + current=$(az role assignment list \ + --assignee "$PRINCIPAL_ID" \ + --scope "$SCOPE_ID" \ + --query "[?roleDefinitionName=='$ROLE'].roleDefinitionName" \ + --output tsv 2>/dev/null) + + if [[ $current == "$ROLE" ]]; then + echo "User [$USER_DISPLAY_NAME] already has the [$ROLE] role assignment on the [$SCOPE_NAME] $SCOPE_TYPE" + else + echo "User [$USER_DISPLAY_NAME] does not have the [$ROLE] role assignment on the [$SCOPE_NAME] $SCOPE_TYPE" + echo "Creating role assignment: assigning [$ROLE] role to user [$USER_DISPLAY_NAME] on the [$SCOPE_NAME] $SCOPE_TYPE..." + ATTEMPT=1 + while [ $ATTEMPT -le $RETRY_COUNT ]; do + echo "Attempt $ATTEMPT of $RETRY_COUNT to assign role..." + az role assignment create \ + --assignee "$PRINCIPAL_ID" \ + --role "$ROLE" \ + --scope "$SCOPE_ID" 1>/dev/null + + if [[ $? == 0 ]]; then + break + else + if [ $ATTEMPT -lt $RETRY_COUNT ]; then + echo "Role assignment failed. Waiting [$SLEEP] seconds before retry..." + sleep $SLEEP + fi + ATTEMPT=$((ATTEMPT + 1)) + fi + done + + if [[ $? == 0 ]]; then + echo "Successfully assigned [$ROLE] role to user [$USER_DISPLAY_NAME] on the [$SCOPE_NAME] $SCOPE_TYPE" + else + echo "Failed to assign [$ROLE] role to user [$USER_DISPLAY_NAME] on the [$SCOPE_NAME] $SCOPE_TYPE" + exit 1 + fi + fi +fi + +# Create secrets +for INDEX in ${!SECRETS[@]}; do + # Check if the secret already exists + echo "Checking if [${SECRETS[$INDEX]}] secret actually exists in the [$KEY_VAULT_NAME] key vault..." + + az keyvault secret show --name ${SECRETS[$INDEX]} --vault-name $KEY_VAULT_NAME &>/dev/null + + if [[ $? != 0 ]]; then + echo "No [${SECRETS[$INDEX]}] secret actually exists in the [$KEY_VAULT_NAME] key vault" + ATTEMPT=1 + while [ $ATTEMPT -le $RETRY_COUNT ]; do + echo "Attempt $ATTEMPT of $RETRY_COUNT to create [${SECRETS[$INDEX]}] secret in the [$KEY_VAULT_NAME] key vault..." + + # Create the secret + az keyvault secret set \ + --name ${SECRETS[$INDEX]} \ + --vault-name $KEY_VAULT_NAME \ + --value ${VALUES[$INDEX]} 1>/dev/null + + if [[ $? == 0 ]]; then + echo "[${SECRETS[$INDEX]}] secret successfully created in the [$KEY_VAULT_NAME] key vault" + break + else + if [ $ATTEMPT -lt $RETRY_COUNT ]; then + echo "Failed to create [${SECRETS[$INDEX]}] secret in the [$KEY_VAULT_NAME] key vault. Waiting [$SLEEP] seconds before retry..." + sleep $SLEEP + fi + ATTEMPT=$((ATTEMPT + 1)) + fi + done + else + echo "[${SECRETS[$INDEX]}] secret already exists in the [$KEY_VAULT_NAME] key vault" + fi +done + +# Show secrets +for INDEX in ${!SECRETS[@]}; do + # retrieve the secret + echo "Retrieving [${SECRETS[$INDEX]}] secret from the [$KEY_VAULT_NAME] key vault..." + VALUE=$(az keyvault secret show \ + --name ${SECRETS[$INDEX]} \ + --vault-name $KEY_VAULT_NAME \ + --query value \ + --output tsv) + + if [[ -n $VALUE ]]; then + echo "[$VALUE] value for [${SECRETS[$INDEX]}] secret successfully retrieved from the [$KEY_VAULT_NAME] key vault" + else + echo "Failed to retrieve [${SECRETS[$INDEX]}] secret from the [$KEY_VAULT_NAME] key vault" + exit + fi +done \ No newline at end of file diff --git a/samples/key-vault-csi-driver/workload-identity/03-create-managed-identity.sh b/samples/key-vault-csi-driver/workload-identity/03-create-managed-identity.sh new file mode 100755 index 0000000..7016e56 --- /dev/null +++ b/samples/key-vault-csi-driver/workload-identity/03-create-managed-identity.sh @@ -0,0 +1,227 @@ +#/bin/bash + +# Variables +source ./00-variables.sh + +# Check if the resource group already exists +echo "Checking if [$AKS_RESOURCE_GROUP_NAME] resource group actually exists in the [$SUBSCRIPTION_ID] subscription..." + +az group show --name $AKS_RESOURCE_GROUP_NAME &>/dev/null + +if [[ $? != 0 ]]; then + echo "No [$AKS_RESOURCE_GROUP_NAME] resource group actually exists in the [$SUBSCRIPTION_ID] subscription" + echo "Creating [$AKS_RESOURCE_GROUP_NAME] resource group in the [$SUBSCRIPTION_ID] subscription..." + + # create the resource group + az group create \ + --name $AKS_RESOURCE_GROUP_NAME \ + --location $LOCATION 1>/dev/null + + if [[ $? == 0 ]]; then + echo "[$AKS_RESOURCE_GROUP_NAME] resource group successfully created in the [$SUBSCRIPTION_ID] subscription" + else + echo "Failed to create [$AKS_RESOURCE_GROUP_NAME] resource group in the [$SUBSCRIPTION_ID] subscription" + exit + fi +else + echo "[$AKS_RESOURCE_GROUP_NAME] resource group already exists in the [$SUBSCRIPTION_ID] subscription" +fi + +# check if the managed identity already exists +echo "Checking if [$MANAGED_IDENTITY_NAME] managed identity actually exists in the [$AKS_RESOURCE_GROUP_NAME] resource group..." + +az identity show \ + --name $MANAGED_IDENTITY_NAME \ + --resource-group $AKS_RESOURCE_GROUP_NAME &>/dev/null + +if [[ $? != 0 ]]; then + echo "No [$MANAGED_IDENTITY_NAME] managed identity actually exists in the [$AKS_RESOURCE_GROUP_NAME] resource group" + echo "Creating [$MANAGED_IDENTITY_NAME] managed identity in the [$AKS_RESOURCE_GROUP_NAME] resource group..." + + # create the managed identity + az identity create \ + --name $MANAGED_IDENTITY_NAME \ + --resource-group $AKS_RESOURCE_GROUP_NAME &>/dev/null + + if [[ $? == 0 ]]; then + echo "[$MANAGED_IDENTITY_NAME] managed identity successfully created in the [$AKS_RESOURCE_GROUP_NAME] resource group" + else + echo "Failed to create [$MANAGED_IDENTITY_NAME] managed identity in the [$AKS_RESOURCE_GROUP_NAME] resource group" + exit + fi +else + echo "[$MANAGED_IDENTITY_NAME] managed identity already exists in the [$AKS_RESOURCE_GROUP_NAME] resource group" +fi + +# Get the managed identity principal id +echo "Retrieving principalId for [$MANAGED_IDENTITY_NAME] managed identity..." +PRINCIPAL_ID=$(az identity show \ + --name $MANAGED_IDENTITY_NAME \ + --resource-group $AKS_RESOURCE_GROUP_NAME \ + --query principalId \ + --output tsv) + +if [[ -n $PRINCIPAL_ID ]]; then + echo "[$PRINCIPAL_ID] principalId or the [$MANAGED_IDENTITY_NAME] managed identity successfully retrieved" +else + echo "Failed to retrieve principalId for the [$MANAGED_IDENTITY_NAME] managed identity" + exit +fi + +# Get the managed identity client id +echo "Retrieving clientId for [$MANAGED_IDENTITY_NAME] managed identity..." +CLIENT_ID=$(az identity show \ + --name $MANAGED_IDENTITY_NAME \ + --resource-group $AKS_RESOURCE_GROUP_NAME \ + --query clientId \ + --output tsv) + +if [[ -n $CLIENT_ID ]]; then + echo "[$CLIENT_ID] clientId for the [$MANAGED_IDENTITY_NAME] managed identity successfully retrieved" +else + echo "Failed to retrieve clientId for the [$MANAGED_IDENTITY_NAME] managed identity" + exit +fi + +# Retrieve the resource id of the Key Vault resource +echo "Retrieving the resource id for the [$KEY_VAULT_NAME] key vault..." +KEY_VAULT_ID=$(az keyvault show \ + --name $KEY_VAULT_NAME \ + --resource-group $KEY_VAULT_RESOURCE_GROUP_NAME \ + --query id \ + --output tsv) + +if [[ -n $KEY_VAULT_ID ]]; then + echo "[$KEY_VAULT_ID] resource id for the [$KEY_VAULT_NAME] key vault successfully retrieved" +else + echo "Failed to retrieve the resource id for the [$KEY_VAULT_NAME] key vault" + exit +fi + +# Assign the Key Vault Administrator role to the managed identity on the node resource group +ROLE="Key Vault Administrator" +PRINCIPAL_ID="$PRINCIPAL_ID" +SCOPE_ID="$KEY_VAULT_ID" +SCOPE_NAME="$KEY_VAULT_NAME" +SCOPE_TYPE="key vault" +echo "Checking if the [$MANAGED_IDENTITY_NAME] managed identity has the [$ROLE] role assignment on the [$SCOPE_NAME] $SCOPE_TYPE..." +current=$(az role assignment list \ + --assignee "$PRINCIPAL_ID" \ + --scope "$SCOPE_ID" \ + --query "[?roleDefinitionName=='$ROLE'].roleDefinitionName" \ + --output tsv 2>/dev/null) + +if [[ $current == "$ROLE" ]]; then + echo "Managed identity [$MANAGED_IDENTITY_NAME] already has the [$ROLE] role assignment on the [$SCOPE_NAME] $SCOPE_TYPE" +else + echo "Managed identity [$MANAGED_IDENTITY_NAME] does not have the [$ROLE] role assignment on the [$SCOPE_NAME] $SCOPE_TYPE" + echo "Creating role assignment: assigning [$ROLE] role to managed identity [$MANAGED_IDENTITY_NAME] on the [$SCOPE_NAME] $SCOPE_TYPE..." + ATTEMPT=1 + while [ $ATTEMPT -le $RETRY_COUNT ]; do + echo "Attempt $ATTEMPT of $RETRY_COUNT to assign role..." + az role assignment create \ + --assignee "$PRINCIPAL_ID" \ + --role "$ROLE" \ + --scope "$SCOPE_ID" 1>/dev/null + + if [[ $? == 0 ]]; then + break + else + if [ $ATTEMPT -lt $RETRY_COUNT ]; then + echo "Role assignment failed. Waiting [$SLEEP] seconds before retry..." + sleep $SLEEP + fi + ATTEMPT=$((ATTEMPT + 1)) + fi + done + + if [[ $? == 0 ]]; then + echo "Successfully assigned [$ROLE] role to managed identity [$MANAGED_IDENTITY_NAME] on the [$SCOPE_NAME] $SCOPE_TYPE" + else + echo "Failed to assign [$ROLE] role to managed identity [$MANAGED_IDENTITY_NAME] on the [$SCOPE_NAME] $SCOPE_TYPE" + exit 1 + fi +fi + +# Check if the namespace exists in the cluster +RESULT=$(kubectl get namespace -o 'jsonpath={.items[?(@.metadata.name=="'$NAMESPACE'")].metadata.name'}) + +if [[ -n $RESULT ]]; then + echo "[$NAMESPACE] namespace already exists in the cluster" +else + echo "[$NAMESPACE] namespace does not exist in the cluster" + echo "Creating [$NAMESPACE] namespace in the cluster..." + kubectl create namespace $NAMESPACE +fi + +# Check if the service account already exists +RESULT=$(kubectl get sa -n $NAMESPACE -o 'jsonpath={.items[?(@.metadata.name=="'$SERVICE_ACCOUNT_NAME'")].metadata.name'}) + +if [[ -n $RESULT ]]; then + echo "[$SERVICE_ACCOUNT_NAME] service account already exists" +else + # Create the service account + echo "[$SERVICE_ACCOUNT_NAME] service account does not exist" + echo "Creating [$SERVICE_ACCOUNT_NAME] service account..." + cat </dev/null + +if [[ $? != 0 ]]; then + echo "No [$FEDERATED_IDENTITY_NAME] federated identity credential actually exists in the [$AKS_RESOURCE_GROUP_NAME] resource group" + + # Get the OIDC Issuer URL + AKS_OIDC_ISSUER_URL="$(az aks show \ + --only-show-errors \ + --name $AKS_NAME \ + --resource-group $AKS_RESOURCE_GROUP_NAME \ + --query oidcIssuerProfile.issuerUrl \ + --output tsv)" + + # Show OIDC Issuer URL + if [[ -n $AKS_OIDC_ISSUER_URL ]]; then + echo "The OIDC Issuer URL of the [$AKS_NAME] cluster is [$AKS_OIDC_ISSUER_URL]" + fi + + echo "Creating [$FEDERATED_IDENTITY_NAME] federated identity credential in the [$AKS_RESOURCE_GROUP_NAME] resource group..." + + # Establish the federated identity credential between the managed identity, the service account issuer, and the subject. + az identity federated-credential create \ + --name $FEDERATED_IDENTITY_NAME \ + --identity-name $MANAGED_IDENTITY_NAME \ + --resource-group $AKS_RESOURCE_GROUP_NAME \ + --issuer $AKS_OIDC_ISSUER_URL \ + --subject system:serviceaccount:$NAMESPACE:$SERVICE_ACCOUNT_NAME + + if [[ $? == 0 ]]; then + echo "[$FEDERATED_IDENTITY_NAME] federated identity credential successfully created in the [$AKS_RESOURCE_GROUP_NAME] resource group" + else + echo "Failed to create [$FEDERATED_IDENTITY_NAME] federated identity credential in the [$AKS_RESOURCE_GROUP_NAME] resource group" + exit + fi +else + echo "[$FEDERATED_IDENTITY_NAME] federated identity credential already exists in the [$AKS_RESOURCE_GROUP_NAME] resource group" +fi diff --git a/samples/key-vault-csi-driver/workload-identity/04-create-secret-provider-class.sh b/samples/key-vault-csi-driver/workload-identity/04-create-secret-provider-class.sh new file mode 100755 index 0000000..64ac5ce --- /dev/null +++ b/samples/key-vault-csi-driver/workload-identity/04-create-secret-provider-class.sh @@ -0,0 +1,50 @@ +#/bin/bash + +# For more information, see: +# https://learn.microsoft.com/en-us/azure/aks/csi-secrets-store-driver +# https://learn.microsoft.com/en-us/azure/aks/csi-secrets-store-identity-access + +# Variables +source ./00-variables.sh + +# Get the managed identity client id +echo "Retrieving clientId for [$MANAGED_IDENTITY_NAME] managed identity..." +CLIENT_ID=$(az identity show \ + --name $MANAGED_IDENTITY_NAME \ + --resource-group $AKS_RESOURCE_GROUP_NAME \ + --query clientId \ + --output tsv) + +if [[ -n $CLIENT_ID ]]; then + echo "[$CLIENT_ID] clientId for the [$MANAGED_IDENTITY_NAME] managed identity successfully retrieved" +else + echo "Failed to retrieve clientId for the [$MANAGED_IDENTITY_NAME] managed identity" + exit +fi + +# Create the SecretProviderClass for the secret store CSI driver with Azure Key Vault provider +echo "Creating the SecretProviderClass for the secret store CSI driver with Azure Key Vault provider..." +cat </dev/null @@ -101,7 +101,7 @@ az postgres flexible-server firewall-rule create \ # Create the PostgreSQL database echo "Checking if PostgreSQL database [$PG_DATABASE_NAME] exists..." az postgres flexible-server db show \ - --database-name "$PG_DATABASE_NAME" \ + --name "$PG_DATABASE_NAME" \ --server-name "$PG_SERVER_NAME" \ --resource-group "$RESOURCE_GROUP_NAME" \ --only-show-errors &>/dev/null @@ -109,7 +109,7 @@ az postgres flexible-server db show \ if [[ $? != 0 ]]; then echo "Creating PostgreSQL database [$PG_DATABASE_NAME]..." az postgres flexible-server db create \ - --database-name "$PG_DATABASE_NAME" \ + --name "$PG_DATABASE_NAME" \ --server-name "$PG_SERVER_NAME" \ --resource-group "$RESOURCE_GROUP_NAME" \ --charset UTF8 \ diff --git a/scripts/01-system-assigned-managed-identity.sh b/scripts/01-system-assigned-managed-identity.sh index f1f6600..6a3f434 100755 --- a/scripts/01-system-assigned-managed-identity.sh +++ b/scripts/01-system-assigned-managed-identity.sh @@ -13,6 +13,9 @@ os_disk_size=50 os_disk_type="Ephemeral" system_node_pool_name="system" +# Azure Key Vault +key_vault_name="$prefix-kv-$suffix" + # Azure Container Registry acr_name="${prefix}acr${suffix}" acr_sku="Basic" @@ -47,8 +50,8 @@ windows_admin_username="azadmin" windows_admin_password="Trustno123456!" # Node count variables -node_count=3 -min_count=3 +node_count=1 +min_count=1 max_count=3 max_pods=100 @@ -57,8 +60,8 @@ user_node_pool_name="user" vm_size="Standard_D4ds_v5" # Standard_D4ds_v4 os_type="Linux" mode="User" -node_pool_node_count=3 -node_pool_min_count=3 +node_pool_node_count=1 +node_pool_min_count=1 node_pool_max_count=3 node_pool_max_pods=100 @@ -200,6 +203,48 @@ else echo "[$resource_group_name] resource group already exists in the [$subscription_name] subscription" fi +# Create Key Vault +echo "Checking if [$key_vault_name] key vault actually exists in the [$resource_group_name] resource group..." +az keyvault show \ + --name $key_vault_name \ + --resource-group $resource_group_name \ + --only-show-errors &>/dev/null + +if [[ $? != 0 ]]; then + echo "No [$key_vault_name] key vault actually exists in the [$resource_group_name] resource group" + echo "Creating Key Vault [$key_vault_name]..." + az keyvault create \ + --name "$key_vault_name" \ + --resource-group "$resource_group_name" \ + --location "$location" \ + --enable-rbac-authorization true \ + --only-show-errors 1>/dev/null + + if [ $? -eq 0 ]; then + echo "Key Vault [$key_vault_name] created successfully." + else + echo "Failed to create Key Vault [$key_vault_name]." + exit 1 + fi +else + echo "[$key_vault_name] key vault already exists in the [$resource_group_name] resource group" +fi + +# Retrieve the Key Vault id +key_vault_id=$(az keyvault show \ + --name $key_vault_name \ + --resource-group $resource_group_name \ + --query id \ + --output tsv \ + --only-show-errors 2>/dev/null) + +if [[ -n $key_vault_id ]]; then + echo "Successfully retrieved the id for the [$key_vault_name] key vault" +else + echo "Failed to retrieve the id for the [$key_vault_name] key vault" + exit +fi + # Check if log analytics workspace exists and retrieve its resource id echo "Retrieving [$log_analytics_name] Log Analytics resource id..." az monitor log-analytics workspace show \ @@ -450,7 +495,7 @@ if [[ $? != 0 ]]; then --windows-admin-username $windows_admin_username \ --windows-admin-password $windows_admin_password \ --node-vm-size $node_size \ - --enable-addons monitoring \ + --enable-addons monitoring,azure-keyvault-secrets-provider \ --workspace-resource-id $workspace_resource_id \ --network-dataplane $network_dataplane \ --network-policy $network_policy \ @@ -582,6 +627,97 @@ else fi fi +# Get the objectId of the Azure Key Vault Secrets Provider identity +kv_secret_provider_managed_identity_object_id=$(az aks show \ + --resource-group $resource_group_name \ + --name $aks_cluster_name \ + --query addonProfiles.azureKeyvaultSecretsProvider.identity.objectId \ + -o tsv) + +if [[ -n $kv_secret_provider_managed_identity_object_id ]]; then + echo "Successfully retrieved the objectId for the Azure Key Vault Secrets Provider identity in the [$aks_cluster_name] AKS cluster" +else + echo "Failed to retrieve the objectId for the Azure Key Vault Secrets Provider identity in the [$aks_cluster_name] AKS cluster" + exit +fi + +# Get the clientId of the Azure Key Vault Secrets Provider identity +kv_secret_provider_managed_identity_client_id=$(az aks show \ + --resource-group $resource_group_name \ + --name $aks_cluster_name \ + --query addonProfiles.azureKeyvaultSecretsProvider.identity.clientId \ + -o tsv) + +if [[ -n $kv_secret_provider_managed_identity_client_id ]]; then + echo "Successfully retrieved the clientId for the Azure Key Vault Secrets Provider identity in the [$aks_cluster_name] AKS cluster" +else + echo "Failed to retrieve the clientId for the Azure Key Vault Secrets Provider identity in the [$aks_cluster_name] AKS cluster" + exit +fi + +# Get the resourceId of the Azure Key Vault Secrets Provider identity +kv_secret_provider_managed_identity_resource_id=$(az aks show \ + --resource-group $resource_group_name \ + --name $aks_cluster_name \ + --query addonProfiles.azureKeyvaultSecretsProvider.identity.resourceId \ + -o tsv) + +if [[ -n $kv_secret_provider_managed_identity_resource_id ]]; then + echo "Successfully retrieved the resourceId for the Azure Key Vault Secrets Provider identity in the [$aks_cluster_name] AKS cluster" +else + echo "Failed to retrieve the resourceId for the Azure Key Vault Secrets Provider identity in the [$aks_cluster_name] AKS cluster" + exit +fi + +# Get the name of the Azure Key Vault Secrets Provider identity from the resourceId +kv_secret_provider_managed_identity_name=$(basename $kv_secret_provider_managed_identity_resource_id) + +# Assign the Key Vault Administrator role to the managed identity on the node resource group +role="Key Vault Administrator" +managed_identity_name="$kv_secret_provider_managed_identity_name" +principal_id="$kv_secret_provider_managed_identity_object_id" +scope_id="$key_vault_id" +scope_name="$key_vault_name" +scope_type="key vault" +echo "Checking if the [$managed_identity_name] managed identity has the [$role] role assignment on the [$scope_name] $scope_type..." +current=$(az role assignment list \ + --assignee "$principal_id" \ + --scope "$scope_id" \ + --query "[?roleDefinitionName=='$role'].roleDefinitionName" \ + --output tsv 2>/dev/null) + +if [[ $current == "$role" ]]; then + echo "Managed identity [$managed_identity_name] already has the [$role] role assignment on the [$scope_name] $scope_type" +else + echo "Managed identity [$managed_identity_name] does not have the [$role] role assignment on the [$scope_name] $scope_type" + echo "Creating role assignment: assigning [$role] role to managed identity [$managed_identity_name] on the [$scope_name] $scope_type..." + ATTEMPT=1 + while [ $ATTEMPT -le $RETRY_COUNT ]; do + echo "Attempt $ATTEMPT of $RETRY_COUNT to assign role..." + az role assignment create \ + --assignee "$principal_id" \ + --role "$role" \ + --scope "$scope_id" 1>/dev/null + + if [[ $? == 0 ]]; then + break + else + if [ $ATTEMPT -lt $RETRY_COUNT ]; then + echo "Role assignment failed. Waiting [$SLEEP] seconds before retry..." + sleep $SLEEP + fi + ATTEMPT=$((ATTEMPT + 1)) + fi + done + + if [[ $? == 0 ]]; then + echo "Successfully assigned [$role] role to managed identity [$managed_identity_name] on the [$scope_name] $scope_type" + else + echo "Failed to assign [$role] role to managed identity [$managed_identity_name] on the [$scope_name] $scope_type" + exit 1 + fi +fi + # Use the following command to configure kubectl to connect to the new Kubernetes cluster echo "Getting access credentials configure kubectl to connect to the [$aks_cluster_name] AKS cluster..." az aks get-credentials \ diff --git a/scripts/01-user-assigned-managed-identity.sh b/scripts/01-user-assigned-managed-identity.sh index 76ae8c0..f9bc85f 100755 --- a/scripts/01-user-assigned-managed-identity.sh +++ b/scripts/01-user-assigned-managed-identity.sh @@ -14,6 +14,9 @@ os_disk_size=50 os_disk_type="Ephemeral" system_node_pool_name="system" +# Azure Key Vault +key_vault_name="$prefix-kv-$suffix" + # Azure Container Registry acr_name="${prefix}acr${suffix}" acr_sku="Basic" @@ -48,8 +51,8 @@ windows_admin_username="azadmin" windows_admin_password="Trustno123456!" # Node count variables -node_count=3 -min_count=3 +node_count=1 +min_count=1 max_count=3 max_pods=100 @@ -58,8 +61,8 @@ user_node_pool_name="user" vm_size="Standard_D4ds_v5" # Standard_D4ds_v4 os_type="Linux" mode="User" -node_pool_node_count=3 -node_pool_min_count=3 +node_pool_node_count=1 +node_pool_min_count=1 node_pool_max_count=3 node_pool_max_pods=100 @@ -201,6 +204,48 @@ else echo "[$resource_group_name] resource group already exists in the [$subscription_name] subscription" fi +# Create Key Vault +echo "Checking if [$key_vault_name] key vault actually exists in the [$resource_group_name] resource group..." +az keyvault show \ + --name $key_vault_name \ + --resource-group $resource_group_name \ + --only-show-errors &>/dev/null + +if [[ $? != 0 ]]; then + echo "No [$key_vault_name] key vault actually exists in the [$resource_group_name] resource group" + echo "Creating Key Vault [$key_vault_name]..." + az keyvault create \ + --name "$key_vault_name" \ + --resource-group "$resource_group_name" \ + --location "$location" \ + --enable-rbac-authorization true \ + --only-show-errors 1>/dev/null + + if [ $? -eq 0 ]; then + echo "Key Vault [$key_vault_name] created successfully." + else + echo "Failed to create Key Vault [$key_vault_name]." + exit 1 + fi +else + echo "[$key_vault_name] key vault already exists in the [$resource_group_name] resource group" +fi + +# Retrieve the Key Vault id +key_vault_id=$(az keyvault show \ + --name $key_vault_name \ + --resource-group $resource_group_name \ + --query id \ + --output tsv \ + --only-show-errors 2>/dev/null) + +if [[ -n $key_vault_id ]]; then + echo "Successfully retrieved the id for the [$key_vault_name] key vault" +else + echo "Failed to retrieve the id for the [$key_vault_name] key vault" + exit +fi + # Check if log analytics workspace exists and retrieve its resource id echo "Retrieving [$log_analytics_name] Log Analytics resource id..." az monitor log-analytics workspace show \ @@ -509,7 +554,7 @@ if [[ $? != 0 ]]; then --windows-admin-username $windows_admin_username \ --windows-admin-password $windows_admin_password \ --node-vm-size $node_size \ - --enable-addons monitoring \ + --enable-addons monitoring,azure-keyvault-secrets-provider \ --workspace-resource-id $workspace_resource_id \ --network-dataplane $network_dataplane \ --network-policy $network_policy \