Integratie

Dataverse-integraties end-to-end monitoren: Application Insights, correlatie-ID's en managed identities

25 oktober 2026
Sabhat Qureshi
16 min. leestijd

De vraag waar om negen uur 's ochtends niemand een antwoord op heeft

Een verkoper verhoogt in Dynamics 365 de kredietlimiet van een klant. Een halfuur later toont het ERP nog altijd de oude waarde. Het Dynamics-team zegt dat het record correct is opgeslagen, het Azure-team zegt dat de function groen staat en het ERP-team zegt dat er niets is binnengekomen. Elk team heeft gelijk over zijn eigen stuk, en niemand kan aantonen wat er met die ene wijziging is gebeurd.

In dit artikel beschrijf ik hoe ik end-to-end monitoring opzet voor Dataverse-integraties op Azure Integration Services: telemetrie uit Dataverse halen, Service Bus, Azure Functions en Logic Apps instrumenteren, één zakelijke transactie over alle stappen heen volgen met een correlatie-ID, en alerts instellen op wat er echt toe doet. Het tweede deel gaat over managed identities, want dezelfde integraties zitten meestal vol connection strings en client secrets die niemand wil roteren. Ik houd me aan wat Microsoft vandaag documenteert en vermeld uitdrukkelijk wat in preview is of beperkingen heeft. Voor de Dataverse-kant van de architectuur begint u best bij de documentatie over Azure-integratie met Microsoft Dataverse.

Waarom: één wijziging, vijf stappen, drie teams

Een typische event-driven integratie met Dynamics 365 heeft meer bewegende delen dan het schema op het whiteboard doet vermoeden:

  1. Dataverse: een gebruiker of proces wijzigt een rij, en plugins draaien in de event pipeline.
  2. Plugin of service endpoint: een geregistreerde stap post de execution context naar Azure Service Bus, synchroon of via een asynchrone systeemtaak.
  3. Service Bus-topic: het bericht wacht in een subscription tot een consumer het ophaalt, het opnieuw wordt geprobeerd of het in de dead-letter queue belandt.
  4. Azure Function of Logic App: de consumer mapt, verrijkt en valideert de gegevens en roept daarbij vaak Dataverse opnieuw aan voor extra context.
  5. Doelsysteem: een ERP, een dataplatform of een API van een partner aanvaardt of weigert de update.

Elke stap heeft zijn eigen logs, zijn eigen bewaartermijn en vaak zijn eigen eigenaar. Zonder gedeelde telemetrie lijkt een fout in stap 4 voor de business een Dynamics-probleem, want daar hebben ze de wijziging gedaan. Ik heb teams dagen zien discussiëren over wiens schuld een ontbrekende update was, terwijl één query over de juiste tabellen het in enkele minuten had uitgeklaard.

Het doel

Voor elke zakelijke wijziging moet wie support doet binnen enkele minuten drie vragen kunnen beantwoorden: heeft ze Dataverse verlaten, waar zit ze nu, en als het misliep, waarom? Alles in dit artikel staat in dienst van die drie vragen.

Het telemetrieontwerp in vier beslissingen

Voor ik iets configureer, maak ik vier ontwerpafspraken met de mensen die de integratie zullen ondersteunen:

  • Eén Log Analytics-workspace per omgevingsniveau. Workspace-based Application Insights bewaart zijn gegevens in een Log Analytics-workspace, zodat u telemetrie uit Application Insights, resource logs van Service Bus en logs van Logic Apps op één plek kunt bevragen.
  • Eén Application Insights-resource per Dataverse-omgeving voor de platformexport. Microsoft geeft aan dat de standaardrapporten niet correct werken wanneer één Application Insights-resource gegevens van meerdere omgevingen bevat, en dat de integratie lokale authenticatie op die resource vereist.
  • Een aparte Application Insights-resource voor de Azure-componenten. Zo kunt u de Azure-kant overschakelen naar ingestie met uitsluitend Microsoft Entra-authenticatie zonder de Dataverse-export te breken, terwijl beide resources nog altijd naar dezelfde workspace schrijven.
  • Eén zakelijke correlatie-ID, overal met dezelfde naam en hetzelfde formaat. Operation-ID's van het platform veranderen bij elke stap die niet via W3C trace context verbonden is. Een correlatie-ID die u zelf meegeeft, overleeft ze allemaal.

De vierde beslissing is de belangrijkste. Voor zover ik in de documentatie kan nagaan, zet Dataverse geen W3C trace context op de Service Bus-berichten die een service endpoint verstuurt. De operation-ID's van het platform koppelen Dataverse en Azure dus niet vanzelf aan elkaar. De correlatie-ID doet dat wel.

Dataverse-kant: platformtelemetrie exporteren

Het Power Platform-beheercentrum kan telemetrie naar Application Insights exporteren via Beheren > Gegevensexport > App Insights. U kiest het gegevenstype (Dataverse-diagnose en -prestaties, Power Automate of Copilot Studio), filters, de bronomgeving en de Application Insights-doelresource. Microsoft beschrijft de configuratie in Gegevens exporteren naar Application Insights.

