Workload identity federation (WIF) lets an external workload call the Dynatrace API with a token issued by its own identity provider. Dynatrace resolves that token to a service user, so you never store a Dynatrace client secret or platform token alongside your workload.
This suits any workload that already has a machine identity—an Azure virtual machine with a managed identity, a GitHub Actions job, a Google Cloud service, or a Kubernetes pod with a projected service account token.
You configure workload identity federation through the Account Management API.
iss and aud claims, then verifies the token signature against the issuer's JSON Web Key Set.Steps 3 to 5 happen inside the platform on every request. Your workload only ever handles its own identity provider token.
Because that token is short-lived and issued on demand, no Dynatrace credential is ever written to disk, baked into an image, or stored in a CI secret.
| Object | Description |
|---|---|
Trust policy | Declares an external identity provider that Dynatrace trusts, identified by an issuer URL and an audience. A trust policy is unique per issuer URL, and audience combination. One Dynatrace account can have many such trust policies. |
Service user mapping | Binds tokens that satisfy a trust policy to one service user, one environment, and a set of scopes. A trust policy can hold many mappings. Same service user can be assigned to mappings from different trust policies, as long as they do not overlap with each other. |
Claim mapping | A condition on a single claim in the external token. A mapping needs at least one claim mapping, and all of them must match for the mapping to apply. |
Each claim mapping uses a matchType:
EXACT—the claim value must match the configured value exactly.GLOB—the claim value is matched against a wildcard pattern, which is useful for matching a group of repositories, branches, or namespaces.account-idm-read and account-idm-write scopes. See OAuth clients.External tokens must carry the iss, aud, exp, iat, and sub claims.
All requests below authenticate with an OAuth bearer token, as described in OAuth clients.
| Operation | Endpoint | Required scope |
|---|---|---|
Create |
|
|
List |
|
|
Retrieve |
|
|
Update |
|
|
Change status |
|
|
Delete |
|
|
| Operation | Endpoint |
|---|---|
POST |
|
Content type |
|
Provide the following parameters in the request body.
| Parameter | Value |
|---|---|
name | Required A name for the trust policy, unique within the account. |
issuerUrl | Required The issuer URL of the external identity provider, matched against the Must use HTTPS and must not contain a query string or a fragment. Dynatrace rejects URLs that resolve to private or loopback addresses. Dynatrace discovers the issuer's JSON Web Key Set automatically from |
audience | Required The expected value of the |
description | Optional A free-text description of the trust policy. |
curl --request POST \--url 'https://api.dynatrace.com/iam/v1/accounts/{accountUuid}/wif/trust-policies' \--header 'Authorization: Bearer {your-access-token}' \--header 'Content-Type: application/json' \--data '{"name": "Azure production tenant","issuerUrl": "https://sts.windows.net/{tenantId}/","audience": "api://dynatrace-wif","description": "Workloads running in the production Azure subscription"}'
A successful request returns 201 and the created trust policy.
{"uuid": "3f1c9a2e-5d47-4b8a-9c31-7e0b2d6f8a15","name": "Azure production tenant","issuerUrl": "https://sts.windows.net/{tenantId}/","issuerUrlHash": "9b74c9897bac770ffc029102a200c5de","jwksUri": "https://sts.windows.net/{tenantId}/.well-known/openid-configuration","description": "Workloads running in the production Azure subscription","audience": "api://dynatrace-wif","status": "ACTIVE","createdAt": "2026-08-06T09:14:22Z","updatedAt": "2026-08-06T09:14:22Z","serviceUserMappings": []}
Dynatrace creates new trust policies with status ACTIVE. The jwksUri field is read-only — Dynatrace derives it automatically from the issuer's OpenID Connect discovery document at {issuerUrl}/.well-known/openid-configuration and you cannot set or override it.
curl --request GET \--url 'https://api.dynatrace.com/iam/v1/accounts/{accountUuid}/wif/trust-policies?pageSize=50&pageNumber=1' \--header 'Authorization: Bearer {your-access-token}'
The response is paginated. Use the pageNumber and pageSize query parameters to page through results.
{"pageSize": 50,"pageNumber": 1,"total": 1,"results": [{"uuid": "3f1c9a2e-5d47-4b8a-9c31-7e0b2d6f8a15","name": "Azure production tenant","issuerUrl": "https://sts.windows.net/{tenantId}/","audience": "api://dynatrace-wif","status": "ACTIVE","createdAt": "2026-08-06T09:14:22Z","updatedAt": "2026-08-06T09:14:22Z"}]}
Retrieving a single trust policy also returns its service user mappings.
curl --request GET \--url 'https://api.dynatrace.com/iam/v1/accounts/{accountUuid}/wif/trust-policies/{trustPolicyUuid}' \--header 'Authorization: Bearer {your-access-token}'
Send the complete object. Fields you omit are cleared.
curl --request PUT \--url 'https://api.dynatrace.com/iam/v1/accounts/{accountUuid}/wif/trust-policies/{trustPolicyUuid}' \--header 'Authorization: Bearer {your-access-token}' \--header 'Content-Type: application/json' \--data '{"name": "Azure production tenant","issuerUrl": "https://sts.windows.net/{tenantId}/","audience": "api://dynatrace-wif","description": "Updated description"}'
Disabling a trust policy immediately stops all workloads that rely on it from authenticating, without deleting its mappings. Disabling is the quickest way to cut off access from a compromised identity provider. Note that there still might be a short delay due to caching until this takes effect.
curl --request PATCH \--url 'https://api.dynatrace.com/iam/v1/accounts/{accountUuid}/wif/trust-policies/{trustPolicyUuid}' \--header 'Authorization: Bearer {your-access-token}' \--header 'Content-Type: application/json' \--data '{ "status": "INACTIVE" }'
Set status to ACTIVE to re-enable the trust policy.
Deleting a trust policy also removes its service user mappings.
curl --request DELETE \--url 'https://api.dynatrace.com/iam/v1/accounts/{accountUuid}/wif/trust-policies/{trustPolicyUuid}' \--header 'Authorization: Bearer {your-access-token}'
A trust policy establishes who Dynatrace trusts. A service user mapping decides what a trusted workload can do.
| Operation | Endpoint | Required scope |
|---|---|---|
Create |
|
|
List |
|
|
Retrieve |
|
|
Update |
|
|
Change status |
|
|
Delete |
|
|
| Operation | Endpoint |
|---|---|
POST |
|
Content type |
|
| Parameter | Value |
|---|---|
serviceUserUuid | Required The UUID of an active service user. Requests that match this mapping run as this user. |
environmentId | Required The environment this mapping grants access to. |
scopes | Required The service user scope subset that will be assigned to the access token issued for the matching workload. |
claimMappings | Required One or more conditions on the claims of the external token. Each entry takes a All claim mappings must match. Each |
curl --request POST \--url 'https://api.dynatrace.com/iam/v1/accounts/{accountUuid}/wif/trust-policies/{trustPolicyUuid}/mappings' \--header 'Authorization: Bearer {your-access-token}' \--header 'Content-Type: application/json' \--data '{"serviceUserUuid": "be820735-3114-4d40-9c44-dfa18fa62be9","environmentId": "{environmentId}","scopes": ["storage:logs:read", "storage:events:read"],"claimMappings": [{"claimName": "sub","claimValue": "11111111-2222-3333-4444-555555555555","matchType": "EXACT"}]}'
A successful request returns 201 and the created mapping.
{"uuid": "7a2b4c6d-8e10-4f32-b5a7-9c1d3e5f7089","serviceUserUuid": "be820735-3114-4d40-9c44-dfa18fa62be9","environmentId": "{environmentId}","scopes": ["storage:logs:read", "storage:events:read"],"status": "ACTIVE","claimMappings": [{"claimName": "sub","claimValue": "11111111-2222-3333-4444-555555555555","matchType": "EXACT"}],"createdAt": "2026-08-06T09:22:41Z","updatedAt": "2026-08-06T09:22:41Z"}
An external token must match exactly one mapping. If it matches none, or more than one, the request fails with an HTTP 401. Keep claim mappings specific enough to stay unambiguous.
To grant a group of workloads the same access, use a GLOB match instead of listing each identity.
{"claimName": "sub","claimValue": "repo:my-org/my-service:ref:refs/heads/*","matchType": "GLOB"}
curl --request GET \--url 'https://api.dynatrace.com/iam/v1/accounts/{accountUuid}/wif/trust-policies/{trustPolicyUuid}/mappings?pageSize=50&pageNumber=1' \--header 'Authorization: Bearer {your-access-token}'
curl --request GET \--url 'https://api.dynatrace.com/iam/v1/accounts/{accountUuid}/wif/trust-policies/{trustPolicyUuid}/mappings/{mappingUuid}' \--header 'Authorization: Bearer {your-access-token}'
Send the complete object, including all claim mappings you want to keep.
curl --request PUT \--url 'https://api.dynatrace.com/iam/v1/accounts/{accountUuid}/wif/trust-policies/{trustPolicyUuid}/mappings/{mappingUuid}' \--header 'Authorization: Bearer {your-access-token}' \--header 'Content-Type: application/json' \--data '{"serviceUserUuid": "be820735-3114-4d40-9c44-dfa18fa62be9","environmentId": "{environmentId}","scopes": ["storage:logs:read"],"claimMappings": [{"claimName": "sub","claimValue": "11111111-2222-3333-4444-555555555555","matchType": "EXACT"}]}'
curl --request PATCH \--url 'https://api.dynatrace.com/iam/v1/accounts/{accountUuid}/wif/trust-policies/{trustPolicyUuid}/mappings/{mappingUuid}' \--header 'Authorization: Bearer {your-access-token}' \--header 'Content-Type: application/json' \--data '{ "status": "INACTIVE" }'
curl --request DELETE \--url 'https://api.dynatrace.com/iam/v1/accounts/{accountUuid}/wif/trust-policies/{trustPolicyUuid}/mappings/{mappingUuid}' \--header 'Authorization: Bearer {your-access-token}'
Once a trust policy and a matching service user mapping exist, the workload authenticates by attaching its own identity provider token to the Authorization header, preceded by the Bearer realm. Note that some workloads, like the OTel Collector, already implement libraries that handle the authorization header with every http request, as well as refreshing the OIDC token.
--header 'Authorization: Bearer {your-oidc-token}'
There is nothing to exchange and no Dynatrace token to manage. Dynatrace resolves the token to the mapped service user on each request.
curl --request POST \--url 'https://{environmentId}.apps.dynatrace.com/platform/storage/query/v1/query:execute' \--header 'Authorization: Bearer {your-oidc-token}' \--header 'Content-Type: application/json' \--data '{ "query": "fetch logs | limit 10" }'
Request a fresh token from your identity provider whenever the current one expires. These tokens are short-lived by design.
Any problem with the token results in an HTTP 401 response. Dynatrace doesn't distinguish between an unknown issuer, an invalid signature, an expired token, an inactive trust policy, and a claim set that matches no mapping or more than one. If you get a 401, check the validation rules and confirm your trust policy and service user mapping against the claims your identity provider actually issues.
This example gives an Azure virtual machine read access to logs in a Dynatrace environment. The virtual machine uses a managed identity, so no secret is stored anywhere.
Register an application in Microsoft Entra ID and set its Application ID URI, for example api://dynatrace-wif. This value becomes the aud claim of the tokens your workloads request, and the audience of the Dynatrace trust policy. You don't need a client secret.
Note your Entra tenant ID and the object ID of the managed identity assigned to the virtual machine.
The issuer URL for Microsoft Entra tokens is https://sts.windows.net/{tenantId}/, including the trailing slash.
curl --request POST \--url 'https://api.dynatrace.com/iam/v1/accounts/{accountUuid}/wif/trust-policies' \--header 'Authorization: Bearer {your-access-token}' \--header 'Content-Type: application/json' \--data '{"name": "Azure production tenant","issuerUrl": "https://sts.windows.net/{tenantId}/","audience": "api://dynatrace-wif"}'
Note the uuid from the response.
Match on the sub claim, which for a managed identity is the object ID of its service principal.
curl --request POST \--url 'https://api.dynatrace.com/iam/v1/accounts/{accountUuid}/wif/trust-policies/{trustPolicyUuid}/mappings' \--header 'Authorization: Bearer {your-access-token}' \--header 'Content-Type: application/json' \--data '{"serviceUserUuid": "be820735-3114-4d40-9c44-dfa18fa62be9","environmentId": "{environmentId}","scopes": ["storage:logs:read"],"claimMappings": [{"claimName": "sub","claimValue": "{managed-identity-object-id}","matchType": "EXACT"}]}'
From inside the virtual machine, request a token from the Azure Instance Metadata Service for the audience you exposed in Step 1.
AZURE_TOKEN=$(curl -s \--header 'Metadata: true' \--url 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=api://dynatrace-wif' \| jq -r '.access_token')
Pass the Entra token straight to Dynatrace. The request runs as the mapped service user.
curl --request POST \--url 'https://{environmentId}.apps.dynatrace.com/platform/storage/query/v1/query:execute' \--header "Authorization: Bearer $AZURE_TOKEN" \--header 'Content-Type: application/json' \--data '{ "query": "fetch logs | limit 10" }'
Repeat Step 4 whenever the Entra token expires.
For the maximum number of trust policies per account and service user mappings per trust policy, see Identity and Access Management limits.
| Rule | Detail |
|---|---|
Trust policy uniqueness | One trust policy per combination of account, issuer URL, and audience. |
Issuer URL | HTTPS only, up to 2048 characters, and no query string or fragment. Dynatrace rejects URLs that resolve to private or loopback addresses. |
Audience | Non-empty, up to 512 characters. Compared exactly: the trust policy audience must equal one value in the external token's |
Signature algorithms | ES256 and RS256. |
Required token claims |
|
Claim mappings | At least one per service user mapping. Each |