|
| 1 | +--- |
| 2 | +title: "Azure Native 3.8: Unified Credentials and Private Clouds" |
| 3 | + |
| 4 | +# The date represents the post's publish date, and by default corresponds with |
| 5 | +# the date and time this file was generated. Dates are used for display and |
| 6 | +# ordering purposes only; they have no effect on whether or when a post is |
| 7 | +# published. To influence the ordering of posts published on the same date, use |
| 8 | +# the time portion of the date value; posts are sorted in descending order by |
| 9 | +# date/time. |
| 10 | +date: 2025-09-04T12:00:00-07:00 |
| 11 | + |
| 12 | +# The draft setting determines whether a post is published. Set it to true if |
| 13 | +# you want to be able to merge the post without publishing it. |
| 14 | +draft: false |
| 15 | + |
| 16 | +# Use the meta_desc property to provide a brief summary (one or two sentences) |
| 17 | +# of the content of the post, which is useful for targeting search results or |
| 18 | +# social-media previews. This field is required or the build will fail the |
| 19 | +# linter test. Max length is 160 characters. |
| 20 | +meta_desc: | |
| 21 | + Pulumi Azure Native v3.8 delivers new credential modes (based on DefaultAzureCredential) |
| 22 | + and private cloud support (based on ARM_METADATA_HOSTNAME). |
| 23 | +
|
| 24 | +# The meta_image appears in social-media previews and on the blog home page. A |
| 25 | +# placeholder image representing the recommended format, dimensions and aspect |
| 26 | +# ratio has been provided for you. |
| 27 | +meta_image: meta.png |
| 28 | + |
| 29 | +# At least one author is required. The values in this list correspond with the |
| 30 | +# `id` properties of the team member files at /data/team/team. Create a file for |
| 31 | +# yourself if you don't already have one. |
| 32 | +authors: |
| 33 | + - eron-wright |
| 34 | + |
| 35 | +# At least one tag is required. Lowercase, hyphen-delimited is recommended. |
| 36 | +tags: |
| 37 | + - azure-native |
| 38 | + - azure |
| 39 | + - features |
| 40 | + - releases |
| 41 | + |
| 42 | +# The social copy used to promote this post on Twitter and Linkedin. These |
| 43 | +# properties do not actually create the post and have no effect on the |
| 44 | +# generated blog page. They are here strictly for reference. |
| 45 | + |
| 46 | +# Here are some examples of posts we have made in the past for inspiration: |
| 47 | +# https://www.linkedin.com/feed/update/urn:li:activity:7171191945841561601 |
| 48 | +# https://www.linkedin.com/feed/update/urn:li:activity:7169021002394296320 |
| 49 | +# https://www.linkedin.com/feed/update/urn:li:activity:7155606616455737345 |
| 50 | +# https://twitter.com/PulumiCorp/status/1763265391042654623 |
| 51 | +# https://twitter.com/PulumiCorp/status/1762900472489185492 |
| 52 | +# https://twitter.com/PulumiCorp/status/1755637618631405655 |
| 53 | + |
| 54 | +social: |
| 55 | + twitter: |
| 56 | + linkedin: |
| 57 | + |
| 58 | +--- |
| 59 | + |
| 60 | +Today we're excited to announce Azure Native Provider v3.8, featuring several enhancements that simplify authentication |
| 61 | +and extend support to private Azure environments. These updates make it easier than ever to manage Azure infrastructure |
| 62 | +using credentials provided by the hosting environment, such as in Azure Kubernetes Service (AKS), Azure VM, and Azure Cloud Shell. |
| 63 | + |
| 64 | +<!--more--> |
| 65 | + |
| 66 | +## Simplified Authentication Across Environments |
| 67 | + |
| 68 | +The highlight of this release is a new authentication mode based on [DefaultAzureCredential][doc1], |
| 69 | +a feature of the Azure SDK that unifies authentication-related settings across deployment environments. |
| 70 | + |
| 71 | +[doc1]: https://learn.microsoft.com/en-us/azure/developer/go/sdk/authentication/credential-chains#defaultazurecredential-overview |
| 72 | + |
| 73 | +### What's New |
| 74 | + |
| 75 | +`DefaultAzureCredential` automatically discovers and uses the best available authentication method for your environment, |
| 76 | +eliminating the need for an environment-specific configuration. It follows the Azure SDK's standard credential chain: |
| 77 | + |
| 78 | +| Order | Credential | Description | |
| 79 | +|-------|---------------------------|------------------------------------------------------------------------------------------------------------------| |
| 80 | +| 1 | [Environment][o1] | Reads environment variables (e.g., `AZURE_TENANT_ID`, `AZURE_CLIENT_ID`) to authenticate as a service principal. | |
| 81 | +| 2 | [Workload Identity][o2] | For programs run on an Azure Kubernetes Service (AKS) cluster. | |
| 82 | +| 3 | [Managed Identity][o3] | For programs deployed to an Azure compute resource (e.g., Azure Virtual Machines) or App hosting platform. | |
| 83 | +| 4 | [Azure CLI][o4] | For local development using Azure CLI's `az login` command. | |
| 84 | +| 5 | [Azure Developer CLI][o5] | For local development using Azure Developer CLI's `azd auth login` command. | |
| 85 | + |
| 86 | +[o1]: https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity#EnvironmentCredential |
| 87 | +[o2]: https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview?tabs=go |
| 88 | +[o3]: https://learn.microsoft.com/en-us/entra/identity/managed-identities-azure-resources/overview |
| 89 | +[o4]: https://learn.microsoft.com/en-us/cli/azure/authenticate-azure-cli?view=azure-cli-latest |
| 90 | +[o5]: https://learn.microsoft.com/en-us/azure/developer/azure-developer-cli/overview?tabs=windows |
| 91 | + |
| 92 | +### Real-World Benefits |
| 93 | + |
| 94 | +This means your Pulumi programs can now run seamlessly across: |
| 95 | + |
| 96 | +- Local development machines using Azure CLI credentials |
| 97 | +- CI/CD pipelines with service principals via environment variables |
| 98 | +- Azure Kubernetes Service with Workload Identity |
| 99 | +- Azure VMs and App Services with Managed Identity |
| 100 | + |
| 101 | +All without changing a single line of configuration code. |
| 102 | + |
| 103 | +### Getting Started |
| 104 | + |
| 105 | +Enable DefaultAzureCredential using Pulumi configuration: |
| 106 | + |
| 107 | +```bash |
| 108 | +pulumi config set azure-native:useDefaultAzureCredential true |
| 109 | +pulumi config set azure-native:subscriptionId <your-subscription-id> |
| 110 | +``` |
| 111 | + |
| 112 | +Note that `subscriptionId` is a required configuration setting in this (and most) authentication modes; it ensures your resources are deployed to the correct Azure subscription. |
| 113 | + |
| 114 | +## Private Azure Cloud Support |
| 115 | + |
| 116 | +This release brings improved support for [Azure private clouds][docprivatecloud]. |
| 117 | +A private cloud is a dedicated cloud computing environment used by a single organization. |
| 118 | + |
| 119 | +The provider can now automatically discover, and configure itself for, any Azure cloud based on |
| 120 | +the `ARM_METADATA_HOSTNAME` environment variable. Note that this takes precedence over the `ARM_ENVIRONMENT` variable. |
| 121 | + |
| 122 | +[docprivatecloud]: https://azure.microsoft.com/en-us/resources/cloud-computing-dictionary/what-is-a-private-cloud |
| 123 | + |
| 124 | +### How To Use |
| 125 | + |
| 126 | +The provider can query the Azure metadata service to automatically configure itself for your specific Azure environment: |
| 127 | + |
| 128 | +```bash |
| 129 | +# Using environment variable |
| 130 | +export ARM_METADATA_HOSTNAME=management.azure.example |
| 131 | + |
| 132 | +# Or via Pulumi configuration |
| 133 | +pulumi config set azure-native:metadataHost management.azure.example |
| 134 | +``` |
| 135 | + |
| 136 | +The provider expects the `2022-09-01` metadata schema, which resembles: |
| 137 | + |
| 138 | +```json |
| 139 | +{ |
| 140 | + "authentication": { |
| 141 | + "loginEndpoint": "https://login.microsoftonline.com", |
| 142 | + "audiences": [ |
| 143 | + "https://management.core.windows.net/", |
| 144 | + "https://management.azure.com/" |
| 145 | + ] |
| 146 | + }, |
| 147 | + "name": "AzureCloud", |
| 148 | + "suffixes": { |
| 149 | + "keyVaultDns": "vault.azure.net", |
| 150 | + "storage": "core.windows.net" |
| 151 | + }, |
| 152 | + "resourceManager": "https://management.azure.com/", |
| 153 | + "microsoftGraphResourceId": "https://graph.microsoft.com/" |
| 154 | +} |
| 155 | +``` |
| 156 | + |
| 157 | +The provider automatically retrieves the correct endpoints for authentication, resource management, and other services. |
| 158 | + |
| 159 | +## Disabling Instance Discovery |
| 160 | + |
| 161 | +Also in this release is a new setting, `disableInstanceDiscovery`, to determine whether the provider requests |
| 162 | +Microsoft Entra instance metadata from the login endpoint (https://login.microsoftonline.com) before authenticating. |
| 163 | +This setting is for Pulumi programs authenticating in disconnected clouds or private clouds. |
| 164 | + |
| 165 | +Setting `disableInstanceDiscovery` to `true` will completely disable both instance discovery and authority validation. |
| 166 | +Please ensure that the configured authority host is valid and trustworthy. |
| 167 | + |
| 168 | +### How To Use |
| 169 | + |
| 170 | +The provider can be configured to disable instance discovery: |
| 171 | + |
| 172 | +```bash |
| 173 | +# Using environment variable |
| 174 | +export ARM_DISABLE_INSTANCE_DISCOVERY=true |
| 175 | + |
| 176 | +# Or via Pulumi configuration |
| 177 | +pulumi config set azure-native:disableInstanceDiscovery true |
| 178 | +``` |
| 179 | + |
| 180 | +## Authentication in AKS with Workload Identity |
| 181 | + |
| 182 | +For programs run in a pod on an Azure Kubernetes Service (AKS) cluster, DefaultAzureCredential automatically uses the |
| 183 | +workload identity of the pod's service account. This workload identity could then be granted roles in Azure to deploy stack resources. |
| 184 | + |
| 185 | +{{% notes type="info" %}} |
| 186 | + |
| 187 | +Ensure that the application pods using workload identity include the label `azure.workload.identity/use: "true"` in the pod spec. |
| 188 | + |
| 189 | +{{% /notes %}} |
| 190 | + |
| 191 | +### Walkthough |
| 192 | + |
| 193 | +Let's use [Pulumi Kubernetes Operator (PKO)][pko1] to demonstrate a use case where you'd run Pulumi deployment operations in a pod |
| 194 | +and could benefit from workload identity. |
| 195 | + |
| 196 | +The operator allocates a dedicated pod for each Pulumi stack under its management, to serve as the execution environment for stack operations. |
| 197 | +Each stack may use the same or a different service account. With AKS, that service account has a _workload identity_ that providers |
| 198 | +may use to authenticate to Azure cloud and even to Pulumi cloud. |
| 199 | + |
| 200 | +[pko1]: https://www.pulumi.com/docs/iac/using-pulumi/continuous-delivery/pulumi-kubernetes-operator/ |
| 201 | + |
| 202 | +#### Create an AKS Cluster |
| 203 | + |
| 204 | +Please follow the steps in ["Deploy and configure workload identity on an Azure Kubernetes Service (AKS) cluster"][aks1] |
| 205 | +to create an AKS cluster, managed identity, and Kubernetes service account. Those steps are: |
| 206 | + |
| 207 | +1. [Create an AKS cluster](https://learn.microsoft.com/en-us/azure/aks/workload-identity-deploy-cluster#create-an-aks-cluster) |
| 208 | +2. [Retrieve the OIDC issuer URL](https://learn.microsoft.com/en-us/azure/aks/workload-identity-deploy-cluster#retrieve-the-oidc-issuer-url) |
| 209 | +3. [Create a managed identity](https://learn.microsoft.com/en-us/azure/aks/workload-identity-deploy-cluster#create-a-managed-identity) |
| 210 | +4. [Create a Kubernetes service account](https://learn.microsoft.com/en-us/azure/aks/workload-identity-deploy-cluster#create-a-kubernetes-service-account) |
| 211 | +5. [Create the federated identity credential](https://learn.microsoft.com/en-us/azure/aks/workload-identity-deploy-cluster#create-the-federated-identity-credential) |
| 212 | + |
| 213 | +You now have an Azure managed identity and associated Kubernetes service account that DefaultAzureCredential can use to authenticate to Azure cloud. |
| 214 | + |
| 215 | +Take note of the `clientId` that represents the managed identity: |
| 216 | + |
| 217 | +```shell |
| 218 | +az identity show --name "${USER_ASSIGNED_IDENTITY_NAME}" --resource-group "${RESOURCE_GROUP}" --query clientId |
| 219 | +"c2bbe0f5-6349-480a-9f6f-3a5cb3e4ecf9" |
| 220 | +``` |
| 221 | + |
| 222 | +[aks1]: https://learn.microsoft.com/en-us/azure/aks/workload-identity-deploy-cluster |
| 223 | + |
| 224 | +#### Install the Pulumi Kubernetes Operator |
| 225 | + |
| 226 | +Install the operator into the `pulumi-kubernetes-operator` namespace: |
| 227 | + |
| 228 | +```shell |
| 229 | +kubectl apply -f https://raw.githubusercontent.com/pulumi/pulumi-kubernetes-operator/refs/tags/v2.0.0/deploy/quickstart/install.yaml |
| 230 | +``` |
| 231 | + |
| 232 | +#### Configure a Pulumi Cloud Access Token |
| 233 | + |
| 234 | +By default, the operator uses Pulumi Cloud as the state backend for your stacks. |
| 235 | +Please create a `Secret` containing a Pulumi access token to be used to authenticate to Pulumi Cloud. Follow [these instructions][doctokens] to create a personal, organization, or team access token. |
| 236 | + |
| 237 | +```shell |
| 238 | +kubectl create secret generic -n ${SERVICE_ACCOUNT_NAMESPACE} pulumi-api-secret --from-literal=accessToken=${PULUMI_ACCESS_TOKEN} |
| 239 | +``` |
| 240 | + |
| 241 | +[doctokens]: https://www.pulumi.com/docs/pulumi-cloud/access-management/access-tokens/ |
| 242 | + |
| 243 | +#### Update the Kubernetes Service Account |
| 244 | + |
| 245 | +To use the service account that was created earlier, we need to grant it the `system:auth-delegator` role: |
| 246 | + |
| 247 | +```shell |
| 248 | +cat <<EOF | kubectl apply -f - |
| 249 | +apiVersion: rbac.authorization.k8s.io/v1 |
| 250 | +kind: ClusterRoleBinding |
| 251 | +metadata: |
| 252 | + name: "${SERVICE_ACCOUNT_NAMESPACE}-${SERVICE_ACCOUNT_NAME}:system:auth-delegator" |
| 253 | +roleRef: |
| 254 | + apiGroup: rbac.authorization.k8s.io |
| 255 | + kind: ClusterRole |
| 256 | + name: system:auth-delegator |
| 257 | +subjects: |
| 258 | +- kind: ServiceAccount |
| 259 | + name: "${SERVICE_ACCOUNT_NAME}" |
| 260 | + namespace: "${SERVICE_ACCOUNT_NAMESPACE}" |
| 261 | +EOF |
| 262 | +``` |
| 263 | + |
| 264 | +#### Define an Example Program |
| 265 | + |
| 266 | +Let's define a simple Pulumi program that uses Azure Native 3.8 and calls the ["getClientConfig"][azdoc1] function to |
| 267 | +access the current identity of the provider. |
| 268 | + |
| 269 | +[azdoc1]: https://www.pulumi.com/registry/packages/azure-native/api-docs/authorization/getclientconfig/ |
| 270 | + |
| 271 | +```shell |
| 272 | +cat <<"EOF" | kubectl apply -n ${SERVICE_ACCOUNT_NAMESPACE} -f - |
| 273 | +apiVersion: pulumi.com/v1 |
| 274 | +kind: Program |
| 275 | +metadata: |
| 276 | + name: sample-workload-identity |
| 277 | +program: |
| 278 | + variables: |
| 279 | + clientConfig: |
| 280 | + fn::invoke: |
| 281 | + function: azure-native:authorization:getClientConfig |
| 282 | + clientToken: |
| 283 | + fn::secret: |
| 284 | + fn::invoke: |
| 285 | + function: azure-native:authorization:getClientToken |
| 286 | + outputs: |
| 287 | + clientConfig: ${clientConfig} |
| 288 | + clientToken: ${clientToken} |
| 289 | +EOF |
| 290 | +``` |
| 291 | + |
| 292 | +#### Run the Program using Workload Identity |
| 293 | + |
| 294 | +Create a Stack object to run the Pulumi program using your service account and with `azure-native:useDefaultAzureCredential` enabled. |
| 295 | + |
| 296 | +```shell |
| 297 | +cat <<EOF | kubectl apply -f - |
| 298 | +apiVersion: pulumi.com/v1 |
| 299 | +kind: Stack |
| 300 | +metadata: |
| 301 | + name: sample-workload-identity |
| 302 | + namespace: "${SERVICE_ACCOUNT_NAMESPACE}" |
| 303 | +spec: |
| 304 | + programRef: |
| 305 | + name: sample-workload-identity |
| 306 | + stack: sample-workload-identity |
| 307 | + envRefs: |
| 308 | + PULUMI_ACCESS_TOKEN: |
| 309 | + type: Secret |
| 310 | + secret: |
| 311 | + name: pulumi-api-secret |
| 312 | + key: accessToken |
| 313 | + serviceAccountName: "${SERVICE_ACCOUNT_NAME}" |
| 314 | + config: |
| 315 | + azure-native:useDefaultAzureCredential: "true" |
| 316 | + azure-native:subscriptionId: "${SUBSCRIPTION}" |
| 317 | + workspaceTemplate: |
| 318 | + spec: |
| 319 | + podTemplate: |
| 320 | + metadata: |
| 321 | + labels: |
| 322 | + azure.workload.identity/use: "true" |
| 323 | +EOF |
| 324 | +``` |
| 325 | + |
| 326 | +Checking the stack outputs, we see a `clientId` matching that of the managed identity! |
| 327 | + |
| 328 | +```shell |
| 329 | +kubectl get stack/sample-workload-identity -oyaml |
| 330 | +``` |
| 331 | + |
| 332 | +```yaml |
| 333 | +apiVersion: pulumi.com/v1 |
| 334 | +kind: Stack |
| 335 | +metadata: |
| 336 | + name: sample-workload-identity |
| 337 | + namespace: default |
| 338 | +status: |
| 339 | + conditions: |
| 340 | + - lastTransitionTime: "2025-09-04T23:32:11Z" |
| 341 | + message: the stack has been processed and is up to date |
| 342 | + reason: ProcessingCompleted |
| 343 | + status: "True" |
| 344 | + type: Ready |
| 345 | + outputs: |
| 346 | + clientConfig: |
| 347 | + clientId: c2bbe0f5-6349-480a-9f6f-3a5cb3e4ecf9 |
| 348 | + objectId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx |
| 349 | + subscriptionId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx |
| 350 | + tenantId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx |
| 351 | + clientToken: '[secret]' |
| 352 | +``` |
| 353 | +
|
| 354 | +## Conclusion |
| 355 | +
|
| 356 | +Azure Native Provider v3.8 represents our commitment to making Azure infrastructure management work seamlessly across all deployment scenarios, |
| 357 | +from local development to production, from public cloud to private cloud. |
| 358 | +
|
| 359 | +Have questions or feedback? [Open an issue on GitHub](https://github.com/pulumi/pulumi-azure-native/issues) |
| 360 | +or join the conversation in our [Community Slack](https://slack.pulumi.com/) (#azure channel). |
0 commit comments