Voorwaarden om eerst na te kijken

  • Managed Environments: de functie is enkel ingeschakeld en ondersteund voor beheerde omgevingen. Een omgeving beheerd maken heeft gevolgen voor de licenties: al het actieve gebruik vereist dan geldige standalone licenties (verschillende Dynamics 365 Enterprise-licenties komen in aanmerking), en Microsoft heeft aangekondigd dat gebruikers zonder passende licentie vanaf februari 2027 geen apps meer kunnen openen in beheerde omgevingen. Kijk dit na voordat u telemetrie belooft. Zie Licentievereisten voor beheerde omgevingen.
  • Beheerdersrollen aan beide kanten: wie de export aanmaakt, heeft de rol Power Platform-beheerder of Dynamics 365-beheerder op tenantniveau nodig, de rol Systeembeheerder of omgevingsbeheerder in de omgeving, en Inzender of Eigenaar op de Application Insights-resource. Microsoft noemt onvoldoende rechten als een veelvoorkomende oorzaak van ontbrekende telemetrie.
  • Lokale authenticatie blijft aan: de integratie vereist lokale authenticatie op de Application Insights-resource. Schakel die dus niet uit op de resource die de Dataverse-export ontvangt.
  • Reken op vertraging: de gegevens beginnen binnen 24 uur na de configuratie binnen te komen, en het gedocumenteerde serviceniveau voor de levering van deze telemetriestromen is 24 uur. Beschouw het als een diagnosebron, niet als een bron voor realtime alerts.
  • Preview: de export van telemetrie voor Power Automate en Dynamics 365 Customer Service is in public preview en niet beschikbaar in GCC, GCC High of 21Vianet.

Voor Dataverse bevat de export de inkomende Web API- en Organization service-aanroepen in de tabel requests, pluginuitvoeringen (type Plugin) en SDK-aanroepen in de tabel dependencies, en fouten in plugins en SDK-bewerkingen in de tabel exceptions, met custom dimensions zoals de naam van de stap, de depth en de entiteitsnaam. Elke uitgaande aanroep van een plugin wordt eveneens als dependency gelogd. De details staan in Telemetriegebeurtenissen voor Microsoft Dataverse.

Wat de export u niet geeft

Ze vertelt u dat een plugin is uitgevoerd, hoe lang dat duurde en of er een exception was. Ze vertelt u niet wat uw code besliste of welk record ze aan Service Bus heeft doorgegeven. Daarvoor moet de plugin zijn eigen telemetrie schrijven.

Logging in plugins: ILogger en ITracingService naast elkaar

Dataverse geeft plugins twee loggingkanalen, en die komen op verschillende plaatsen terecht. Microsoft raadt aan om beide te gebruiken, zoals uitgelegd in Telemetrie naar uw Application Insights-resource schrijven met ILogger.

CriteriumILogger (Microsoft.Xrm.Sdk.PluginTelemetry)ITracingService
Waar het terechtkomtUw eigen Application Insights-resource (traces en exceptions), rechtstreeks geschreven en nooit naar Microsoft verstuurdDe tabel Plugin Trace Log, het foutdialoogvenster bij synchrone stappen of de systeemtaak bij asynchrone stappen
VereistDe Application Insights-export vanuit het beheercentrum, en dus een beheerde omgeving; anders wordt er niets geschrevenPlugin trace logging op Uitzondering of Alle in de omgeving
BewaartermijnDe bewaartermijn van uw Application Insights (standaard 90 dagen)24 uur; een dagelijkse bulkverwijdering ruimt oudere records op
VolumeEen string per logaanroep10 KB tekst per uitvoering; oudere regels vallen daarboven weg
Wie het kan lezenIedereen met leesrechten op de Application Insights-resource of de workspaceHet berichtblok en de (beveiligde) configuratiekolommen zijn enkel leesbaar voor systeembeheerders

In de praktijk houd ik ITracingService voor debugging en voor de foutdetails die gebruikers en support te zien krijgen, en voeg ik ILogger toe voor telemetrie die ik weken later nog wil bevragen. Gebruik de namespace Microsoft.Xrm.Sdk.PluginTelemetry: als u in plaats daarvan Microsoft.Extensions.Logging importeert, is de ILogger-instantie null. ILogger wordt bovendien niet ondersteund in een profiling- of debugsessie van de Plugin Registration Tool.

Een plugin die de correlatie-ID meegeeft en naar Service Bus post

Ik verkies een kleine, eigen Azure-aware plugin boven de standaardstap van het service endpoint. Zo kan ik telemetrie schrijven op het moment van de overdracht en de correlatie-ID toevoegen aan de shared variables van de execution context, die volgens de documentatie van Microsoft vóór het posten aanpasbaar zijn. Het service endpoint zelf is geregistreerd met JSON als berichtformaat.

using System;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.PluginTelemetry; // not Microsoft.Extensions.Logging

namespace Crx.Integration.Plugins
{
    // Registered asynchronously on Update of account (PostOperation).
    // Unsecure configuration = ID of the Service Bus service endpoint (message format: JSON).
    public class AccountPostUpdateErpSyncPlugin : IPlugin
    {
        private readonly Guid _serviceEndpointId;

        public AccountPostUpdateErpSyncPlugin(string unsecureConfig)
        {
            if (!Guid.TryParse(unsecureConfig, out _serviceEndpointId))
            {
                throw new InvalidPluginExecutionException("Service endpoint ID missing from the step configuration.");
            }
        }

