The solution uses the native Power Pages Azure File API. File bytes are stored
in a private, customer-owned Azure Blob container, while Dataverse stores an
Annotation marker linked to an existing Contact record. A BlobCreated Event
Grid subscription invokes ScanNativeFile for every native upload.
Open the page with an existing Contact ID:
https://<site>.powerappsportals.com/Secure-Upload/?id=<contact-guid>
The implementation in power-pages/secure-upload.html supports chunked upload,
server-rendered attachment listing, download through a short-lived SAS URL, and
delete. The Liquid FetchXML listing only returns Annotations whose subject is
SCAN:SAFE. A new upload therefore remains hidden until the scanner marks its
Annotation safe.
Required site settings include:
Site/FileManagement/EnableWebAPI=trueSite/FileManagement/BlobStorageAccountName=<storage-account>Site/FileManagement/BlobStorageContainerName=<native-container>Site/FileManagement/SupportedFileTypeSite/FileManagement/SupportedMimeTypeSite/FileManagement/MaxFileSize=102400Site/FileManagement/DownloadViaSASUri=trueWebapi/annotation/enabled=trueWebapi/annotation/fields=*
The Contact table permission requires Read and Append To. The Note permission requires Read, Update, Create, Delete, and Append. Both are assigned to the Authenticated Users and Administrators web roles.
- Resource group for the solution
- General-purpose v2 Storage account
- Private native upload container
- .NET 8 isolated Function App
- Application Insights created with the Function App
- Storage-account Event Grid subscription targeting
ScanNativeFile
The portal enterprise application has Reader and Storage Blob Data Contributor roles. Blob CORS permits the Power Pages origin. Keep storage public access disabled.
No new browser-facing app registration or custom upload API is required. Event Grid uses the Azure Function destination integration; the application does not store an Event Grid client secret.
The Function scans the native blob in place. It derives the Annotation ID from the native blob path:
<annotation-guid-without-dashes>/<filename>
Clean files are retained and their Annotation subject is changed to
SCAN:SAFE. Infected, invalid, or scanner-error files are fail-closed: both the
blob and Annotation are deleted.
The current stub-fail-closed scanner deliberately rejects every upload.
Replace SymantecScanner.ScanAsync with the approved Symantec integration
and set SCANNER_MODE=symantec only after that integration is validated.
The safe-only listing is a user-interface gate, not a synchronous upload
quarantine. Before production use, enforce the same SCAN:SAFE rule on direct
download authorization so a caller who learns a pending Annotation ID cannot
download during the short scan window.
The Function App managed identity is registered as a Dataverse application
user with the Sentinel Native File Scanner role. The role grants global Note
Read, Write, and Delete privileges. Set:
DATAVERSE_URL=https://<environment>.crm.dynamics.comNATIVE_CONTAINER=<native-container>MAX_UPLOAD_BYTES=104857600ALLOWED_EXTENSIONS=.pdf,.doc,.docx,.xls,.xlsx,.txt,.png,.jpg,.jpegSCANNER_MODE=stub-fail-closeduntil Symantec is integrated
Install the .NET 8 SDK or newer, then run:
dotnet publish -c Release
Deploy the contents of bin/Release/net8.0/publish with zip deployment. The
storage-account Event Grid subscription must include only
Microsoft.Storage.BlobCreated, use the subject prefix
/blobServices/default/containers/<native-container>/blobs/, and target
ScanNativeFile.