We left our last case study on this client on a cliffhanger. A 44% license reduction, cleanly proven by five weeks of telemetry, got rejected on sight, not because the number was wrong, but because the security model behind it split every multi-license role into roughly a hundred narrower ones. The client's own words, in substance: a hundred-role model is unmaintainable, and they'd rather keep more of their license spend than hand their team something nobody could administer. They asked for a rebuild from first principles: cluster people by what they actually do, not by trimming the roles that already existed.

We said we'd report back. This is the report, and it starts with a version of the rebuild we never showed the client at all.

The first version worked, mathematically, and we couldn't ship it

Framed correctly, this is the Role Mining Problem: minimize the number of roles, on the condition that every user keeps everything the evidence says they genuinely need. It's a real optimization problem, not a heuristic, so our first attempt built exactly that: closed frequent-itemset mining over the residual access, feeding a weighted set-cover to select the minimum bundle set.

It found a mathematically clean answer. It also produced roughly 1,300 individual, effectively one-off roles, access patterns so specific to a single person that no bundle of any size fit them.

Bar chart: the first mining pass produced roughly 1,300 individual one-off roles; after adding duty-aware clustering, that fell to roughly 610.

We caught this ourselves before it ever reached the client, in our own internal review of the first pass. Thirteen hundred one-off roles isn't a rounding error in an otherwise-good design, it's a second unmaintainable model wearing a more sophisticated hat. Naive similarity clustering, it turned out, was too weak to see the actual shape of how this organization's people work: real duties, recognizable job functions, that a purely statistical clustering pass had no way to recognize as anything other than noise.

The fix was to stop asking the algorithm to invent structure from nothing, and instead let it snap residual access onto the organization's own existing job-function groupings wherever a real one existed, only falling back to a mined, unnamed cluster when nothing recognizable fit. That one change, plus a stricter rule that no shared core role is ever allowed to grant a single member more than their own evidence supports, cut the one-off tail by more than half, to roughly 610.

Better. Still not something we were ready to call finished.

The bug that was quietly hiding real savings

While refining the tail, we found something else: a chunk of the model's projected savings had never actually been real.

Part of the plan was straightforward, downgrade a privilege from write to read-only wherever telemetry showed navigation but no write evidence, and let the cheaper read-only license requirement flow through. The number the model was reporting for that lever looked plausible. It was also, we discovered on closer inspection, computed from a broken table that had quietly collapsed every access level onto the same license tier, meaning the "cheaper read-only tier" our model thought it was applying had never actually been different from the original write tier at all. The read-only downgrade lever had been running the whole time and doing nothing.

⚠ What this actually meant

D365 attaches a license requirement to a specific privilege, not to a generic "access level" layered on top of it. "View a record" and "maintain a record" are different privileges with their own separate license entitlements, not the same privilege at two settings. A model that assumes downgrading access automatically downgrades the license bill will be wrong exactly this way, quietly, and look correct in every report until someone checks the actual entitlement table underneath it.

Once the real per-privilege entitlement data was substituted in, hundreds of the downgrade candidates we'd been counting on turned out not to save anything at all, and a smaller, different set turned out to save real money we hadn't been crediting. The corrected picture was worse in headline optimism and better in accuracy, monthly projected cost dropped from an initial working estimate of $58,000 to a verified $47,000 once the fix was applied, a meaningfully different number built on evidence instead of an assumption that happened to sound reasonable.

Bar chart: modeled monthly cost before the read-tier fix at $58,000 per month, after the fix at $47,000 per month.

Where the first real design landed

With the tail reduced and the read-tier bug fixed, the first design we were willing to call correct came to roughly 760 roles and duties in total, a shared core for every recognizable job function, a set of mined add-ons for the genuinely cross-cutting access, and a residual individual layer for what truly couldn't be shared. Zero users came out over-licensed relative to their own evidence. Monthly cost landed at roughly $47,000, down from the original $72,000 baseline, and closer to the client's original $40,000 estimate than we expected, once the numbers were done honestly rather than optimistically.

It was also, on paper, mathematically the cheapest correct design achievable from the evidence, and we still weren't ready to call it done. Seven hundred and sixty roles is a real number to hand an IT security team, and "cheapest possible" and "something people can actually run" are not the same target. That tension is exactly where the client came back into the conversation, and it's the subject of the next piece in this series.

✓ What we'd tell the next client attempting this

Mining an optimal role set from real usage is a genuinely different, harder problem than trimming existing roles, and the first mathematically correct answer is very unlikely to be the one you actually ship. Budget real time for at least one full pass where you catch your own design's blind spots, the tail nobody predicted, the assumption baked into a savings number that turns out not to hold, before a client ever sees a draft.

Questions we get asked

Would this same tail problem happen to any organization trying this?

It's most likely on organizations where genuinely individualized work is common, people whose day-to-day access doesn't cleanly match anyone else's. Smaller, more uniform organizations tend to see a much smaller tail from the same technique, which is exactly the pattern we cover in our cross-client parameter study.

How did you catch the read-tier bug before it reached the client?

Internal review of our own projected savings, specifically, cross-checking a lever's reported savings against the actual per-privilege entitlement data it claimed to be using, rather than trusting a plausible-looking output number on its own.

Is a design with zero over-licensed users always the goal?

It's one legitimate goal, not the only one. It's also, as this piece shows, achievable at the cost of a role count nobody can actually administer. The next piece in this series covers what the client chose instead once they saw that trade-off explicitly.