        public void Execute(IServiceProvider serviceProvider)
        {
            var context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            var tracingService = (ITracingService)serviceProvider.GetService(typeof(ITracingService));
            var logger = (ILogger)serviceProvider.GetService(typeof(ILogger));
            var notificationService = (IServiceEndpointNotificationService)serviceProvider.GetService(typeof(IServiceEndpointNotificationService));

            // One ID for the whole journey. Dataverse's own CorrelationId is a sensible default.
            var correlationId = context.CorrelationId.ToString();
            context.SharedVariables["crx_correlationid"] = correlationId;

            try
            {
                logger?.LogInformation("ERP sync: posting {0} {1}. CorrelationId {2}",
                    context.PrimaryEntityName, context.PrimaryEntityId, correlationId);
                tracingService.Trace($"ERP sync: posting {context.PrimaryEntityName} {context.PrimaryEntityId}. CorrelationId {correlationId}");

                // Keep this plug-in to "stamp, log, post": a failed async post re-runs the whole plug-in
                notificationService.Execute(new EntityReference("serviceendpoint", _serviceEndpointId), context);
            }
            catch (Exception ex)
            {
                logger?.LogError(ex, "ERP sync: post to Service Bus failed. CorrelationId {0}", correlationId);
                tracingService.Trace($"Error: {ex.Message}");
                throw new InvalidPluginExecutionException($"An error occurred: {ex.Message}");
            }
        }
    }
}

Twee gedocumenteerde details bepalen dit ontwerp. De execution context in de body van het bericht bevat de CorrelationId en, bij asynchrone stappen, de OperationId van de systeemtaak, wat helpt bij het detecteren van duplicaten. En wanneer een asynchrone post mislukt, probeert de systeemtaak het opnieuw door de volledige plugin opnieuw uit te voeren. De plugin doet dus best niets meer dan de context aanpassen, loggen en posten. Houd er ook rekening mee dat een synchrone post niet kan worden teruggedraaid als de transactie daarna wordt teruggerold. Zie Een eigen Azure-aware plugin schrijven.

Azure-kant: Functions, Logic Apps en Service Bus

Aan de Azure-kant is het doel dat elke component naar dezelfde workspace schrijft, een herkenbare rolnaam gebruikt en de correlatie-ID doorzoekbaar logt.

Azure Functions: OpenTelemetry en gestructureerde logs

Voor .NET-functions in het isolated worker-model documenteert Microsoft nu OpenTelemetry als de manier om telemetrie van host en worker naar Application Insights te sturen: zet telemetryMode op OpenTelemetry in host.json en registreer in Program.cs de OpenTelemetry-integratie van de Functions-worker met de Azure Monitor-exporter. De configuratie staat beschreven in OpenTelemetry gebruiken met Azure Functions.

// host.json
{
  "version": "2.0",
  "telemetryMode": "OpenTelemetry"
}

// Program.cs (.NET isolated worker)
// Packages: Microsoft.Azure.Functions.Worker.OpenTelemetry, OpenTelemetry.Extensions.Hosting,
//           Azure.Monitor.OpenTelemetry.Exporter, Azure.Identity, Azure.Messaging.ServiceBus
using Azure.Identity;
using Azure.Messaging.ServiceBus;
using Azure.Monitor.OpenTelemetry.Exporter;
using Microsoft.Azure.Functions.Worker.Builder;
using Microsoft.Azure.Functions.Worker.OpenTelemetry;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

var builder = FunctionsApplication.CreateBuilder(args);
builder.ConfigureFunctionsWebApplication();

// The user-assigned identity of this integration (use DefaultAzureCredential for local development)
var credential = new ManagedIdentityCredential(builder.Configuration["UamiClientId"]);

builder.Services.AddOpenTelemetry()
    .UseFunctionsWorkerDefaults()
    .UseAzureMonitorExporter(options => options.Credential = credential); // Entra ID ingestion (worker)
builder.Logging.AddOpenTelemetry(options => options.IncludeScopes = true); // scopes are off by default

builder.Services.AddSingleton(new ServiceBusClient(
    builder.Configuration["ServiceBusConnection__fullyQualifiedNamespace"], credential));
builder.Services.AddSingleton(sp =>
    sp.GetRequiredService<ServiceBusClient>().CreateSender("erp-outbound"));

builder.Build().Run();

Drie details uit die documentatie zijn belangrijk voor integraties. Logging scopes worden standaard niet meegenomen, dus schakel IncludeScopes in of neem de correlatie-ID op in het berichtsjabloon. De operation-ID van een aanroep via een Service Bus-trigger komt uit de traceparent van het inkomende bericht. En als u Microsoft Entra-authenticatie voor de ingestie gebruikt, configureert u die apart voor de host (de instelling APPLICATIONINSIGHTS_AUTHENTICATION_STRING) en voor de worker (de credential van de exporter). Verkiest u de klassieke Application Insights SDK, dan gelden dezelfde principes.

Logic Apps: Standard versus Consumption

Standard logic apps draaien op de Azure Functions-runtime en kunnen telemetrie naar Application Insights sturen. Met enhanced telemetry ingeschakeld in host.json komen trigger- en action-events in de tabel requests terecht, met de workflownaam, de run-ID en de client tracking ID in de custom dimensions; de operation-ID is gelijk aan de run-ID van de workflow. Zie Enhanced telemetry voor Standard-workflows.

// host.json of a Standard logic app: enhanced Application Insights telemetry
{
  "version": "2.0",
  "extensionBundle": {
    "id": "Microsoft.Azure.Functions.ExtensionBundle.Workflows",
    "version": "[1, 2.00]"
  },
  "extensions": {
    "workflow": {
      "Settings": {
        "Runtime.ApplicationInsightTelemetryVersion": "v2"
      }
    }
  }
}

