An operation that runs as a batch job is stamped with the batch service account, not the human who triggered it. On the tables where that happens, the audit log looks full and tells you nothing about any user.
One account, 44.6 million rows
On one client's `INVENTJOURNALTABLE`, 44.6 million rows were stamped as created by a single account. Not a prolific user. A batch service account. Every posting that ran through the batch framework recorded the service identity as the creator, so the create stamp that normally names the human instead names the machine.
This is the trap. The table has enormous write volume, so it looks like rich evidence. Read the actor behind the writes and it collapses to one non-human identity. There is no per-user signal in those 44.6 million rows, because they all belong to the same account.
Why batch attribution breaks the model
The entire write-evidence method rests on the create stamp naming a person. Posting a journal creates rows on a terminal table, the rows carry `CreatedBy`, and `CreatedBy` is the user. That chain holds when the user posts interactively. It breaks the moment the post runs under batch.
Under batch, the framework executes the operation on a schedule under the batch service account. D365 stamps `CreatedBy` with the account that ran the code, which is the service account, not the person who queued the job hours earlier. The human intent is real, but the stamp that would have captured it is overwritten by the service identity.
So a table dominated by batch activity produces exactly the wrong reading. Attribute those writes naively and either one service account appears to be the busiest user in the tenant, or, worse, the write credit spreads across every user who holds a privilege that maps to the table, crediting people who never triggered anything.
The failure is invisible until you check
Nothing about a batch-dominated table looks wrong from the outside. The row counts are high. The `CreatedBy` column is populated. The table maps cleanly to privileges. Totals and coverage all look healthy. The defect only shows up when you stop looking at how many rows were created and start looking at who created them.
That one shift, from volume to actor distribution, is the whole catch. A table with 44.6 million rows is impressive until you see that 44.6 million of them belong to one account.
The per-table actor-distribution check
The fix is a mechanical screen applied to every candidate table before it is trusted for attribution. For each table, compute how the create and modify activity is distributed across accounts, then disqualify the table when the activity concentrates in one or two non-human identities.
Concretely:
- - For each table, list the accounts and their share of `records_created`.
- - Flag any account matching the known service-role or Microsoft-system account lists.
- - If one or two such accounts dominate the table's activity, mark the table as unusable for per-user attribution and exclude it.
A table that fails this check yields zero per-user signal and must be dropped from the attribution set entirely. Keeping it does not add conservative, weak evidence; it adds false evidence, because it credits the service account's work to whichever humans happen to hold the mapped privilege.
A complementary signal: the lift test
Actor distribution catches the pure batch case. A second check catches the gray area. For a candidate table, compare `records_created` by users who hold the privilege against users who do not. If holders show materially higher activity, the mapping is real. If there is no lift, either the mapping is wrong or the activity is batched and attributed to a service account rather than to the holders. No lift is a reason to distrust the table, not to ship it.
Why you cannot paper over it
It is tempting to subtract the service account and keep whatever human rows remain. Sometimes that works. Often it does not, because the human who triggered the batch never appears on the table at all; only the service account does. There is no residual human signal to recover. The honest outcome is to exclude the table and document that posting on it is a batch operation with no per-user attribution, rather than invent a user from a stamp that only ever held a machine.
Bottom line
Batch jobs stamp the service account, so a table can show tens of millions of writes and still carry zero per-user signal, as `INVENTJOURNALTABLE` did at 44.6 million rows under one account. Run a per-table actor-distribution check before trusting any table for attribution, exclude tables that concentrate in one or two service accounts, and back it with a lift test. Volume is not evidence until you know who is behind it.
Frequently asked questions
Can we recover the real user by looking at when the batch job was queued?
Sometimes, if the batch header retains who scheduled it, but that is a separate record from the posted rows and is not the create stamp this method reads. Treat scheduler identity as a different, weaker source and do not blend it into the table's create-stamp evidence.
Should we exclude every high-volume table as a precaution?
No. Volume alone is fine. Exclude only tables whose activity concentrates in service or system accounts. A high-volume table spread across many real users is strong evidence, not a problem.
How do we know which accounts are service accounts?
Maintain explicit service-role and system-account lists and match the table's top creators against them. The check is only as good as those lists, so keeping them current is part of the method.