D365F&O Tenant Migration
The symptom
While troubleshooting a custom SysOperation batch process that uploads a generated CSV file to a SharePoint document library (via SharePointDocumentStorageProvider) after a Tenant Migration, I ran into an upload failure that looked like this:
Upload failed: Exception has been thrown by the target of an invocation.
Inner: An unknown error occurred while communicating with the SharePoint server '<tenant>.sharepoint.com'.
The exception itself is unhelpful — it's the generic wrapper message that the SharePoint client SDK returns whenever the underlying HTTP call fails, without exposing the actual status code or response body.
The first clue came from Document management parameters (Organization administration > Document management > Document management parameters > SharePoint). This page has two separate connection tests:
- Test interactive SharePoint connection — succeeded
- Test batch SharePoint connection — failed with "You are not authorized to connect to '<tenant>.sharepoint.com'"
That asymmetry was the key diagnostic signal. If the interactive test works but the batch test doesn't, the problem isn't the SharePoint site, the folder path, or the Document Type configuration — it's specifically about how non-interactive (application-only) calls authenticate.
Root cause
Interactive connections use the signed-in user's delegated permissions — essentially riding on the user's existing Microsoft 365 license and SharePoint access, just like a browser session would.
Batch connections are different. Since Microsoft deprecated the old Microsoft-managed high-trust connection between finance and operations environments and SharePoint (this affects environments on version 10.0.40 and later that have SharePoint user authentication enabled), batch jobs authenticate as an application rather than as a user. This means the finance and operations service principal needs its own application-level grant to talk to SharePoint Online — and that grant is not provisioned automatically. A tenant administrator has to assign it manually once per tenant.
If nobody has performed this one-time setup, every batch/non-interactive call to SharePoint — including custom X++ code using SharePointDocumentStorageProvider, and the built-in Export attachments feature — will fail with exactly the vague error shown above, while everything that goes through an interactive user session keeps working fine. This makes the problem easy to misdiagnose as a networking, firewall, or tenant-migration issue when it's actually a missing permission grant.
The fix
The fix is to grant the Microsoft Dynamics ERP first-party service principal an application permission (Sites.ReadWrite.All) against the SharePoint Online first-party service principal, in the target tenant. Both service principals already exist in every tenant that runs finance and operations and SharePoint — there's nothing to register or create.
Option A — PowerShell (Microsoft Graph SDK)
This is Microsoft's documented approach. Run it once, as a Global Administrator (or a role with Application.ReadWrite.All), against the tenant that hosts the finance and operations environment's SharePoint site:
Import-Module Microsoft.Graph.Applications
# Replace with your tenant
Connect-MgGraph -TenantId <yourtenant>.onmicrosoft.com -Scopes 'Application.ReadWrite.All'
# These AppIds are fixed first-party application IDs — they do not change between tenants
$erpServicePrincipal = Get-MgServicePrincipal -Filter "AppId eq '00000015-0000-0000-c000-000000000000'"
$sharePointServicePrincipal = Get-MgServicePrincipal -Filter "AppId eq '00000008-0000-0ff5-ci00-000000000000'"
$spAppRole = $sharePointServicePrincipal.AppRoles | Where-Object { $_.Value -eq 'Sites.ReadWrite.All' }
# Assign the SharePoint 'Sites.ReadWrite.All' application role to the Dynamics ERP service principal
New-MgServicePrincipalAppRoleAssignedTo `
-ServicePrincipalId $erpServicePrincipal.Id `
-PrincipalId $erpServicePrincipal.Id `
-ResourceId $sharePointServicePrincipal.Id `
-AppRoleId $spAppRole.Id
Option B — Microsoft Graph Explorer (no local PowerShell needed)
If PowerShell isn't installed locally, or a policy restricts running scripts on your machine, the exact same operation can be done through Graph Explorer, signed in as a Global Administrator:
1. Find the Dynamics ERP service principal
GET https://graph.microsoft.com/v1.0/servicePrincipals?$filter=appId eq '00000015-0000-0000-c000-000000000000'
Copy the id from the response — this is erpServicePrincipalId.
2. Find the SharePoint Online service principal
GET https://graph.microsoft.com/v1.0/servicePrincipals?$filter=appId eq '00000008-0000-0ff5-ci00-000000000000'
Copy the id — sharePointServicePrincipalId — and, from the appRoles array in the same response, find the entry where "value": "Sites.ReadWrite.All" and copy its id — this is appRoleId.
Note: Graph Explorer's default permission set (
User.Read) won't be enough for either of these steps. Under Modify permissions, consent toApplication.ReadWrite.Allfirst — as a Global Admin you can grant this directly from the same screen.
3. Grant the permission
POST https://graph.microsoft.com/v1.0/servicePrincipals/{erpServicePrincipalId}/appRoleAssignments
Request body:
{
"principalId": "{erpServicePrincipalId}",
"resourceId": "{sharePointServicePrincipalId}",
"appRoleId": "{appRoleId}"
}
A 201 Created response confirms the grant succeeded.
Verifying the fix
Back in Document management parameters > SharePoint, run Test batch SharePoint connection again. It should now report success, matching the interactive test.
A security note
Sites.ReadWrite.All is a broad grant — it gives the finance and operations application read/write access to every SharePoint site in the tenant, not just the specific document library used by a given integration. There's no officially documented narrower alternative for this particular first-party grant (the more restrictive Sites.Selected model applies to apps you register and control yourself, not to this Microsoft-managed service principal). If your organization has strict least-privilege requirements for SharePoint, this is worth flagging to your security team as an accepted trade-off and revisiting if Microsoft introduces a more granular option in the future.
Conclusion
When a finance and operations environment can reach SharePoint interactively but not from batch, don't waste time chasing networking, DNS, or tenant-migration theories first — check whether the one-time application consent for batch SharePoint access has actually been granted. This step is easy to miss because it doesn't live in D365 F&O at all: it's an Entra ID (Azure AD) application permission that must be granted once per tenant, and there's no button for it in the finance and operations client. Whether you use PowerShell or Graph Explorer, the operation is a single, five-minute application role assignment — but until it's done, every non-interactive SharePoint call from your environment, including custom integrations and the built-in Export attachments feature, will keep failing with the same unhelpful "unknown error" message.
Nenhum comentário:
Postar um comentário