To let a service call the Dataverse Web API without a signed-in user, register an application in Microsoft Entra ID with a client secret or certificate, then in the Power Platform admin center create an application user for that app in each environment and assign it a least-privilege custom security role. The service requests a token with the OAuth 2.0 client credentials grant using the scope https://<environment>.crm.dynamics.com/.default and calls api/data/v9.2. No Dynamics 365 or Power Apps licence is needed for the application user, and no delegated API permission is required on the app registration.
Who this is for and what you will have at the end
This guide is for administrators and developers setting up unattended access to Dataverse for integrations: middleware, Azure Functions, Azure Data Factory pipelines, scheduled scripts and partner products.
At the end you will have:
- An app registration with a credential that can obtain Dataverse tokens.
- An application user in the target environment with a custom security role.
- A tested token request and Web API call.
- A map from common errors to their causes.
How server-to-server access works
Server-to-server (S2S) authentication has three parts:
| Part | Where | Purpose |
|---|---|---|
| App registration | Microsoft Entra ID | Identity of the service, with a secret, certificate or managed identity |
| Application user | Each Dataverse environment | A systemuser record bound to the app's service principal |
| Security role | Each Dataverse environment | Controls which tables and operations the application user can access |
Microsoft's documentation highlights these characteristics:
- All operations run as the application user, not as whoever is using your application. If your service must act on behalf of a specific user, it can impersonate that user, provided its security role has the required privileges.
- There's no licence fee for application users, but their requests count against the tenant's pooled limit for non-licensed users. Application users, non-interactive users and the SYSTEM user share that pool.
- Only one application user is supported per Entra-registered application in an environment.
- Application users can run in environments secured with a security group without being members of that group.
- Service protection limits apply to application users exactly as they do to other users; see Dataverse service protection API limits.
Use S2S rather than a named service account with a password. Microsoft's guidance is not to run unattended scenarios with an ordinary user's credentials, because that account needs a paid licence; an application user bound to an app registration doesn't consume one.
Prerequisites
- A Microsoft Entra role that can register applications.
- Administrator privileges in the Dataverse environment, so you can create users and assign roles.
- The environment URL, for example
https://contoso.crm.dynamics.com.pac admin listshows the URL of every environment. - A plan for the data the integration needs, so you can build the security role before you create the user.
Step 1: Register the application in Microsoft Entra ID
- In the Microsoft Entra admin center, go to Applications > App registrations and select + New registration.
- Enter a name that makes the integration's owner obvious, for example
dataverse-orders-sync. - Select Accounts in this organizational directory only for a single-tenant integration, and select Register. A redirect URI isn't needed.
- On Overview, record the Application (client) ID and Directory (tenant) ID.
- Under Manage > Certificates & secrets, add a credential:
- Certificate: select Upload certificate and upload the public key as a
.cer,.pemor.crtfile. - Client secret: select + New client secret, add a description and expiry, and copy the value immediately; it isn't shown again.
- Certificate: select Upload certificate and upload the public key as a
You don't need to add the Access Dynamics 365 as organization users delegated permission. That permission is for apps that act as a signed-in user; S2S authorises the app through the application user in Dataverse.
If the workload runs in Azure, a managed identity avoids storing any credential. You use its Application ID in Step 3.
Step 2: Create a custom security role
Microsoft's guidance is to give the application user a custom security role, stored in a solution so it can be deployed with the application to other environments.
Design it from what the integration actually does:
- Grant only the tables it reads or writes, with the narrowest access level that works.
- Leave out delete, assign and share privileges unless the integration needs them.
- If the service impersonates users, it needs the privilege to act on behalf of another user; otherwise leave it out.
Avoid System Administrator for integrations. It's convenient for a first test, but it gives the service full control of the environment, and some tooling assigns it by default (see the CLI section below).
Step 3: Create the application user
In the Power Platform admin center:
- Sign in to the Power Platform admin center and select Manage in the navigation pane.
- In the Manage pane, select Environments, then select the environment.
- Select Settings, then Users + permissions > Application users.
- Select + New app user to open Create a new app user.
- Select + Add an app, choose the app registration from Step 1 and select Add. You can search by application name or Application ID. For a managed identity, enter its Application ID, not its name. Enterprise applications don't appear in the list; search for a multitenant app by name or ID.
- Under Business Unit, select a business unit, then enter an Email address.
- Select the edit icon next to security roles, choose the custom role from Step 2, select Save, and confirm with Save.
- Select Create.
The application user is active as soon as it's created. Its Details page shows the name, Microsoft Entra application ID, state, roles, app type, business unit and email address. You can change only the business unit, email address and security roles afterwards; the Application ID can't be changed.
Repeat this step in every environment the integration must reach. Application users don't carry over between environments.
Alternative: Power Platform CLI
For scripted environment setup, the Power Platform CLI can assign an existing app registration as an application user:
pac admin list-roles --environment https://contoso.crm.dynamics.com
pac admin assign-user `
--environment https://contoso.crm.dynamics.com `
--user 00001111-aaaa-2222-bbbb-3333cccc4444 `
--role "Orders Integration" `
--application-userWith --application-user, the --user value is the Application ID. If you don't pass --business-unit, the application user goes into the business unit of the account running the command.
pac admin create-service-principal creates a new Entra application and its application user in one step, but its --role parameter defaults to System Administrator and it prints the client secret in clear text. If you use it, pass a least-privilege role and move the secret to a vault straight away. pac admin list-service-principal lists Entra applications that have access to Dataverse.
Step 4: Get a token and call the Web API
Request a token from the Microsoft identity platform with the client credentials grant. The scope is the environment URL plus /.default:
# Replace {tenant} with your Directory (tenant) ID.
# The scope is https://contoso.crm.dynamics.com/.default, URL-encoded.
curl -X POST -H "Content-Type: application/x-www-form-urlencoded" \
-d 'client_id=00001111-aaaa-2222-bbbb-3333cccc4444&scope=https%3A%2F%2Fcontoso.crm.dynamics.com%2F.default&client_secret=<url-encoded secret>&grant_type=client_credentials' \
'https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token'A successful response contains token_type, expires_in and access_token. Send the access token as a bearer token:
curl -X GET \
-H "Authorization: Bearer <access_token>" \
-H "Accept: application/json" \
-H "OData-MaxVersion: 4.0" \
-H "OData-Version: 4.0" \
'https://contoso.crm.dynamics.com/api/data/v9.2/WhoAmI'With a certificate, replace client_secret with client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer and a client_assertion JWT signed with the certificate; Microsoft Authentication Library (MSAL) builds the assertion for you. The token is valid for about an hour, and the client credentials flow never returns a refresh token, so request a new token when it expires.
In .NET, ServiceClient from the Dataverse SDK handles token acquisition. Connection strings for the two credential types:
AuthType=ClientSecret;url=https://contoso.crm.dynamics.com;ClientId=00001111-aaaa-2222-bbbb-3333cccc4444;ClientSecret=<secret>
AuthType=Certificate;url=https://contoso.crm.dynamics.com;ClientId=00001111-aaaa-2222-bbbb-3333cccc4444;Thumbprint=<certificate thumbprint>Microsoft recommends using the most secure flow available and reserving secret-based connection strings for cases where options such as managed identities aren't viable. Keep secrets out of source control and configuration files; load them from a secret store at run time.
Verification
- The
WhoAmIresponse returnsUserId,BusinessUnitIdandOrganizationId. TheUserIdshould be the application user'ssystemuserid, not a person's. - In the Power Platform admin center, open the application user's Details and confirm the state is active and only the intended roles are assigned.
- Run one real operation the integration needs, such as reading a row from each table it uses, and one it shouldn't be able to perform, such as deleting a row. The second should fail with a missing privilege error.
Troubleshooting
AADSTS70011: The provided value for the input parameter 'scope' is not valid. The scope is malformed or names the wrong resource. Use the exact environment URL with /.default, one resource per request.
The user is not a member of the organization. (0x80072560, UserNotMemberOfOrg). The token is valid but there's no application user for this app in the environment you called. Create it in that environment, and check you're calling the environment you think you are.
... is missing ... privilege on ... entity ... Consider adding missing privilege to one of the principal (user/team) roles for the request to succeed. (0x80040220, PrivilegeDenied). The full message names the principal, the missing privilege and the table. Add that privilege to the application user's custom role rather than assigning a broader role.
Integration stops after an admin cleanup. Someone deactivated the application user. Microsoft warns that disabling an application user breaks every integration that uses it. Reactivate it from Application users > Activate.
429 Too Many Requests. The application user has hit a service protection limit. Retry after the Retry-After interval and review parallelism.
App not found in the Add an app list. The list shows app registrations, not enterprise applications. Search by Application ID; for a managed identity, use its Application ID.
Operating application users
- Name drift: if the Entra app is renamed, the Details page shows Refresh to resync the application user name.
- Role changes: use Edit security roles on the Application users page; the selected roles replace the current ones.
- Retirement: deactivate the application user first. Only inactive application users can be deleted, and you must reassign the records they own before deletion. Remove the credential or the app registration in Entra ID as well.
- Credentials: track secret and certificate expiry and rotate before they expire; prefer certificates or managed identities.
The same identity-first approach applies across a Zero Trust estate; see zero trust enterprise remote access architecture. If the application user also receives events from Dataverse, compare the push options in Dataverse integration choices.
Checklist
- One app registration per integration, named after its owner and purpose.
- No delegated Dynamics 365 permission on the app registration.
- Certificate or managed identity preferred; any secret stored in a vault with its expiry tracked.
- Custom security role in a solution, least privilege, never System Administrator.
- Application user created in each required environment with the correct business unit.
- Token scope
https://<environment>/.default, tested withWhoAmI. - Negative test confirms the role blocks operations the integration shouldn't perform.
- Retry logic for
429in place before go-live.
References
- Manage application users in the Power Platform admin center
- Use single-tenant server-to-server authentication
- Build web applications using server-to-server (S2S) authentication
- Use OAuth authentication with Microsoft Dataverse
- OAuth 2.0 client credentials flow on the Microsoft identity platform
- Use XRM tooling connection strings for Dataverse
- Microsoft Power Platform CLI admin command group
- Web service error codes
- Requests limits and allocations
- Service protection API limits