Azure Native 3.8: Unified Credentials and Private Clouds

Posted on

Today we’re excited to announce Azure Native Provider v3.8, featuring several enhancements that simplify authentication and extend support to private Azure environments. These updates make it easier than ever to manage Azure infrastructure using credentials provided by the hosting environment, such as in Azure Kubernetes Service (AKS), Azure VM, and Azure Cloud Shell.

Simplified Authentication Across Environments

The highlight of this release is a new authentication mode based on DefaultAzureCredential, a feature of the Azure SDK that unifies authentication-related settings across deployment environments.

What’s New

DefaultAzureCredential automatically discovers and uses the best available authentication method for your environment, eliminating the need for an environment-specific configuration. It follows the Azure SDK’s standard credential chain:

OrderCredentialDescription
1EnvironmentReads environment variables (e.g., AZURE_TENANT_ID, AZURE_CLIENT_ID) to authenticate as a service principal.
2Workload IdentityFor programs run on an Azure Kubernetes Service (AKS) cluster.
3Managed IdentityFor programs deployed to an Azure compute resource (e.g., Azure Virtual Machines) or App hosting platform.
4Azure CLIFor local development using Azure CLI’s az login command.
5Azure Developer CLIFor local development using Azure Developer CLI’s azd auth login command.

Real-World Benefits

This means your Pulumi programs can now run seamlessly across:

  • Local development machines using Azure CLI credentials
  • CI/CD pipelines with service principals via environment variables
  • Azure Kubernetes Service with Workload Identity
  • Azure VMs and App Services with Managed Identity

All without changing a single line of configuration code.

Getting Started

Enable DefaultAzureCredential using Pulumi configuration:

pulumi config set azure-native:useDefaultAzureCredential true
pulumi config set azure-native:subscriptionId <your-subscription-id>

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.

Private Azure Cloud Support

This release brings improved support for Azure private clouds. A private cloud is a dedicated cloud computing environment used by a single organization.

The provider can now automatically discover, and configure itself for, any Azure cloud based on the ARM_METADATA_HOSTNAME environment variable. Note that this takes precedence over the ARM_ENVIRONMENT variable.

How To Use

The provider can query the Azure metadata service to automatically configure itself for your specific Azure environment:

# Using environment variable
export ARM_METADATA_HOSTNAME=management.azure.example

# Or via Pulumi configuration
pulumi config set azure-native:metadataHost management.azure.example

The provider expects the 2022-09-01 metadata schema, which resembles:

{
  "authentication": {
    "loginEndpoint": "https://login.microsoftonline.com",
    "audiences": [
      "https://management.core.windows.net/",
      "https://management.azure.com/"
    ]
  },
  "name": "AzureCloud",
  "suffixes": {
    "keyVaultDns": "vault.azure.net",
    "storage": "core.windows.net"
  },
  "resourceManager": "https://management.azure.com/",
  "microsoftGraphResourceId": "https://graph.microsoft.com/"
}

The provider automatically retrieves the correct endpoints for authentication, resource management, and other services.

Disabling Instance Discovery

