|
| 1 | +--- |
| 2 | +title: Configure cross-tenant workload identity on Azure Kubernetes Service (AKS) |
| 3 | +description: Learn how to configure cross-tenant workload identity on Azure Kubernetes Service (AKS). |
| 4 | +author: schaffererin |
| 5 | +ms.topic: article |
| 6 | +ms.subservice: aks-security |
| 7 | +ms.date: 06/11/2024 |
| 8 | +ms.author: schaffererin |
| 9 | +--- |
| 10 | + |
| 11 | +# Configure cross-tenant workload identity on Azure Kubernetes Service (AKS) |
| 12 | + |
| 13 | +In this article, you learn how to configure cross-tenant workload identity on Azure Kubernetes Service (AKS). Cross-tenant workload identity allows you to access resources in another tenant from your AKS cluster. |
| 14 | + |
| 15 | +For more information on workload identity, see the [Workload identity overview][workload-identity-overview]. |
| 16 | + |
| 17 | +## Prerequisites |
| 18 | + |
| 19 | +* ***Two Azure subscriptions***, each in a separate tenant. In this article, we refer to these as *Tenant A* and *Tenant B*. |
| 20 | +* Azure CLI installed on your local machine. If you don't have the Azure CLI installed, see [Install the Azure CLI][install-azure-cli]. |
| 21 | +* X |
| 22 | +* Y |
| 23 | +* Z |
| 24 | + |
| 25 | +## Configure resources in Tenant A |
| 26 | + |
| 27 | +In *Tenant A*, you create an AKS cluster with workload identity and OIDC issuer enabled. You use this cluster to deploy an application that attempts to access resources in *Tenant B*. |
| 28 | + |
| 29 | +1. Log in to your *Tenant A* subscription using the [`az account set`][az-account-set] command. |
| 30 | + |
| 31 | + ```azurecli-interactive |
| 32 | + # Set environment variable |
| 33 | + TENANT_A_SUBSCRIPTION_ID=<subscription-id> |
| 34 | +
|
| 35 | + # Log in to your Tenant A subscription |
| 36 | + az account set --subscription $TENANT_A_SUBSCRIPTION_ID |
| 37 | + ``` |
| 38 | +
|
| 39 | +2. Create a resource group in *Tenant A* to host the AKS cluster using the [`az group create`][az-group-create] command. |
| 40 | +
|
| 41 | + ```azurecli-interactive |
| 42 | + # Set environment variables |
| 43 | + RESOURCE_GROUP=<resource-group-name> |
| 44 | + LOCATION=<location> |
| 45 | +
|
| 46 | + # Create a resource group |
| 47 | + az group create --name $RESOURCE_GROUP --location $LOCATION |
| 48 | + ``` |
| 49 | +
|
| 50 | +3. Create an AKS cluster in *Tenant A* with workload identity and OIDC issuer enabled using the [`az aks create`][az-aks-create] command. |
| 51 | +
|
| 52 | + ```azurecli-interactive |
| 53 | + # Set environment variables |
| 54 | + CLUSTER_NAME=<cluster-name> |
| 55 | +
|
| 56 | + # Create an AKS cluster |
| 57 | + az aks create --resource-group $RESOURCE_GROUP --name $CLUSTER_NAME --enable-oidc-issuer --enable-workload-identity --generate-ssh-keys |
| 58 | + ``` |
| 59 | +
|
| 60 | +4. Get the OIDC issuer URL from the cluster in *Tenant A* using the [`az aks show`][az-aks-show] command. |
| 61 | +
|
| 62 | + ```azurecli-interactive |
| 63 | + OIDC_ISSUER_URL=$(az aks show --resource-group $RESOURCE_GROUP --name $CLUSTER_NAME --query "oidcIssuerProfile.issuerUrl" --output tsv) |
| 64 | + ``` |
| 65 | +
|
| 66 | +## Configure resources in Tenant B |
| 67 | +
|
| 68 | +In *Tenant B*, you create a managed identity, assign it permissions to read subscription information, and establish the trust between the managed identity and the AKS cluster in *Tenant A*. |
| 69 | +
|
| 70 | +1. Log in to your *Tenant B* subscription using the [`az account set`][az-account-set] command. |
| 71 | +
|
| 72 | + ```azurecli-interactive |
| 73 | + # Set environment variable |
| 74 | + TENANT_B_SUBSCRIPTION_ID=<subscription-id> |
| 75 | +
|
| 76 | + # Log in to your Tenant B subscription |
| 77 | + az account set --subscription $TENANT_B_SUBSCRIPTION_ID |
| 78 | + ``` |
| 79 | +
|
| 80 | +2. Create a resource group in *Tenant B* to host the managed identity using the [`az group create`][az-group-create] command. |
| 81 | +
|
| 82 | + ```azurecli-interactive |
| 83 | + # Set environment variables |
| 84 | + RESOURCE_GROUP=<resource-group-name> |
| 85 | + LOCATION=<location> |
| 86 | +
|
| 87 | + # Create a resource group |
| 88 | + az group create --name $RESOURCE_GROUP --location $LOCATION |
| 89 | + ``` |
| 90 | +
|
| 91 | +3. Create a user-assigned managed identity in *Tenant B* using the [`az identity create`][az-identity-create] command. |
| 92 | +
|
| 93 | + ```azurecli-interactive |
| 94 | + # Set environment variable |
| 95 | + IDENTITY_NAME=<identity-name> |
| 96 | +
|
| 97 | + # Create a user-assigned managed identity |
| 98 | + az identity create --resource-group $RESOURCE_GROUP --name $IDENTITY_NAME |
| 99 | + ``` |
| 100 | +
|
| 101 | +4. Get the client ID of the managed identity in *Tenant B* using the [`az identity show`][az-identity-show] command. |
| 102 | +
|
| 103 | + ```azurecli-interactive |
| 104 | + CLIENT_ID=$(az identity show --resource-group $RESOURCE_GROUP --name $IDENTITY_NAME --query clientId --output tsv) |
| 105 | + ``` |
| 106 | +
|
| 107 | +5. Get the principal ID of the managed identity in *Tenant B* using the [`az identity show`][az-identity-show] command. |
| 108 | +
|
| 109 | + ```azurecli-interactive |
| 110 | + PRINCIPAL_ID=$(az identity show --resource-group $RESOURCE_GROUP --name $IDENTITY_NAME --query principalId --output tsv) |
| 111 | + ``` |
| 112 | +
|
| 113 | +6. Assign the managed identity in *Tenant B* permissions to read subscription information using the [`az role assignment create`][az-role-assignment-create] command. |
| 114 | +
|
| 115 | + ```azurecli-interactive |
| 116 | + az role assignment create --role "Reader" --assignee $PRINCIPAL_ID --scope /subscriptions/$TENANT_A_SUBSCRIPTION_ID |
| 117 | + ``` |
| 118 | +
|
| 119 | +## Establish trust between AKS cluster and managed identity |
| 120 | +
|
| 121 | +In this section, you create the federated identity credential needed to establish trust between the AKS cluster in *Tenant A* and the managed identity in *Tenant B*. You use the OIDC issuer URL from the AKS cluster in *Tenant A* and the name of the managed identity in *Tenant B*. |
| 122 | +
|
| 123 | +* Create a federated identity credential using the [`az aks federated-identity add`][az-aks-federated-identity-add] command. |
| 124 | +
|
| 125 | + ```azurecli-interactive |
| 126 | + az identity federated-credential create --name $FEDERATED_CREDENTIAL_NAME --identity-name $IDENTITY_NAME --resource-group $RESOURCE_GROUP --issuer $OIDC_ISSUER_URL --subject system:serviceaccount:default:wi-demo-account |
| 127 | + ``` |
| 128 | +
|
| 129 | +`--subject system:serviceaccount:default:wi-demo-account` is the name of the Kubernetes service account that you will create later in *Tenant A*. When your application pod makes authentication requests, this value is sent to Azure AD as the `subject` in the authorization request. Azure AD determines eligibility based on whether this value matches what you set when you created the federated identity credential, so it's important to ensure the value matches. |
| 130 | +
|
| 131 | +## Create application to read subscription information |
| 132 | +
|
| 133 | +XYZ |
| 134 | +
|
| 135 | +## Test application |
| 136 | +
|
| 137 | +Before you deploy the application to your AKS cluster, you test the application locally to make sure it works. Since your application will read subscription quota information using [Azure Quota API](/rest/api/reserved-vm-instances/quotaapi), you also need to make sure the `Microsoft.Quota` resource provider is registered in your subscription. |
| 138 | +
|
| 139 | +1. Log in to either *Tenant A* or *Tenant B* using the [`az account set`][az-account-set] command. |
| 140 | +
|
| 141 | + ```azurecli-interactive |
| 142 | + az account set --subscription $TENANT_A_SUBSCRIPTION_ID |
| 143 | + ``` |
| 144 | +
|
| 145 | +2. Check if the `Microsoft.Quota` resource provider is registered in your subscription using the [`az provider show`][az-provider-show] command. |
| 146 | +
|
| 147 | + ```azurecli-interactive |
| 148 | + az provider show --namespace Microsoft.Quota |
| 149 | + ``` |
| 150 | +
|
| 151 | + If the `registrationState` is `Registered`, the resource provider is registered. If it's not registered, you can register it using the [`az provider register`][az-provider-register] command. |
| 152 | +
|
| 153 | + ```azurecli-interactive |
| 154 | + az provider register --namespace Microsoft.Quota |
| 155 | + ``` |
| 156 | +
|
| 157 | +3. Once the registration state is `Registered`, you can test the application locally using the following commands: |
| 158 | +
|
| 159 | + ```azurecli-interactive |
| 160 | + XYZ |
| 161 | + ``` |
| 162 | +
|
| 163 | +## Deploy application to AKS cluster |
| 164 | +
|
| 165 | +Now that you confirmed the application works locally, you can push it to a container registry so that it can be pulled from within your AKS cluster. |
| 166 | +
|
| 167 | +1. XYZ |
| 168 | +
|
| 169 | + ```azurecli-interactive |
| 170 | + XYZ |
| 171 | + ``` |
| 172 | +
|
| 173 | +2. Get the cluster credentials for the AKS cluster in *Tenant A* using the [`az aks get-credentials`][az-aks-get-credentials] command. |
| 174 | +
|
| 175 | + ```azurecli-interactive |
| 176 | + az aks get-credentials --resource-group $RESOURCE_GROUP --name $CLUSTER_NAME |
| 177 | + ``` |
| 178 | +
|
| 179 | +3. Create a new Kubernetes service account in the `default` namespace with the client ID of your managed identity using the `kubectl apply` command. Make sure to replace the `<YOUR_USER_ASSIGNED_MANAGED_IDENTITY>` placeholder with the client ID of your managed identity in *Tenant B*. |
| 180 | +
|
| 181 | + ```azurecli-interactive |
| 182 | + kubectl apply -f - <<EOF |
| 183 | + apiVersion: v1 |
| 184 | + kind: ServiceAccount |
| 185 | + metadata: |
| 186 | + annotations: |
| 187 | + azure.workload.identity/client-id: <YOUR_USER_ASSIGNED_MANAGED_IDENTITY_CLIENT_ID> |
| 188 | + name: wi-demo-account |
| 189 | + namespace: default |
| 190 | + EOF |
| 191 | + ``` |
| 192 | +
|
| 193 | +4. Create a new pod in the `default` namespace with the image name, the *Tenant B* tenant ID, and the *Tenant B* subscription ID using the `kubectl apply` command. Make sure to replace the placeholders with your own values. |
| 194 | +
|
| 195 | + ```azurecli-interactive |
| 196 | + kubectl apply -f - <<EOF |
| 197 | + apiVersion: v1 |
| 198 | + kind: Pod |
| 199 | + metadata: |
| 200 | + name: wi-demo-app |
| 201 | + namespace: default |
| 202 | + labels: |
| 203 | + azure.workload.identity/use: "true" |
| 204 | + spec: |
| 205 | + serviceAccountName: wi-demo-account |
| 206 | + containers: |
| 207 | + - name: wi-demo-app |
| 208 | + image: <YOUR_IMAGE_NAME> |
| 209 | + env: |
| 210 | + - name: AZURE_TENANT_ID |
| 211 | + value: <TENANT_B_ID> |
| 212 | + - name: AZURE_SUBSCRIPTION_ID |
| 213 | + value: <TENANT_B_SUBSCRIPTION_ID> |
| 214 | + - name: AZURE_REGION |
| 215 | + value: eastus |
| 216 | + - name: AZURE_RESOURCE_NAME |
| 217 | + value: cores |
| 218 | + resources: {} |
| 219 | + dnsPolicy: ClusterFirst |
| 220 | + restartPolicy: Always |
| 221 | + EOF |
| 222 | + ``` |
| 223 | +
|
| 224 | +5. Verify that the pod is running using the `kubectl get pods` command. |
| 225 | +
|
| 226 | + ```azurecli-interactive |
| 227 | + kubectl get pods |
| 228 | + ``` |
| 229 | +
|
| 230 | +6. Check the logs of the pod to see if the application was able to read subscription information using the `kubectl logs` command. |
| 231 | +
|
| 232 | + ```azurecli-interactive |
| 233 | + kubectl logs wi-demo-app |
| 234 | + ``` |
| 235 | +
|
| 236 | + Your output should look similar to the following example output: |
| 237 | +
|
| 238 | + ```output |
| 239 | + 2023/08/24 17:48:03 clientResponse: {"id":"/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.Compute/locations/eastus/providers/Microsoft.Quota/quotas/cores","name":"cores","properties":{"isQuotaApplicable":true,"limit":{"limitObjectType":"LimitValue","limitType":"Independent","value":20},"name":{"localizedValue":"Total Regional vCPUs","value":"cores"},"properties":{},"unit":"Count"},"type":"Microsoft.Quota/Quotas"} |
| 240 | + 2023/08/24 17:48:04 usagesClientResponse: {"id":"/subscriptions/00000000-0000-0000-0000-000000000000/providers/Microsoft.Compute/locations/eastus/providers/Microsoft.Quota/usages/cores","name":"cores","properties":{"isQuotaApplicable":true,"name":{"localizedValue":"Total Regional vCPUs","value":"cores"},"properties":{},"unit":"Count","usages":{}},"type":"Microsoft.Quota/Usages"} |
| 241 | + ``` |
| 242 | +
|
| 243 | +## Next steps |
| 244 | +
|
| 245 | +In this article, you learned how to configure cross-tenant workload identity on Azure Kubernetes Service (AKS). To learn more about workload identity, see the following articles: |
| 246 | +
|
| 247 | +* [Workload identity overview][workload-identity-overview] |
| 248 | +* [Configure workload identity on Azure Kubernetes Service (AKS)][configure-workload-identity] |
| 249 | +
|
| 250 | +<!-- LINKS --> |
| 251 | +[workload-identity-overview]: ./workload-identity-overview.md |
| 252 | +[configure-workload-identity]: ./workload-identity-deploy-cluster.md |
| 253 | +[install-azure-cli]: /cli/azure/install-azure-cli |
| 254 | +[az-account-set]: /cli/azure/account#az_account_set |
| 255 | +[az-group-create]: /cli/azure/group#az_group_create |
| 256 | +[az-aks-create]: /cli/azure/aks#az_aks_create |
| 257 | +[az-aks-show]: /cli/azure/aks#az_aks_show |
| 258 | +[az-identity-create]: /cli/azure/identity#az_identity_create |
| 259 | +[az-identity-show]: /cli/azure/identity#az_identity_show |
| 260 | +[az-role-assignment-create]: /cli/azure/role/assignment#az_role_assignment_create |
| 261 | +[az-aks-federated-identity-add]: /cli/azure/aks/federated-identity#az_aks_federated_identity_add |
| 262 | +[az-provider-show]: /cli/azure/provider#az_provider_show |
| 263 | +[az-provider-register]: /cli/azure/provider#az_provider_register |
| 264 | +[az-aks-get-credentials]: /cli/azure/aks#az_aks_get_credentials |
0 commit comments