Most mid-market identity stacks already fire something when HR marks a hire or a termination. The IdP creates the account, assigns a few groups, and the standard SaaS apps follow. Joiner mover leaver automation that actually holds up in a SOC 2 sample has to keep going after that first hop — into the custom admin panels, internal tools, and homegrown consoles that never spoke SCIM and still show up on the auditor's system list. That last mile is the work we build.
The failure mode is consistent. Joiners wait days on tickets for the tools they need on day one. Movers keep the old role's groups after a transfer. Leavers lose IdP and email access on time, then leave a live row in an internal app nobody wired to the same event. NIST SP 800-53 AC-2 treats account management as a lifecycle, not a single disable click. Auditors sample the same way.
Joiner Mover Leaver Automation Stops Too Early
An HRIS export into Okta or Entra is necessary and not sufficient. Native SaaS connectors cover the apps the vendor already packaged. Everything else stays on a runbook: create the local user, add them to the support console, grant the warehouse role, remember to undo it when they leave. Runbooks work until a Friday termination, a same-week transfer, and a product launch collide.
We treat JML as three distinct event types with different payloads, not one "sync users" job:
- Joiner — worker appears in HRIS as active, with a job code, manager, and start date.
- Mover — job code, department, or location changes while the worker stays active.
- Leaver — status flips to terminated, or the last day is in the past.
If those three events do not reach the same connector layer, you do not have joiner mover leaver automation. You have onboarding for some apps and a hope for the rest.
The Event Path We Deliver: HRIS to IdP to Custom App
The architecture we ship is a narrow pipeline, not a second identity platform.
- HRIS is the source of truth for employment state. Workday, BambooHR, Rippling, or a UKG extract — we read hire, transfer, and terminate records, not a nightly dump of every field in the HCM.
- The IdP remains the source of truth for authentication. We do not invent a parallel login. The connector consumes the IdP user id after the HRIS event has already created or updated the IdP account.
- Custom apps receive idempotent provision and deprovision calls. Each target exposes either an API, a SCIM-ish facade we add, or a least-privilege database write scoped to status and role columns.
# HRIS webhook -> IdP confirm -> custom-app JML
def handle_hris_event(event):
worker = normalize_worker(event)
idp_user = idp.ensure_user(worker) # create / update / deactivate
mapping.upsert(worker.employee_id, idp_user.id)
for target in targets_for(worker.job_code, event.type):
if event.type == "joiner":
target.provision(idp_user, birthright(worker.job_code))
elif event.type == "mover":
target.reconcile(idp_user, birthright(worker.job_code))
elif event.type == "leaver":
target.disable(idp_user)
audit.write(
action=event.type,
employee_id=worker.employee_id,
system=target.name,
at=utcnow(),
)
The mapping table is part of the control. Custom apps almost never use the IdP subject as a primary key. If that map is missing, a leaver event cannot find the row to disable, and the sample fails even though the IdP look is clean.
Joiners: Birthright Access Instead of a Ticket Pile
Day-one access is a policy problem dressed up as a help-desk problem. We maintain a job-code map: role X gets IdP groups A/B, Salesforce permission set C, and custom-app role D. The joiner event applies that map. Exceptions still exist — a temporary project grant — but they are labeled as exceptions with an owner and an expiry, not mixed into the birthright set.
The tradeoff is explicit. A coarse job-code map over-provisions a few people. A fine map needs HR to keep job codes honest. We start coarse enough to ship, then split codes that consistently need a second grant. That is cheaper than leaving every joiner on twenty tickets and calling it "least privilege."
This is the same provisioning story we document for SOC 2 CC6.1 access provisioning automation — the control is the deterministic grant, not the ticket screenshot.
Movers: The Event Teams Forget to Automate
Transfers are where privilege accumulates. Someone moves from Support to Engineering and keeps the old Zendesk admin group "just in case." Quarterly recertification is supposed to catch it. Reviewers rubber-stamp what they already know.
A mover event is a reconcile, not an additive patch. The connector computes the new birthright set, diffs it against current grants on each target, adds the missing pieces, and revokes the prior-role leftovers in the same run. Human review sits on the revoke list when the grant is marked privileged or unused-but-sensitive — not on every group change.
If you only automate join and leave, movers become the silent leak. That is why this article is not a rewrite of a leaver-only deprovisioning piece. Hire and terminate are the easy events. Transfer is the one that keeps showing up as unexplained access in the next review window.
Usage-vs-grant revoke lists belong with privilege creep detection. JML is the event that should have prevented the creep.
Leavers: Custom Apps Are the Last Mile
IdP deactivate plus Google or Microsoft license removal is table stakes. The sample that fails is the internal deployment console, the billing admin tool, the homegrown customer-support override panel. Those accounts were created by a teammate on week one and never entered the IdP's app catalog.
For each mapped target we require a disable that is:
- Idempotent — a second leaver event does not error.
- Logged — success, already-disabled, or unreachable, each as a first-class outcome.
- Retried — a target that is down at 5:01 p.m. on a termination day cannot silently drop the event.
Systems with no API get a database-direct connector: a service account that can flip active and nothing else. Ugly, scoped, logged. Better than a wiki checklist.
Same-day mailbox transfer and seat reclaim for employees is a tighter sequence than this event bus; we cover that operational path in employee offboarding automation. JML is the spine those actions hang from.
Nightly Reconcile Versus Webhooks
Webhooks are fast and incomplete. HRIS events get missed, a contractor is entered as an employee, a transfer is keyed two days late. We run a nightly reconcile against the HRIS roster anyway: every active worker should match IdP state and custom-app state; every terminated worker should be disabled everywhere in the map. The webhook is the SLA. The reconcile is the safety net.
The ceiling is honest: this is an O(workers × targets) scan, fine for mid-market headcount, not a global directory of millions. If the roster grows past that, the upgrade is partitioned reconcile by system, not a new platform.
What the Auditor Actually Pulls
The artifact is not the connector code. It is a join of HRIS events to per-system actions across the observation window: employee id, event type, timestamp, target system, result. When the sample includes a hire, a transfer, and a termination, each row has a matching action on the custom apps — not only on the IdP. That is the difference between "we have an offboarding checklist" and SOC 2 CC6.3 access-removal evidence that survives sampling.
We do not claim a SOC 2 certification. We build the pipeline that produces the records the auditor asks for when they test the access lifecycle.
This pattern sits inside our security automation work: custom entitlement ingest and lifecycle enforcement for systems the GRC platform never reached. Spreadsheet reviews and ticket exports stay as the fallback only where a target truly cannot be called.
See what an Access & Credential Governance Diagnostic finds in your environment: rutagon.com/contact · 907-841-8407 · contact@rutagon.com.
Frequently Asked Questions
What is joiner mover leaver automation if we already sync HR to Okta?
HR-to-IdP sync is one hop. Joiner mover leaver automation is the rest of the path — birthright grants, transfer revoke, and terminate disable — on every system the auditor can sample, including apps with no SCIM connector.
Do we need a full IGA platform before this is worth building?
No. Mid-market stacks usually need a job-code map, an event consumer, and a handful of custom-app connectors. That is an engineering engagement, not a multi-year identity suite. The IGA product can come later if the catalog grows; the lifecycle still has to work now.
How do you handle a hire that starts before HR finishes the record?
We allow a controlled exception path: manager-requested access with an expiry that dies if the HRIS joiner never arrives. Standing "just create them in the app" remains the failure mode we are replacing.
What happens when a custom app is down during a termination?
The leaver event lands in a retry queue and the nightly reconcile keeps flagging the account as active-after-terminate until the disable succeeds. The audit log records the miss, the retries, and the eventual close — not a silent drop.
How is this different from contractor offboarding?
Employees have an HRIS trigger. Contractors often do not. Contractor end dates live in procurement or a SOW tracker, which is a different source of truth and a different SLA pipeline. Do not force contractors through a worker-only JML feed or you will miss them.