Excellent questions on security and error handling - these were critical design decisions for us.
Authentication & Security:
We use Azure Managed Identity for the Function App to access Key Vault, which stores both the D365 service principal credentials and ERP API keys. The function authenticates to D365 using OAuth 2.0 client credentials flow with a dedicated application user (minimal privileges - read opportunities, write to custom sync entities). For the ERP, we use API keys rotated quarterly via automated Key Vault updates. No credentials are hardcoded or stored in app settings.
Azure Functions Trigger Configuration:
Our webhook registration uses the Plugin Registration Tool to target specific opportunity events (Update, Create on qualified+ stages). The webhook endpoint is secured with a shared secret validated in the function. We register asynchronous execution mode to prevent blocking D365 operations.
Data Transformation Pipeline:
The transformation layer is modular - we built reusable converters for common patterns (currency, dates, picklists to enums). Complex transformations use Azure Durable Functions for multi-step orchestration. For example, when syncing a closed-won opportunity, we orchestrate: 1) Fetch opportunity + related quote lines, 2) Transform to ERP order format, 3) Create ERP sales order, 4) Link ERP order ID back to D365 custom field, 5) Update sync status.
ERP API Integration & Error Handling:
We implemented comprehensive error handling with custom sync status entities in D365. Each opportunity has a related “Integration Status” record tracking: last sync timestamp, sync state (pending/success/failed), error messages, and retry count. When ERP sync fails, we:
- Log detailed error to Application Insights with correlation IDs
- Update Integration Status record with error details
- Send Teams notification to integration support channel for critical failures
- Automatically retry transient errors (timeouts, 429s) up to 3 times
- Flag permanent errors (400 validation) for manual review
We also built a model-driven app view showing all failed syncs with error details, allowing admins to manually trigger re-sync after fixing data issues. The ERP returns transaction IDs which we store in D365 for reconciliation.
Monitoring & Performance:
Application Insights dashboards track sync latency (average 2.3 seconds end-to-end), success rates (99.2% over 6 months), and error patterns. We set up alerts for sync failure rates exceeding 5% in any 15-minute window. The webhook approach reduced our previous batch processing from 30-minute cycles to near real-time (under 5 seconds from opportunity update to ERP sync completion).
Key Lessons Learned:
- Start with idempotent operations - our ERP upsert logic uses D365 opportunity GUID as external ID to prevent duplicates on retries
- Version your transformation mappings - we can roll back configuration without code deployment
- Monitor webhook registration health - we discovered webhooks can be inadvertently disabled during solution imports
- Plan for bi-directional sync complexity early - we initially built one-way but now handle ERP updates flowing back to D365
The combination of Dataverse webhooks, Azure Functions, and proper error handling gave us a scalable, maintainable integration that’s processed over 50,000 opportunities with minimal manual intervention. Happy to share more specifics on any component!