Consumption logic apps hebben die Application Insights-integratie niet: u voegt een diagnostische instelling toe met de categorie voor workflow runtime die naar een Log Analytics-workspace stuurt, eventueel met de Logic Apps Management-oplossing. Bij Standard schrijft zo'n diagnostische instelling naar de tabel LogicAppWorkflowRuntime. Stel bij beide plannen de Custom Tracking Id van de trigger in op de correlatie-ID uit het bericht, en gebruik tracked properties voor de zakelijke sleutels waarop u zoekt. Microsoft wijst erop dat resource logs tientallen minuten vertraging kunnen hebben en niet volledig verliesvrij zijn. Gebruik ze dus voor diagnose, niet als audittrail. Zie Diagnostische gegevens verzamelen voor Logic Apps-workflows.

Service Bus: metrics, resource logs en trace context

Service Bus levert zonder configuratie platformmetrics per entiteit: actieve berichten, dead-lettered berichten, inkomende en uitgaande berichten, serverfouten, gebruikersfouten en gethrottelde aanvragen. De aantallen dead-lettered en actieve berichten kunnen niet via een diagnostische instelling naar Log Analytics worden geëxporteerd, dus stelt u daarop metric alerts in. Resource logs (operationele logs, diagnostische foutlogs, logs voor virtuele netwerken, applicatiemetriclogs en, enkel in de Premium-laag, runtime audit logs en logs voor georeplicatie) gaan via een diagnostische instelling naar Log Analytics, in AzureDiagnostics of in resourcespecifieke tabellen. De referentie is Referentie voor monitoringgegevens van Azure Service Bus.

Operationele logs bevatten enkel beheerbewerkingen, geen verzonden of ontvangen berichten. Tracing op berichtniveau moet dus van de clients komen. De Azure.Messaging.ServiceBus-SDK geeft W3C trace context mee in de application property Diagnostic-Id; zo komen een function die een bericht verstuurt en de function die het ontvangt in dezelfde Application Insights-operation terecht. Zie End-to-end tracing en diagnose in Service Bus.

De application map

Zodra de Azure-componenten telemetrie met duidelijke rolnamen versturen, toont de application map de stappen die uw code ziet: de function, haar Service Bus- en HTTP-dependencies en haar aanroepen naar Dataverse. Verwacht niet dat de Dataverse-plugin in hetzelfde beeld verschijnt: de platformexport zit in een eigen resource en is niet via trace context aan uw functions gekoppeld. Precies dat gat dicht de correlatie-ID. Zie Application Map in Application Insights (overzicht).

Eén zakelijke transactie volgen over alle stappen heen

Het patroon dat ik gebruik is eenvoudig en hangt niet af van de tracingmogelijkheden van één bepaald product:

  1. De plugin neemt de CorrelationId van Dataverse (of een ID die van een API stroomopwaarts kwam), schrijft die naar de shared variables van de execution context en logt ze met ILogger.
  2. De function leest de ID uit de body van het bericht, tagt er de aanroep mee en neemt ze op in elke logregel.
  3. Elk bericht dat de function verder verstuurt, draagt de ID als Service Bus-CorrelationId en als application property.
  4. De Logic App gebruikt ze als Custom Tracking Id en als tracked property.
  5. Aanroepen naar het doelsysteem geven ze mee in een header, zodat het ontvangende team in zijn eigen logs op dezelfde waarde kan zoeken.

De function: lezen, taggen, doorgeven

Een isolated worker-function met een Service Bus-trigger, die de client en sender gebruikt die hierboven in Program.cs zijn geregistreerd. De client-ID van de managed identity komt uit de app settings; niets in de configuratie is een secret.

using System.Diagnostics;
using System.Text.Json;
using Azure.Messaging.ServiceBus;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;

public class ErpSyncFunction
{
    private readonly ILogger<ErpSyncFunction> _logger;
    private readonly ServiceBusSender _erpSender;

    public ErpSyncFunction(ILogger<ErpSyncFunction> logger, ServiceBusSender erpSender)
    {
        _logger = logger;
        _erpSender = erpSender;
    }

    [Function(nameof(ErpSyncFunction))]
    public async Task Run(
        [ServiceBusTrigger("crm-account-events", "erp-sync", Connection = "ServiceBusConnection")]
        ServiceBusReceivedMessage message)
    {
        // Body = the RemoteExecutionContext as JSON. Check the shape against a captured message first.
        using var body = JsonDocument.Parse(message.Body.ToString());
        var context = body.RootElement;
        var correlationId = ReadSharedVariable(context, "crx_correlationid")
            ?? context.GetProperty("CorrelationId").GetString()!;

        // Searchable on the request row and on every log line of this invocation
        Activity.Current?.SetTag("crx.correlation_id", correlationId);
        using var scope = _logger.BeginScope(new Dictionary<string, object> { ["CorrelationId"] = correlationId });

        _logger.LogInformation("ERP sync started for {EntityName} {EntityId}",
            context.GetProperty("PrimaryEntityName").GetString(),
            context.GetProperty("PrimaryEntityId").GetString());

        // ... map and validate. Throw on failure: Service Bus retries and eventually dead-letters.

        var outbound = new ServiceBusMessage(BinaryData.FromObjectAsJson(new { correlationId /*, payload */ }))
        {
            CorrelationId = correlationId,
            ContentType = "application/json"
        };
        outbound.ApplicationProperties["crxCorrelationId"] = correlationId;
        await _erpSender.SendMessageAsync(outbound);
    }

