Back to Blog
3 min read

Dataverse Integration: Connecting Power Platform to Azure

Microsoft Dataverse is the data backbone of the Power Platform. Integrating it with Azure services unlocks powerful analytics and AI capabilities.

Understanding Dataverse

Dataverse provides:

  • Relational data storage with rich metadata
  • Business logic and security
  • API access (OData, REST)
  • Integration with Power Apps, Power Automate, and Dynamics 365

Connecting to Dataverse from Azure

Azure Data Factory Connection

{
  "name": "DataverseLinkedService",
  "type": "Microsoft.DataFactory/factories/linkedservices",
  "properties": {
    "type": "CommonDataServiceForApps",
    "typeProperties": {
      "deploymentType": "Online",
      "organizationUrl": "https://yourorg.crm.dynamics.com",
      "authenticationType": "AADServicePrincipal",
      "servicePrincipalId": "<client-id>",
      "servicePrincipalCredentialType": "ServicePrincipalKey",
      "servicePrincipalCredential": {
        "type": "SecureString",
        "value": "<client-secret>"
      }
    }
  }
}

Copy Data to Data Lake

{
  "name": "CopyDataverseToLake",
  "type": "Copy",
  "inputs": [
    {
      "referenceName": "DataverseAccount",
      "type": "DatasetReference"
    }
  ],
  "outputs": [
    {
      "referenceName": "DataLakeParquet",
      "type": "DatasetReference"
    }
  ],
  "typeProperties": {
    "source": {
      "type": "CommonDataServiceForAppsSource",
      "query": "<fetch><entity name='account'><attribute name='accountid'/><attribute name='name'/><attribute name='revenue'/><filter><condition attribute='modifiedon' operator='ge' value='@{pipeline().parameters.LastModified}'/></filter></entity></fetch>"
    },
    "sink": {
      "type": "ParquetSink",
      "storeSettings": {
        "type": "AzureBlobFSWriteSettings"
      }
    }
  }
}

Enable continuous data sync to Azure:

# After Synapse Link is configured, query Dataverse data in Synapse
from pyspark.sql import SparkSession

spark = SparkSession.builder.getOrCreate()

# Read from Synapse Link exported data
accounts_df = spark.read \
    .format("delta") \
    .load("abfss://dataverse-org@storage.dfs.core.windows.net/account/")

# Perform analytics
accounts_df.filter("revenue > 1000000") \
    .groupBy("industrycode") \
    .agg({"revenue": "sum", "accountid": "count"}) \
    .show()
-- Query via Serverless SQL
SELECT
    name,
    revenue,
    industrycode_display as Industry
FROM OPENROWSET(
    BULK 'https://storage.dfs.core.windows.net/dataverse-org/account/',
    FORMAT = 'DELTA'
) AS accounts
WHERE revenue > 1000000
ORDER BY revenue DESC;

Power Automate Integration

Trigger on Dataverse Changes

{
  "definition": {
    "triggers": {
      "When_a_row_is_created_modified_or_deleted": {
        "type": "OpenApiConnectionWebhook",
        "inputs": {
          "host": {
            "connectionName": "shared_commondataserviceforapps",
            "operationId": "SubscribeWebhookTrigger"
          },
          "parameters": {
            "subscriptionRequest/message": 3,
            "subscriptionRequest/entityname": "account",
            "subscriptionRequest/scope": 4,
            "subscriptionRequest/filteringattributes": "name,revenue"
          }
        }
      }
    },
    "actions": {
      "Send_to_Event_Hub": {
        "type": "ApiConnection",
        "inputs": {
          "host": {
            "connection": {
              "name": "@parameters('$connections')['eventhubs']['connectionId']"
            }
          },
          "method": "post",
          "body": {
            "ContentData": "@{base64(triggerBody())}"
          },
          "path": "/entities/dataverse-changes/messages"
        }
      }
    }
  }
}

Azure Functions Dataverse Plugin

using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
using Microsoft.PowerPlatform.Dataverse.Client;
using Microsoft.Xrm.Sdk;

public class DataverseFunction
{
    private readonly ServiceClient _serviceClient;

    public DataverseFunction(ServiceClient serviceClient)
    {
        _serviceClient = serviceClient;
    }

    [FunctionName("ProcessAccountUpdate")]
    public async Task Run(
        [ServiceBusTrigger("dataverse-account-updates")] string message,
        ILogger log)
    {
        var accountData = JsonConvert.DeserializeObject<AccountUpdate>(message);

        // Update related records in Dataverse
        var entity = new Entity("contact")
        {
            Id = accountData.PrimaryContactId
        };
        entity["description"] = $"Account revenue updated to {accountData.Revenue}";

        await _serviceClient.UpdateAsync(entity);

        log.LogInformation($"Updated contact for account {accountData.AccountId}");
    }
}

// Startup configuration
public class Startup : FunctionsStartup
{
    public override void Configure(IFunctionsHostBuilder builder)
    {
        var connectionString = Environment.GetEnvironmentVariable("DataverseConnection");
        builder.Services.AddSingleton(new ServiceClient(connectionString));
    }
}

Virtual Tables

Connect external data as virtual tables in Dataverse:

<!-- Virtual Entity Provider Plugin -->
<plugin>
  <name>Azure SQL Virtual Provider</name>
  <configuration>
    <datasource type="azuresql">
      <connectionstring>
        Server=sql.database.windows.net;Database=ExternalDB;
      </connectionstring>
    </datasource>
    <entitymapping>
      <source>dbo.ExternalProducts</source>
      <target>new_virtualproduct</target>
      <columns>
        <column source="ProductID" target="new_productid" />
        <column source="ProductName" target="new_name" />
        <column source="Price" target="new_price" />
      </columns>
    </entitymapping>
  </configuration>
</plugin>

Security and Access Control

// Query with impersonation
var client = new ServiceClient(connectionString);

// Impersonate another user
client.CallerId = targetUserId;

// Query respects the impersonated user's security
var query = new QueryExpression("account")
{
    ColumnSet = new ColumnSet("name", "revenue"),
    Criteria = new FilterExpression
    {
        Conditions =
        {
            new ConditionExpression("statecode", ConditionOperator.Equal, 0)
        }
    }
};

var results = await client.RetrieveMultipleAsync(query);

Dataverse integration enables a seamless data platform spanning Power Platform and Azure services.

Michael John Peña

Michael John Peña

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.