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:
Order | Credential | Description |
---|---|---|
1 | Environment | Reads environment variables (e.g., AZURE_TENANT_ID , AZURE_CLIENT_ID ) to authenticate as a service principal. |
2 | Workload Identity | For programs run on an Azure Kubernetes Service (AKS) cluster. |
3 | Managed Identity | For programs deployed to an Azure compute resource (e.g., Azure Virtual Machines) or App hosting platform. |
4 | Azure CLI | For local development using Azure CLI’s az login command. |
5 | Azure Developer CLI | For 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.
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:
- Create an AKS cluster
- Retrieve the OIDC issuer URL
- Create a managed identity
- Create a Kubernetes service account
- 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).