    private static string? ReadSharedVariable(JsonElement context, string key)
    {
        if (!context.TryGetProperty("SharedVariables", out var variables)) return null;
        foreach (var pair in variables.EnumerateArray())
        {
            if (pair.GetProperty("key").GetString() == key) return pair.GetProperty("value").ToString();
        }
        return null;
    }
}

Eén query om een correlatie-ID te volgen

Voer deze query uit in de Log Analytics-workspace waarnaar beide Application Insights-resources en de diagnostische instelling van Logic Apps schrijven. Ze zoekt elke operation waarin de ID voorkomt, verzamelt alle telemetrie van die operations, voegt de events van de Logic App toe en sorteert alles chronologisch.

// Log Analytics workspace shared by both Application Insights resources and the Logic Apps diagnostic setting
let cid = "6f1c2d3e-5a4b-4c7d-9e8f-0a1b2c3d4e5f";
let lookback = ago(3d);
// 1. Every operation (plug-in, function invocation) that mentions the ID
let ops = union AppTraces, AppRequests, AppExceptions
    | where TimeGenerated > lookback
    | where Message has cid or tostring(Properties) has cid
    | distinct OperationId;
// 2. All Dataverse and Function telemetry belonging to those operations
let appTelemetry = union AppRequests, AppDependencies, AppTraces, AppExceptions
    | where TimeGenerated > lookback and OperationId in (ops)
    | project TimeGenerated, Hop = AppRoleName, Kind = Type,
              Detail = coalesce(Name, Message, OuterMessage), Success, DurationMs, OperationId;
// 3. Logic App events that carry the ID as client tracking ID
let workflowEvents = LogicAppWorkflowRuntime
    | where TimeGenerated > lookback and ClientTrackingId == cid
    | project TimeGenerated, Hop = WorkflowName, Kind = OperationName,
              Detail = coalesce(ActionName, TriggerName, Error), Success = Status != "Failed",
              DurationMs = real(null), OperationId = RunId;
union appTelemetry, workflowEvents
| order by TimeGenerated asc

Bewaar de query als gedeelde query of als workbook met de correlatie-ID als parameter, en geef supportmedewerkers leesrechten op de workspace. De eerste keer dat iemand de vraag 'waar blijft mijn update?' in twee minuten beantwoordt zonder te escaleren, heeft de inspanning zich terugbetaald.

Alerts die de moeite waard zijn

Ik houd de eerste reeks alerts klein en stuur elke alert naar het team dat er iets mee kan doen:

SignaalBronRegel
Dead-lettered berichtenService Bus-metric DeadletteredMessages, opgesplitst per entiteitMaximum boven 0 over 5 minuten. Elk dead-lettered bericht is een zakelijke wijziging die niet is aangekomen.
Groeiende achterstandService Bus-metric ActiveMessages, opgesplitst per entiteitBoven een drempel op basis van uw normale volume gedurende 15 minuten of langer: de consumer ligt plat, wordt gethrottled of is te traag.
Mislukte function-uitvoeringenLog alert op AppRequests uit de Application Insights van de integratieElke request met Success gelijk aan false, gegroepeerd per function, per 5 minuten.
Mislukte workflow-runsConsumption: de metric Runs Failed. Standard: een log alert op LogicAppWorkflowRuntimeElke run die eindigt met de status Failed.
Fouten in pluginsAppDependencies (type Plugin) en AppExceptions uit de Dataverse-exportGebruik dit voor trends en een dagelijkse controle, niet om iemand op te piepen, gezien het gedocumenteerde leveringsniveau van 24 uur.
Latentie en stilteDuur van function-requests en het aantal geslaagde runs95e percentiel boven de verwerkingstijd die u met de business hebt afgesproken, en nul geslaagde runs tijdens de kantooruren wanneer u verkeer verwacht.
// Failed function executions (integration Application Insights / workspace)
AppRequests
| where Success == false
| summarize Failures = count() by AppRoleName, OperationName, bin(TimeGenerated, 5m)

// Failed Standard workflow runs (Logic Apps diagnostic setting)
LogicAppWorkflowRuntime
| where OperationName has "WorkflowRunCompleted" and Status == "Failed"
| summarize FailedRuns = count() by WorkflowName, bin(TimeGenerated, 5m)

// Failing plug-ins (Dataverse export, for trends rather than paging)
AppDependencies
| where DependencyType == "Plugin" and Success == false
| summarize Failures = count() by Name, bin(TimeGenerated, 1h)

Stuur elke alert naar een action group met een eigenaar bij naam, en zet een link naar de correlatiequery in de beschrijving van de alert. Een alert waarvoor niemand verantwoordelijk is, wordt binnen enkele weken ruis.

Zonder secrets dankzij managed identities

Monitoring vertelt u wanneer een integratie stukgaat. Managed identities nemen een van de vaakste oorzaken weg: een verlopen of uitgelekt secret. Een managed identity is een Microsoft Entra-identiteit waarvan Azure de referenties voor u beheert. Zie Wat zijn managed identities voor Azure-resources?.

System-assigned of user-assigned?

  • System-assigned: wordt samen met de resource aangemaakt en verwijderd, één per resource. Eenvoudig, maar als de resource opnieuw wordt aangemaakt, krijgt u een nieuwe principal, en u kunt geen rollen toekennen of de Dataverse-toepassingsgebruiker aanmaken voordat de resource bestaat.
  • User-assigned: een aparte resource die u één keer aanmaakt en aan een of meer apps koppelt. U kunt rollen toekennen en de identiteit in Dataverse registreren nog voor er code is uitgerold, en ze overleeft herimplementaties.

