The audit log showing an end user's account is an important clue, but it doesn't necessarily mean that the user manually deleted 10,000 records.
I'd investigate the effective Dataverse permissions and whether something was executing under that user's context.
A few things I'd check:
1. Verify the user's effective security roles
Even though the Entra security group is assigned the User access level, check all security roles assigned directly to the user and through every Entra group/team they belong to.
Pay particular attention to the Delete privilege on the affected table and its access level.
2. Check the canvas app for Delete/Remove operations
Search the app for functions such as:
Remove()
RemoveIf()
Patch()
SubmitForm()
Also check whether any button, timer, OnStart/OnVisible formula, or component can trigger a delete indirectly.
A loop or incorrect RemoveIf() condition could potentially delete a very large number of records surprisingly quickly.
3. Check Power Automate flows
This is probably one of the first places I'd investigate.
Look for flows using:
Delete a row
List rows
Apply to each
Dataverse Web API
Execute changes / custom actions
Check the run history around the exact time of the incident and identify which connection/account performed the operation.
4. Check whether the deletion was performed synchronously or asynchronously
10,000 deletions in approximately one hour sounds more like an automated/bulk operation than someone manually deleting records one by one.
Look at the audit entries around the first and last deletion and see whether they have the same operation, user, application/context information and timestamps.
5. Don't rely only on the "User" displayed in the audit record
The auditing user is useful, but you also want to determine the application/service context that generated the operation.
If the deletion originated from a canvas app, flow, custom API, plugin, integration, etc., the end user's identity may be the caller/context under which the operation was executed.
6. Check plug-ins and custom code
If the table has plug-ins, workflows, business rules, custom APIs or other server-side automation, verify whether any of them can cascade or initiate deletes.
Also check cascade delete relationships. A delete on a parent record can result in related records being deleted automatically.
7. Review the security role change/history
Since a custom security role was being used, verify exactly when the Delete privilege was removed and whether the role was assigned to any other users/teams.
I'd also check the user's Entra group memberships at the time of the incident. Removing the Delete privilege now doesn't tell you what effective privileges the user had when the deletion occurred.
Your recovery steps are good, especially restoring the environment and removing Delete from the custom role. I'd additionally consider keeping Delete restricted to a small administrative group and handling business deletions through a controlled process.
The most useful next step would be to take one or two audit records from the deletion window and inspect all available fields, not just the user name. The timestamp, operation, table, record, initiating user/application context and surrounding audit events can help determine whether this was actually a canvas-app action, automation, cascade delete, or some other server-side operation.
I wouldn't conclude that the end user intentionally deleted the records just because their account appears in the audit log.