I recently worked with a customer to show the ease of integration between Logic Apps and the Dataverse as part of Dynamics 365 (D365). The flows of integrations we looked at included:
- Inbound: D365 updates pushed in near real-time into a Logic Apps HTTP trigger.
- Outbound: A Logic App sending HTTP requests to retrieve data from D365.
The focus of this short post will be on the outbound use case, showing how to use the Microsoft Dataverse connector with Bicep automation.
A simple use case
The app shown here couldn't be much simpler: it's a Timer recurrence which uses the List Rows action to retrieve data from D365, here's an snip from an execution:
Impressed? 🤣
Getting this setup clicking-through the Azure Portal is fairly simple. The connector example uses a Service Principal to authenticate the Logic App to D365 (OAuth being an alternative), so several parameters are needed:
Additionally you'll be required to configure an Environment parameter for D365, which is a URL for the target environment, e.g. https://meaingful-url-for-your-org.crm.dynamics.com.
Configuring the Service Principal may be the most troublesome part and is outside of the scope of this Bicep automation, and would be considered a separate task per-environment. This page may help you complete the required identity creation.
So... what about the Bicep?
You can see the Bicep files in the GitHub repository here. We have to deploy 2 resources:
resource laworkflow 'Microsoft.Logic/workflows@2019-05-01' = { }
...
resource commondataserviceApiConnection 'Microsoft.Web/connections@2016-06-01' = { }
...
The first Microsoft.Logic/workflows resource deploys the app configuration, and the second Microsoft.Web/connections resource deploys the Dataverse connection used by the app. The relationship between resources after deployment will be:
The Bicep for such a simple example took some trial and error to get right and the documentation is far from clear, something I will try to get improved. In hindsight it seems straight forward, these snippets outline where I struggled.
A snip from the connections resource:
resource commondataserviceApiConnection 'Microsoft.Web/connections@2016-06-01' = {
name: 'commondataservice'
...
properties: {
displayName: 'la-to-d365-commondataservice'
api: {
id: '/subscriptions/${subscription().subscriptionId}/providers/Microsoft.Web/locations/${location}/managedApis/commondataservice'
...
The property at path properties.api.id is all important here. Now looking at the workflows resource:
resource laworkflow 'Microsoft.Logic/workflows@2019-05-01' = {
name: logicAppName
...
parameters: {
'$connections': {
value: {
commondataservice: {
connectionName: 'commondataservice'
connectionId: resourceId('Microsoft.Web/connections', 'commondataservice')
id: commondataserviceApiConnection.properties.api.id
}
}
}
...
Here we see the important parameters for the connection configuration, creating the relationship between the resources:
- connectionName: reference the name of the connection as specified in the resource.
- connectionId: uses the Bicep resourceId function to obtain the deployed Azure resource ID.
- id: references the properties.api.id value specified earlier.
So fairly simple, but understanding what value is required where isn't straight forward and that's where documentation improvement is needed.
Secret Management
An extra area I looked at was improved secret management in Bicep. Values required for the Service Principal must be handled securely, so how do you achieve this? The approach I took was to use the az.getSecret Bicep function within the .bicepparm file, allowing for a secret to be read from an Azure KeyVault at deployment time. This has the advantage of separating the main template file from the parameters it uses. The KeyVault used is pre-provisioned which stores the Service Principal secrets and not deployed as part of this Bicep code.
using './logicapps.bicep'
...
param commondataserviceEnvironment = getSecret(
readEnvironmentVariable('AZURE_KV_SUBSCRIPTION_ID'),
readEnvironmentVariable('AZURE_KV_RESOURCE_GROUP'),
readEnvironmentVariable('AZURE_KV_NAME'),
'commondataserviceClientSecret')
This example obtains the commondataserviceClientSecret parameter value from Key Vault at the given Subscription, Resource Group, Key Vault name, and secret name. You must grant Azure Resource Manager access to the Key Vault, enabled by the setting shown below:
The Subscription ID, Resource Group name, and Key Vault name are read from environment variables using the readEnvironmentVariable function, showing another possibility for configuration alongside individual .bicepparm file per-environment.
In Summary
While this was a very simple Logic Apps use case, I hope it ties together the areas of connector automation, configuration, and security, helping you accelerate the time to a working solution. Happy integrating!