Voor integraties gebruik ik doorgaans één user-assigned identity per integratie per omgeving, zodat de rechten van de ERP-synchronisatie in productie op één plek zichtbaar zijn en nooit met test worden gedeeld. De uitzondering is een Standard logic app met ingebouwde service provider-connectors zoals Service Bus: Microsoft vermeldt dat de meeste daarvan momenteel enkel de system-assigned identity ondersteunen.

Functions naar Service Bus, Storage en Application Insights zonder connection strings

Een identity-based connection in Functions is een groep app settings met hetzelfde voorvoegsel. Voor Service Bus stelt u <voorvoegsel>__fullyQualifiedNamespace in en, voor een user-assigned identity, <voorvoegsel>__credential met de waarde managedidentity en <voorvoegsel>__clientId. Voor het opslagaccount van de host en voor Application Insights bestaan gelijkaardige instellingen. Microsoft documenteert ze in Verbindingen configureren in Azure Functions.

# Function app settings: identifiers only, no secrets
ServiceBusConnection__fullyQualifiedNamespace = sb-crx-integration.servicebus.windows.net
ServiceBusConnection__credential              = managedidentity
ServiceBusConnection__clientId                = <client ID of id-crx-erp-sync>
AzureWebJobsStorage__accountName              = stcrxerpsync
AzureWebJobsStorage__credential               = managedidentity
AzureWebJobsStorage__clientId                 = <client ID of id-crx-erp-sync>
APPLICATIONINSIGHTS_CONNECTION_STRING         = InstrumentationKey=...;IngestionEndpoint=...
APPLICATIONINSIGHTS_AUTHENTICATION_STRING     = ClientId=<client ID of id-crx-erp-sync>;Authorization=AAD
UamiClientId                                  = <client ID of id-crx-erp-sync>
DataverseUrl                                  = https://yourorg.crm4.dynamics.com
ErpApiKey                                     = @Microsoft.KeyVault(SecretUri=https://kv-crx-integration.vault.azure.net/secrets/erp-api-key/)

# Role assignments at the narrowest scope (Azure CLI)
MI=$(az identity show -g rg-crx-integration -n id-crx-erp-sync --query principalId -o tsv)
SB=$(az servicebus namespace show -g rg-crx-integration -n sb-crx-integration --query id -o tsv)

az role assignment create --assignee-object-id $MI --assignee-principal-type ServicePrincipal \
  --role "Azure Service Bus Data Receiver" --scope "$SB/topics/crm-account-events/subscriptions/erp-sync"
az role assignment create --assignee-object-id $MI --assignee-principal-type ServicePrincipal \
  --role "Azure Service Bus Data Sender" --scope "$SB/queues/erp-outbound"
az role assignment create --assignee-object-id $MI --assignee-principal-type ServicePrincipal \
  --role "Key Vault Secrets User" \
  --scope $(az keyvault show -g rg-crx-integration -n kv-crx-integration --query id -o tsv)
az role assignment create --assignee-object-id $MI --assignee-principal-type ServicePrincipal \
  --role "Monitoring Metrics Publisher" \
  --scope $(az monitor app-insights component show -g rg-crx-integration -a appi-crx-integration --query id -o tsv)

# Key Vault references must use the user-assigned identity, not the system-assigned one
az functionapp update -g rg-crx-integration -n func-crx-erp-sync \
  --set keyVaultReferenceIdentity=$(az identity show -g rg-crx-integration -n id-crx-erp-sync --query id -o tsv)

Ken rollen toe op het kleinst mogelijke bereik: Azure Service Bus Data Receiver op de topic subscription waarnaar de function luistert, en Azure Service Bus Data Sender op de queue of topic waarnaar ze schrijft. Eén addertje uit de documentatie van de Service Bus-trigger: voor nauwkeurige schaling leest de extensie runtime-eigenschappen van de entiteit, en daarvoor is de rol Azure Service Bus Data Owner nodig of een aangepaste rol met Microsoft.ServiceBus/namespaces/*/read. Zonder die rol krijgt u geen fout bij het opstarten; de schaling valt stilletjes terug op een minder nauwkeurige schatting. Roltoewijzingen kunnen tot vijf minuten nodig hebben om door te werken, en de Azure-portal kan geen rollen toekennen op het niveau van een topic subscription, dus gebruikt u de CLI of Bicep. Zie Managed identities gebruiken voor toegang tot Azure Service Bus.

Logic Apps naar Service Bus en Key Vault

Logic Apps ondersteunt managed identities in beide hostingplannen: de beheerde connectors voor Azure Service Bus en Azure Key Vault en de ingebouwde HTTP-actie in Consumption en Standard, en in Standard daarnaast de ingebouwde Service Bus-connector. Een logic app kan meerdere identiteiten hebben, maar elke verbinding gebruikt er telkens één. Zie Authenticeren met managed identities in Azure Logic Apps.

Functions en Logic Apps die Dataverse aanroepen als managed identity

Dataverse aanvaardt een managed identity als toepassingsgebruiker. Open in het Power Platform-beheercentrum de omgeving, ga naar Instellingen > Gebruikers + machtigingen > Toepassingsgebruikers, kies Nieuwe app-gebruiker en vul de toepassings-ID van de managed identity in (niet de naam). Ken daarna een business unit en een beveiligingsrol met minimale rechten toe. Zie Toepassingsgebruikers beheren in het Power Platform-beheercentrum.

Geef in een function de Dataverse-ServiceClient een token provider die de managed identity om een token voor de URL van de omgeving vraagt. Dit bouwt verder op de Program.cs van hierboven:

// Program.cs: Dataverse as the managed identity (registered as an application user)
// Packages: Microsoft.PowerPlatform.Dataverse.Client, Azure.Identity
using Azure.Core;
using Microsoft.PowerPlatform.Dataverse.Client;

var dataverseUrl = new Uri(builder.Configuration["DataverseUrl"]!);
var dataverseScope = $"{dataverseUrl.GetLeftPart(UriPartial.Authority)}/.default";

builder.Services.AddSingleton(sp => new ServiceClient(
    dataverseUrl,
    async _ => (await credential.GetTokenAsync(
        new TokenRequestContext(new[] { dataverseScope }), CancellationToken.None)).Token,
    useUniqueInstance: true,
    logger: sp.GetRequiredService<ILogger<ServiceClient>>()));

Voor Logic Apps is er een addertje: de Microsoft Dataverse-connector vermeldt momenteel OAuth, een service principal met client secret en een clientcertificaat als authenticatietypes, geen managed identity. Wanneer ik een logic app zonder secret Dataverse wil laten aanroepen, gebruik ik de HTTP-actie op de Dataverse Web API met managed identity-authenticatie en de URL van de omgeving als audience, en registreer ik die identiteit als toepassingsgebruiker zoals hierboven beschreven. Zie de authenticatietypes in de referentie van de Microsoft Dataverse-connector.

Managed identity voor Dataverse-plugins

Plugins kunnen via Power Platform managed identity tokens voor Azure-resources verkrijgen zonder opgeslagen referenties. Microsoft raadt versie 2 van de configuratie aan, die uit deze stappen bestaat:

  1. Maak een user-assigned managed identity of een app-registratie aan in Microsoft Entra ID en noteer de client-ID en tenant-ID.
  2. Roep IManagedIdentityService.AcquireToken aan in de plugin, onderteken de assembly (of het pluginpakket) met een certificaat en registreer ze.
  3. Voeg op de identiteit een federated identity credential toe met als issuer https://login.microsoftonline.com/{tenantId}/v2.0 en een subject identifier die is opgebouwd uit de gecodeerde tenant-ID, de omgevings-ID en hashes van de issuer en het subject van het certificaat.
  4. Maak via de Web API een managedidentity-record aan in Dataverse met versie 2 en koppel de pluginassembly of het pakket eraan.
  5. Geef de identiteit toegang tot de Azure-resource, bijvoorbeeld de rol Key Vault Secrets User op een key vault.
  6. Activeer de plugin en controleer dat ze een token verkrijgt.
// Inside Execute(): read a secret from Key Vault as the plug-in's managed identity
var identityService = (IManagedIdentityService)serviceProvider.GetService(typeof(IManagedIdentityService));
var token = identityService.AcquireToken(new[] { "https://vault.azure.net/.default" });

using (var client = new HttpClient())
{
    client.Timeout = TimeSpan.FromSeconds(15);
    client.DefaultRequestHeaders.ConnectionClose = true; // KeepAlive false, per plug-in guidance
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

    var response = client
        .GetAsync("https://kv-crx-integration.vault.azure.net/secrets/erp-api-key?api-version=7.4")
        .GetAwaiter()
        .GetResult();
    response.EnsureSuccessStatusCode();
    // Read "value" from the JSON body. Never trace or log the secret itself.
}

Ken de beperkingen voordat u erop rekent. Zelfondertekende certificaten zijn enkel bedoeld voor ontwikkeling en tests. De subject identifier bevat de omgevings-ID, dus elke omgeving heeft haar eigen federated credential en haar eigen managedidentity-record nodig; die horen thuis in uw deploymentscripts, niet in iemands geheugen. De managed identity-commando's van de Power Platform CLI werken niet met user-assigned identities en niet op Linux. De configuratiehandleiding is Power Platform managed identity instellen voor Dataverse-plugins.

Wat nog een secret nodig heeft, en waar u het bewaart

Volledig zonder secrets werken lukt zelden op de eerste dag. Deze kom ik nog het vaakst tegen:

  • Het Dataverse-service endpoint naar Service Bus: de gedocumenteerde manier waarop Dataverse naar Service Bus post, is nog altijd een shared access signature. Gebruik een aparte policy met enkel Send-rechten op die ene topic, en roteer de sleutel. Het schema van de tabel ServiceEndpoint toont nu wel een authenticatietype Managed Identity en kolommen voor Key Vault, maar ik heb er geen gedocumenteerde configuratieprocedure voor gevonden, dus in productie reken ik er nog niet op.
  • Doelsystemen buiten Microsoft Entra ID: API-sleutels of OAuth-client secrets voor een ERP of een partner-API. Bewaar ze in Key Vault en verwijs ernaar vanuit de app settings met @Microsoft.KeyVault(...), wat de app met haar managed identity oplost.
  • De Dataverse-connector in Logic Apps: als u die gebruikt in plaats van de HTTP-actie, hoort het client secret of certificaat in Key Vault, met een eigenaar en een vervaldatum in iemands agenda.
  • Plugins die niet-Azure-API's aanroepen: zet geen sleutel in de beveiligde configuratie van een stap, maar laat de managed identity van de plugin hem tijdens de uitvoering uit Key Vault lezen, en schrijf hem nooit naar een trace.
  • Inkomende webhooks: Dataverse-webhooks authenticeren met een sleutel, een HTTP-header of een querystring. Een function met HTTP-trigger achter een webhook heeft dus een function key of iets gelijkaardigs nodig. Bewaar die in Key Vault en in de registratie van de webhook, nergens anders.

Gebruik voor Key Vault zelf het machtigingsmodel met Azure RBAC en geef elke identiteit de rol Key Vault Secrets User op de vault (of op afzonderlijke secrets). Wanneer een function app of Standard logic app Key Vault-verwijzingen oplost met een user-assigned identity, stelt u keyVaultReferenceIdentity in op de app; anders gebruikt ze de system-assigned identity. Zie Key Vault-verwijzingen gebruiken in App Service en Azure Functions.

Checklist voor de uitrol

  1. Teken de stappen van elke integratie uit met een eigenaar per stap, en spreek de naam en het formaat van de correlatie-ID af.
  2. Maak per omgevingsniveau een Log Analytics-workspace met workspace-based Application Insights: één resource per Dataverse-omgeving voor de platformexport, en één voor de Azure-componenten.
  3. Kijk de licenties na voordat u Managed Environments inschakelt, configureer daarna de Application Insights-export met een account dat alle vereiste rollen heeft, en reken 24 uur voor de eerste gegevens.
  4. Voeg ILogger toe naast ITracingService in de plugins op het integratiepad, en laat plugin trace logging in productie op Uitzondering staan.
  5. Registreer het service endpoint met JSON als berichtformaat en post via een plugin die de correlatie-ID meegeeft.
  6. Schakel OpenTelemetry in voor Functions, log gestructureerde waarden, tag de aanroep en geef de ID door bij elk uitgaand bericht en elke HTTP-aanroep.
  7. Schakel enhanced telemetry (Standard) of een diagnostische instelling (Consumption) in voor Logic Apps, en stel Custom Tracking Id en tracked properties in.
  8. Voeg diagnostische instellingen voor Service Bus toe en metric alerts voor dead-lettered berichten en achterstand, plus log alerts voor mislukte functions en workflow-runs.
  9. Maak één user-assigned identity per integratie per omgeving, ken rollen toe op entiteitsniveau, registreer de identiteit als Dataverse-toepassingsgebruiker met een rol met minimale rechten, en verplaats elk resterend secret naar Key Vault.
  10. Bewaar de correlatiequery als workbook, breek daarna in test bewust iets en controleer dat support het binnen enkele minuten kan traceren.

Veelvoorkomende valkuilen

  • Het bewijs wegsamplen. Integraties hebben vaak lage volumes waarbij elk bericht telt. Kijk uw sampling-instellingen na: bij parent-based sampling wordt volgens de documentatie geen request-telemetrie aangemaakt wanneer het inkomende bericht niet gesampled is.
  • Lokale authenticatie uitschakelen op de verkeerde resource. De Dataverse-export heeft lokale authenticatie nodig op haar Application Insights-resource. Dwing ingestie met uitsluitend Entra-authenticatie af op de resource aan de Azure-kant.
  • SAS te vroeg uitschakelen op de Service Bus-namespace. Als het Dataverse-service endpoint nog een shared access signature gebruikt, kan Dataverse niet meer posten zodra u lokale authenticatie op de namespace uitschakelt. Inventariseer eerst elke SAS-gebruiker.
  • Eén Application Insights-resource voor meerdere Dataverse-omgevingen. Volgens Microsoft werken de standaardrapporten dan niet correct, en telemetrie van test en productie door elkaar maakt elke query moeilijker.
  • Realtime Dataverse-telemetrie verwachten. Het gedocumenteerde leveringsniveau is 24 uur. Laat alerts afgaan op signalen aan de Azure-kant en gebruik de Dataverse-export voor diagnose en trends.
  • Enkel Receiver-rechten voor een schalende function. Zonder de leesrechten die hierboven beschreven staan, wordt de schaling stilletjes minder nauwkeurig. Denk bij Standard logic apps ook aan het feit dat de meeste ingebouwde connectors enkel met de system-assigned identity werken.
  • Persoonsgegevens loggen. Application Insights wordt door meer mensen gelezen dan de plugin trace log, waarvan de gevoelige kolommen voorbehouden zijn aan systeembeheerders. Log record-ID's en correlatie-ID's, geen namen, e-mailadressen of payloads; onder de GDPR is telemetrie ook een verwerking van persoonsgegevens.
  • Tokenlevensduur en doorlooptijd vergeten. Nieuwe roltoewijzingen kunnen tot vijf minuten nodig hebben, en nadat u een rol hebt ingetrokken, kan een draaiende app haar bestaande Service Bus-token blijven gebruiken tot het verloopt, wat tot 24 uur kan duren. Herstart de app wanneer u toegang intrekt.

Conclusie

End-to-end monitoring van een Dataverse-integratie is geen product dat u aanzet. Het zijn een handvol beslissingen die u consequent toepast: exporteer wat Dataverse aanbiedt, schrijf uw eigen telemetrie waar het platform ophoudt, geef één correlatie-ID mee door alle stappen, en stel alerts in op de paar signalen die betekenen dat een zakelijke wijziging niet is aangekomen. Managed identities nemen vervolgens een hele categorie incidenten weg. Begin met één integratie, maak ze traceerbaar van Dynamics 365 tot het doelsysteem, en gebruik ze als sjabloon voor de rest.

Hulp nodig bij je Dynamics 365-project?

Ik help organisaties hun Dynamics 365-integraties observeerbaar en secretvrij te maken, van het ontwerp van Application Insights en alerts tot de uitrol van managed identities over Dataverse, Service Bus, Functions en Logic Apps.