Skip to content

Add Azure delegated token authentication provider - #4591

Draft
nspringdd wants to merge 8 commits into
masterfrom
nathan.spring/azure-delegated-auth
Draft

nspringdd wants to merge 8 commits into
masterfrom
nathan.spring/azure-delegated-auth

Conversation

@nspringdd

@nspringdd nspringdd commented Sep 10, 2026

Copy link
Copy Markdown

Summary

Adds Azure support for delegated authentication through a new AzureAuth provider.

Applications can provide a pre-minted Microsoft Entra ID access token or inject a TokenSource that acquires and renews tokens. The client does not invoke the Azure CLI or add an Azure SDK dependency.

Review guide

The change is split into four parts:

  1. api/datadog/azure.go defines AzureAuth, token resolution, and proof construction.
  2. api/datadog/configuration.go loads DD_AZURE_ACCESS_TOKEN into the default context.
  3. .generator/ contains the matching templates so regeneration preserves the feature.
  4. tests/api/azure_test.go covers token precedence, failures, and the authentication proof.

Token resolution

AzureAuth resolves an access token in this order:

  1. AzureAuth.AccessToken
  2. ContextAzureVariables or DD_AZURE_ACCESS_TOKEN
  3. AzureAuth.TokenSource

TokenSource has this signature:

type AzureTokenSource func(context.Context) (string, error)

The application owns credential selection, tenant selection, audience or scope, and token renewal. This lets applications adapt Managed Identity, Workload Identity, or an explicitly selected developer credential without making those choices inside the API client.

Authentication flow

After resolving the access token, AzureAuth includes the Datadog organization UUID in the Azure delegated authentication proof and exchanges it for a Datadog delegated token through the existing DelegatedTokenProvider interface.

Usage

Using a pre-minted token from DD_AZURE_ACCESS_TOKEN:

configuration := datadog.NewConfiguration()
configuration.DelegatedTokenConfig = &datadog.DelegatedTokenConfig{
    OrgUUID:      orgUUID,
    ProviderAuth: &datadog.AzureAuth{},
    Provider:     datadog.ProviderAzure,
}

Using application-managed token acquisition:

configuration.DelegatedTokenConfig.ProviderAuth = &datadog.AzureAuth{
    TokenSource: applicationAzureTokenSource,
}

applicationAzureTokenSource is any func(context.Context) (string, error) that returns a current Microsoft Entra ID access token.

Testing

tests/api/azure_test.go covers:

  • End-to-end proof construction
  • Field, context, environment, and token-source precedence
  • Context forwarding to TokenSource
  • Wrapped token-source errors
  • Empty-token rejection
  • Missing-source and missing-organization errors

Verified with:

  • go test ./api -run '^TestAzure' from the tests module
  • go test ./api/datadog
  • go test ./examples/datadog/azure

Related change

PR #4590 adds the corresponding GCP provider. The two PRs may have small mechanical conflicts in shared generator and configuration maps if merged out of order.

Adds AzureAuth, a DelegatedTokenProvider that exchanges a Microsoft
Entra ID (Azure) access token for a Datadog delegated token, mirroring
the existing AWSAuth and the delegated-token contract.

The access token is sourced from (in order): the AzureAuth.AccessToken
field, the DD_AZURE_ACCESS_TOKEN environment variable (or its
ContextAzureVariables context override), or minted via
'az account get-access-token' (optionally scoped with the Resource
field).

Org routing follows the delegated-token servicer contract: the proof is
'<access-token>:<org-uuid>' - the servicer splits on the last colon
(JWTs contain no colons) and treats the suffix as the org UUID.

Also mirrors the provider into the code generator: azure.j2 (extra
file), configuration.j2 (ContextAzureVariables + DD_AZURE_ACCESS_TOKEN
env pickup), and example_azure.j2 (examples/datadog/azure/main.go) so
the provider survives regeneration.

Co-authored-by: Nathan Spring <nathan.spring@datadoghq.com>
The inline delegatedTokenTestContext copy in azure_test.go collides
with the identically-named helper added by the GCP delegated auth PR
(#4590) - whichever PR merges second would break the tests module
compile on master. Restore the shared provider-generic helper file
from the GCP branch instead: identical content on both branches means
the second merge is a no-op for this path.

Co-authored-by: Nathan Spring <nathan.spring@datadoghq.com>
- The new no-token error case empties PATH in the test loop so
  exec.LookPath("az") fails deterministically on machines with the
  Azure CLI installed (dev boxes and CI alike); previously the case
  would shell out to a real, logged-in az.
- exec.ExitError.Error() renders only 'exit status N', dropping the
  stderr Output() captured (e.g. login-required diagnostics). Surface
  the trimmed stderr in the wrapped error so mint failures are
  diagnosable.

Co-authored-by: Nathan Spring <nathan.spring@datadoghq.com>
Tests a fake az executable on PATH exercising the real
mintAccessToken path hermetically: pins JSON output mode (token
extracted without depending on JMESPath behavior across az versions),
the --resource passthrough, token parsing, and the error path
surfacing CLI stderr. Works on machines with and without the Azure
CLI installed.

Co-authored-by: Nathan Spring <nathan.spring@datadoghq.com>
The helpers are unexported in package datadog and the tests module
cannot call them directly; the mint paths are covered end to end by
the PATH-shim tests instead.

Co-authored-by: Nathan Spring <nathan.spring@datadoghq.com>
The stderr-fix commit updated azure.go but not its generator template,
and the factored-out-for-testing comment cleanup also missed the
template - the next regeneration would have reverted both changes.
Rebuilt azure.j2 from the checked-in body; render parity verified.

Co-authored-by: Nathan Spring <nathan.spring@datadoghq.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant