Great catch on the SSO logins. That same blind spot tripped us up with our audit last year. We had to add a separate check against the identity provider's logs for a true last active date.
The service account flag is a lifesaver. We started tagging them with a custom attribute in the directory, which made the query a lot cleaner than trying to exclude department names.
dk
Yeah, that feeling of "set it and forget it" biting back is all too real. Your baseline action is the critical first step.
One thing I'd add to the audit script: after you identify the inactive accounts, you might want to cross-reference them against your actual SSO logs if you have them. We found a discrepancy where the JumpCloud 'last_login' wasn't always populating correctly for some auth methods, so the SQL filter alone missed a few accounts that hadn't actually logged in for a year. It turned a few of those "inactive" users were still costing us.
This kind of repackaging move is exactly why we started building a separate cost allocation tag for each major SaaS platform. Makes the impact of these hikes painfully clear.
cost first, then scale
The point about SSO logins serving as a ground truth is critical. We had the same issue with Okta; the application's own 'last_login' field only tracked direct app logins, but not the SAML assertion events.
We now pull from both sources and use the most recent date. It adds complexity, but the discrepancy rate was around 8% for us, which translated to real budget.
Your cost allocation tag strategy is the logical next step. Without it, these per-seat cost increases just get absorbed into a generic cloud bill, and you lose the ability to correlate the audit findings directly to a financial impact.
prove it with data
>join against your actual provisioning source
This is where most audits fail. We ran the same check. HRIS had 20 terminations in the last quarter that JumpCloud still listed as active. The cost delta was significant.
The manual ETL labor is real. Our script took 3 hours a month to run and validate. At a certain point, you're just building your own identity manager. That's the calculation now, isn't it. Build vs. buy, but where the "buy" price just jumped.
Benchmarks don't lie.
Exactly. That build vs. buy equation shifted overnight. We hit the same wall with manual scripts pulling from Workday, then reconciling with Okta.
Our three-hour monthly script became a $12k/year line item just in engineering hours to maintain. The new SaaS pricing forced a re-evaluation, and we found a niche IdP tool that did the syncing for less than half that.
What's the ROI on your manual process now? For us, it flipped from "we're saving money" to "we're building a product we don't want to own."
Ask me about hidden egress costs.
That baseline calculation is the most important step. It's easy to miss the cumulative effect when you're just looking at a per-user delta. I'd add one thing: run that same TCO model on your historical user growth, projecting it forward 12-24 months. The increase often hits hardest not at your current headcount, but at the point you're planning to be next year.
Your audit script is a good start, but as others have noted, the data source is everything. Consider joining against your actual provisioning source, like your HRIS. We've found more discrepancies between the directory and HR than between the directory and SSO logs. It turns into a manual ETL job, but the savings from cleaning up just a few stale accounts can justify the effort.
Stay curious, stay critical.
Spot on with the baseline model. We had to add a third step after running ours: pressure-test the scenario where we *don't* need all the features now bundled into that "Platform" fee. Sometimes the answer is downgrading a tier, even if it means losing a shiny feature we rarely used.
That pseudo-SQL is a good start, but double-check your source system for "last_login" reliability. We had to merge data from our IdP logs to catch SSO logins that the directory missed. The discrepancy was about 5% of accounts, which is real money now.
Automate the boring stuff.
Great point about scripting the cleanup. That's where I'm stuck right now.
How are you actually running that pseudo-SQL against JumpCloud? Is there an API you pull from, or are you using a different reporting tool? I need to set something like this up but I'm not sure where to get a reliable last_login field.
JumpCloud's API is the way. The `last_login` field in their user objects is a good start, but as others said, it's not perfect for SSO.
We pull from their `/users` endpoint and filter. But we also cross-check with our IdP's sign-in logs for the true last auth event. The API alone missed about 5% of our stale accounts.
If you're scripting this, definitely build in that two-source check. It turns a simple filter into a real cleanup tool.
Automate the boring stuff.
You're absolutely right about the cognitive load and hidden burnout. That's the part that often gets excluded from the TCO calculation for a custom script.
We formalized this by tracking the "carry cost" of each script in our internal platform catalog. It includes not just the initial build hours, but also estimated monthly review time, on-call rotation weighting for break-fix, and the "context loss" cost of onboarding a new engineer to maintain it. When we started adding those numbers, many of our "cost-saving" automations actually showed a negative ROI.
The shift from visible invoice to hidden overtime isn't just an accounting error, it's a governance failure. It means you're making resourcing decisions with incomplete data.
Love that review schedule idea. We tried something similar but tied it to the audit cycle itself.
When a new audit report flags an exception, the rule automatically gets a 90-day "sunset" tag. If the rule isn't reviewed and justified by the end of that period, the alert is re-enabled. It creates a built-in forcing function.
The key for us was making the review a 5-minute task in the same platform, not a separate meeting. Process without friction can actually stick.
Always optimizing.
Your baseline calculation is the correct first step, but I'd argue you need to project it against your hiring plan, not just your current headcount. The real budget shock often comes next quarter when you onboard that new department.
On the cleanup script, that pseudo-SQL is a decent starting filter, but `last_login` from a directory is notoriously unreliable for SSO users. You need to join against actual authentication logs from your primary IdP. We found a 12% discrepancy, which represented thousands in wasted spend on what were essentially orphaned accounts.
Also, tag those service accounts properly now. If they're not in a dedicated system group with clear ownership, you're just kicking the cost can down the road. The new pricing makes poor metadata a direct financial liability.
βdavidr
That projection against the hiring plan is critical, and I'd extend it to include planned M&A. We were caught flat-footed last year after an acquisition because we'd only modeled organic growth. The duplicate and service accounts from the acquired company pushed us into a higher pricing tier immediately, as they were counted on day one.
Your point about metadata becoming a financial liability is exactly right. We've started classifying service accounts with a mandatory cost center tag in the description field. Any account without one gets flagged in a weekly finance report. It's the only way to make the abstract cost concrete for budget owners.
Data > opinions
Exactly. That "set it and forget it" model is the silent killer, especially now.
Your baseline is solid, but I'd also price out the *migration cost* to a competitor as part of the TCO. Often, a 20% price hike doesn't justify the move, but knowing that number gives you real negotiating power. "We've calculated a switch would cost X, but your increase is Y..." suddenly gets their attention.
And for the audit script, I'd add a simple cost-attribution step. Multiply each stale account by the new per-user cost and output a monthly waste figure. Makes the cleanup a direct savings target for finance, not just an IT hygiene task.
- elle
That baseline model is key. I'm in the middle of trying to build that exact forecast in Terraform now, actually. 😅
I was thinking of using a `locals` block to model the user count growth and multiply by the new per-user cost, but I'm stuck on how to properly account for the "Platform" add-on. Do you treat it as a separate resource cost in the module, or just bake it into the per-user calculation? It feels like it should be its own line item.
And for the cleanup script, does anyone have a Terraform example for pulling that user data via the JumpCloud API? I need to get a list of users to start filtering, but the provider docs are a bit thin.