Also in this release is a new setting, disableInstanceDiscovery, to determine whether the provider requests Microsoft Entra instance metadata from the login endpoint (https://login.microsoftonline.com) before authenticating. This setting is for Pulumi programs authenticating in disconnected clouds or private clouds.

Setting disableInstanceDiscovery to true will completely disable both instance discovery and authority validation. Please ensure that the configured authority host is valid and trustworthy.

How To Use

The provider can be configured to disable instance discovery:

# Using environment variable
export ARM_DISABLE_INSTANCE_DISCOVERY=true

# Or via Pulumi configuration
pulumi config set azure-native:disableInstanceDiscovery true

Authentication in AKS with Workload Identity

For programs run in a pod on an Azure Kubernetes Service (AKS) cluster, DefaultAzureCredential automatically uses the workload identity of the pod’s service account. This workload identity could then be granted roles in Azure to deploy stack resources.

Ensure that the application pods using workload identity include the label azure.workload.identity/use: "true" in the pod spec.

Walkthough

Let’s use Pulumi Kubernetes Operator (PKO) to demonstrate a use case where you’d run Pulumi deployment operations in a pod and could benefit from workload identity.

The operator allocates a dedicated pod for each Pulumi stack under its management, to serve as the execution environment for stack operations. Each stack may use the same or a different service account. With AKS, that service account has a workload identity that providers may use to authenticate to Azure cloud and even to Pulumi cloud.

Create an AKS Cluster

Please follow the steps in “Deploy and configure workload identity on an Azure Kubernetes Service (AKS) cluster” to create an AKS cluster, managed identity, and Kubernetes service account. Those steps are:

  1. Create an AKS cluster
  2. Retrieve the OIDC issuer URL
  3. Create a managed identity
  4. Create a Kubernetes service account
  5. Create the federated identity credential

You now have an Azure managed identity and associated Kubernetes service account that DefaultAzureCredential can use to authenticate to Azure cloud.

Take note of the clientId that represents the managed identity:

az identity show --name "${USER_ASSIGNED_IDENTITY_NAME}" --resource-group "${RESOURCE_GROUP}" --query clientId
"c2bbe0f5-6349-480a-9f6f-3a5cb3e4ecf9"

Install the Pulumi Kubernetes Operator

Install the operator into the pulumi-kubernetes-operator namespace:

kubectl apply -f https://raw.githubusercontent.com/pulumi/pulumi-kubernetes-operator/refs/tags/v2.0.0/deploy/quickstart/install.yaml

Configure a Pulumi Cloud Access Token

By default, the operator uses Pulumi Cloud as the state backend for your stacks. Please create a Secret containing a Pulumi access token to be used to authenticate to Pulumi Cloud. Follow these instructions to create a personal, organization, or team access token.

kubectl create secret generic -n ${SERVICE_ACCOUNT_NAMESPACE} pulumi-api-secret --from-literal=accessToken=${PULUMI_ACCESS_TOKEN}

Update the Kubernetes Service Account

To use the service account that was created earlier, we need to grant it the system:auth-delegator role:

cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: "${SERVICE_ACCOUNT_NAMESPACE}-${SERVICE_ACCOUNT_NAME}:system:auth-delegator"
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:auth-delegator
subjects:
- kind: ServiceAccount
  name: "${SERVICE_ACCOUNT_NAME}"
  namespace: "${SERVICE_ACCOUNT_NAMESPACE}"
EOF

Define an Example Program

Let’s define a simple Pulumi program that uses Azure Native 3.8 and calls the “getClientConfig” function to access the current identity of the provider.

cat <<"EOF" | kubectl apply -n ${SERVICE_ACCOUNT_NAMESPACE} -f -
apiVersion: pulumi.com/v1
kind: Program
metadata:
  name: sample-workload-identity
program:
  variables:
    clientConfig:
      fn::invoke:
        function: azure-native:authorization:getClientConfig
    clientToken:
      fn::secret:
        fn::invoke:
          function: azure-native:authorization:getClientToken
  outputs:
    clientConfig: ${clientConfig}
    clientToken: ${clientToken}
EOF

Run the Program using Workload Identity

Create a Stack object to run the Pulumi program using your service account and with azure-native:useDefaultAzureCredential enabled.

cat <<EOF | kubectl apply -f -
apiVersion: pulumi.com/v1
kind: Stack
metadata:
  name: sample-workload-identity
  namespace: "${SERVICE_ACCOUNT_NAMESPACE}"
spec:
  programRef:
    name: sample-workload-identity
  stack: sample-workload-identity
  envRefs:
    PULUMI_ACCESS_TOKEN:
      type: Secret
      secret:
        name: pulumi-api-secret
        key: accessToken
  serviceAccountName: "${SERVICE_ACCOUNT_NAME}"
  config:
    azure-native:useDefaultAzureCredential: "true"
    azure-native:subscriptionId: "${SUBSCRIPTION}"
  workspaceTemplate:
    spec:
      podTemplate:
        metadata:
          labels:
            azure.workload.identity/use: "true"
EOF

Checking the stack outputs, we see a clientId matching that of the managed identity!

kubectl get stack/sample-workload-identity -oyaml
apiVersion: pulumi.com/v1
kind: Stack
metadata:
  name: sample-workload-identity
  namespace: default
status:
  conditions:
  - lastTransitionTime: "2025-09-04T23:32:11Z"
    message: the stack has been processed and is up to date
    reason: ProcessingCompleted
    status: "True"
    type: Ready
  outputs:
    clientConfig:
      clientId: c2bbe0f5-6349-480a-9f6f-3a5cb3e4ecf9
      objectId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
      subscriptionId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
      tenantId: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
    clientToken: '[secret]'

Conclusion

Azure Native Provider v3.8 represents our commitment to making Azure infrastructure management work seamlessly across all deployment scenarios, from local development to production, from public cloud to private cloud.

Have questions or feedback? Open an issue on GitHub or join the conversation in our Community Slack (#azure channel).