Power Automate + Dataverse Web API: Run Bound Actions Safely (HTTP + App Registration + Excel “Target List”)
1. What this tool is for
This flow architecture uses the raw Dataverse Web API via the HTTP action to bypass the standard Power Automate connectors.
It securely authenticates using an Azure App Registration to execute complex, bound actions (like FulfillSalesOrder) against a strictly controlled list of records.
2. When to use it (and when not to)
Use it when:
- The standard Dataverse connector is throwing vague errors on legacy records, or
- You have a massive, specific list of client records that must be updated with zero collateral damage
Do not use it when:
- A simple 1-step Classic Workflow can achieve the same result without requiring App Registration secrets
3. Pre-flight checklist
Before you even open the Power Automate designer, you must have:
- The Keys: Azure Tenant ID, Client ID, Client Secret from an App Registration
- The Access: That App Registration added as an Application User in your Dataverse environment with the correct security roles
- The List: An Excel file uploaded to SharePoint/OneDrive, formatted as a Table, containing the exact Order Numbers you want to target
4. Step-by-step
We are going to let the Excel file drive the flow so we don't accidentally nuke the wrong records.
Step 1: Get the token (outside the loop)
Add an HTTP action at the very top of your flow to POST to your Azure Entra ID token endpoint.
Crucial structural note: put this step outside your main Excel loop.
If you put this inside the loop, you will hammer auth thousands of times, get throttled, and crash your flow.
Configure the HTTP action like this
Method: POST
URI: https://login.microsoftonline.com/YOUR_TENANT_ID/oauth2/token
Headers:
Content-Type:application/x-www-form-urlencoded
Body (x-www-form-urlencoded, no spaces):
client_id=YOUR_CLIENT_ID&client_secret=YOUR_SECRET&resource=YOUR_ENVIRONMENT_URL&grant_type=client_credentials
Consultant gotchas:
- Because this is
application/x-www-form-urlencoded, do not add spaces around&.
A space becomes part of your secret and the request fails. - Ensure
YOUR_ENVIRONMENT_URLdoes not end with a trailing slash/.
Step 2: Read the target list
Use Excel — List rows present in a table to grab the client’s list of Order Numbers.
Step 3: The cross-check
Add an Apply to each loop over the Excel rows.
Inside the loop, use a Dataverse — List rows action to search for the specific order:
ordernumber eq '[Excel Order Number]'
Step 4: The safety valve
Add a Condition inside the loop:
length(outputs('List_rows')?['body/value'])is equal to1
Do not be a hero and run this on thousands of production records without this safety net.
Step 5: The API strike (bound action)
In the If yes branch, add a second HTTP action pointing to the bound action endpoint:
https://[org].crm.dynamics.com/api/data/v9.2/salesorders/Microsoft.Dynamics.CRM.FulfillSalesOrder
Pass your token in the headers:
Authorization:Bearer [Token]
Use this JSON body:
{
"OrderClose": {
"@@odata.type": "Microsoft.Dynamics.CRM.orderclose",
"subject": "Closed manually to match corresponding order in BC",
"salesorderid@odata.bind": "/salesorders([Your Dynamic Sales Order ID])"
},
"Status": 100001
}