DataverseConnection is a .NET 8 class library that provides reusable, dependency-injectable connection logic for Microsoft Dataverse. It is opinionated about interactive authentication: when used from the CLI you choose between three human-friendly credential types, while library callers can still plug in any TokenCredential.
The included DataverseWhoAmI console application demonstrates the library and verifies connectivity.
- Reusable .NET 8 library for Dataverse connectivity
- Dependency injection through
AddDataverse,AddDataverseWithOrganizationServices, andAddDataverseFactory - Three opinionated Azure Identity credential types:
InteractiveBrowserCredential(default)DeviceCodeCredentialAzureCliCredential
- Any explicitly supplied
TokenCredentialwhen calling the library directly (for exampleDefaultAzureCredentialor a service principal) - Credential-specific Azure Identity options
- Persistent token caching by default for the interactive credentials, so you log in as rarely as possible
- Token caching isolated by credential instance and Dataverse resource
- Cross-platform support for Windows, Linux, and macOS
- .NET 8 SDK
- Access to a Microsoft Dataverse environment
- An Azure identity with access to that environment
using DataverseConnection;
using Microsoft.Extensions.DependencyInjection;
var services = new ServiceCollection();
services.AddDataverse(options =>
{
// Optional when DATAVERSE_URL is available through IConfiguration.
options.DataverseUrl = "https://yourorg.crm4.dynamics.com";
});If no credential type or custom credential is specified, the library uses InteractiveBrowserCredential — a person running the tool from a computer can always complete a browser sign-in.
Tip: You usually don't need to configure anything in code. If you register an
IConfiguration, the library reads the Dataverse URL and credential type from your app settings automatically — see Configuration. Use theconfigureOptionscallback only when a tool needs something specific.
Set DataverseOptions.CredentialType to one of the three opinionated Azure Identity credentials. Selecting a type does not create a fallback chain.
| Value | Credential | Notes |
|---|---|---|
InteractiveBrowserCredential (default) |
InteractiveBrowserCredential |
Opens a browser sign-in; persistent token cache by default. |
DeviceCodeCredential |
DeviceCodeCredential |
Prints a code to sign in from any device; persistent token cache by default. |
AzureCliCredential |
AzureCliCredential |
Reuses an existing az login session (the az CLI owns its own cache). |
This requires an authenticated Azure CLI session, normally created with az login.
services.AddDataverse(options =>
{
options.DataverseUrl = "https://yourorg.crm4.dynamics.com";
options.CredentialType = DataverseCredentialType.AzureCliCredential;
});You can provide Azure CLI-specific options:
using Azure.Identity;
services.AddDataverse(options =>
{
options.CredentialType = DataverseCredentialType.AzureCliCredential;
options.AzureCliCredentialOptions = new AzureCliCredentialOptions
{
TenantId = "<tenant-id>"
};
});using Azure.Identity;
services.AddDataverse(options =>
{
options.DataverseUrl = "https://yourorg.crm4.dynamics.com";
options.CredentialType = DataverseCredentialType.DeviceCodeCredential;
options.DeviceCodeCredentialOptions = new DeviceCodeCredentialOptions
{
TenantId = "<tenant-id>",
ClientId = "<application-client-id>"
};
});The device-code credential presents instructions that let the user authenticate from a browser, including on a different device.
using Azure.Identity;
services.AddDataverse(options =>
{
options.DataverseUrl = "https://yourorg.crm4.dynamics.com";
options.CredentialType = DataverseCredentialType.InteractiveBrowserCredential;
options.InteractiveBrowserCredentialOptions = new InteractiveBrowserCredentialOptions
{
TenantId = "<tenant-id>",
ClientId = "<application-client-id>"
};
});When you do not supply InteractiveBrowserCredentialOptions, the library enables persistent token caching automatically (see Persistent token caching). Supplying your own options means the library uses them as-is and does not add caching on your behalf.
The three built-in types are the opinionated choices for the CLI. When calling the library directly you are not limited to them: set DataverseOptions.TokenCredential to any credential — for example DefaultAzureCredential, a service principal, or a managed identity. An explicitly supplied TokenCredential always takes precedence over CredentialType and all credential-specific options.
using Azure.Identity;
// Use DefaultAzureCredential (or any TokenCredential) when hosting the library yourself.
TokenCredential credential = new DefaultAzureCredential();
services.AddDataverse(options =>
{
options.DataverseUrl = "https://yourorg.crm4.dynamics.com";
options.TokenCredential = credential;
});using Microsoft.PowerPlatform.Dataverse.Client;
using var provider = services.BuildServiceProvider();
var serviceClient = provider.GetRequiredService<ServiceClient>();To register the related organization-service interfaces as well:
services.AddDataverseWithOrganizationServices(options =>
{
options.DataverseUrl = "https://yourorg.crm4.dynamics.com";
options.CredentialType = DataverseCredentialType.AzureCliCredential;
});This registers:
ServiceClientIOrganizationServiceAsync2IOrganizationServiceAsyncIOrganizationService
Use IServiceClientFactory when you need separate ServiceClient instances:
services.AddDataverseFactory(options =>
{
options.DataverseUrl = "https://yourorg.crm4.dynamics.com";
options.CredentialType = DataverseCredentialType.DeviceCodeCredential;
});
using var provider = services.BuildServiceProvider();
var factory = provider.GetRequiredService<IServiceClientFactory>();
var defaultClient = factory.CreateClient();
var interactiveClient = factory.CreateClient(new DataverseOptions
{
DataverseUrl = "https://anotherorg.crm4.dynamics.com",
CredentialType = DataverseCredentialType.InteractiveBrowserCredential
});To inject a custom credential into the factory, set DataverseOptions.TokenCredential in the configureOptions callback (or per client via CreateClient). A per-client DataverseOptions.TokenCredential always has the highest precedence.
For InteractiveBrowserCredential and DeviceCodeCredential, the library enables persistent token caching by default (when you do not pass your own credential-specific options). Tokens and the signed-in account are stored under ~/.dataverseconnection, so subsequent runs — including separate CLI invocations — acquire tokens silently instead of prompting again. AzureCliCredential is unaffected because the az CLI manages its own cache.
The on-disk cache is encrypted using the operating system keychain (DPAPI on Windows, Keychain on macOS, libsecret on Linux/WSL). If encrypted storage is unavailable — common on headless Linux or WSL without libsecret — the library falls back to a non-persistent credential that prompts on every run, rather than writing tokens to disk unencrypted.
By default the library reads its settings from the registered IConfiguration, so a tool does not have to write any authentication code — it just registers an IConfiguration and calls one of the AddDataverse* methods. Every tool can share the same app settings and behave consistently without reinventing the wiring.
Register a configuration source and the Dataverse services:
using DataverseConnection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: true)
.AddEnvironmentVariables()
.Build();
var services = new ServiceCollection();
services.AddSingleton<IConfiguration>(configuration);
// URL and credential type are read from configuration automatically.
services.AddDataverseWithOrganizationServices();
services.AddDataverseFactory();The library reads two flat keys (from appsettings.json, environment variables, or any other configuration source):
| Key | Required | Values |
|---|---|---|
DATAVERSE_URL |
Yes (unless set on DataverseOptions.DataverseUrl) |
The environment URL, e.g. https://yourorg.crm4.dynamics.com. |
DATAVERSE_CREDENTIAL_TYPE |
No (defaults to browser) |
browser, devicecode, or azcli (case-insensitive). |
{
"DATAVERSE_URL": "https://yourorg.crm4.dynamics.com",
"DATAVERSE_CREDENTIAL_TYPE": "browser"
}The credential-type strings map to the opinionated credential types:
| Config value | Credential type |
|---|---|
browser (default) |
InteractiveBrowserCredential |
devicecode |
DeviceCodeCredential |
azcli |
AzureCliCredential |
An unrecognized DATAVERSE_CREDENTIAL_TYPE throws at startup, listing the valid values.
Values read from configuration are just the defaults. To do something specific — a fixed credential type, a custom TokenCredential, credential-specific options, or a hard-coded URL — pass a configureOptions callback. It runs after the configuration is applied, so anything you set there wins:
services.AddDataverseWithOrganizationServices(options =>
{
// Overrides DATAVERSE_CREDENTIAL_TYPE from configuration.
options.CredentialType = DataverseCredentialType.AzureCliCredential;
});Because the callback overrides configuration, a tool that needs full control writes only the lines it cares about; everything else still comes from the shared app settings.
cd DataverseWhoAmI
dotnet runThe tool executes WhoAmIRequest and prints the user, business unit, and organization IDs.
MIT