ACloud.Solutions

Automation

Joiner mover leaver automation with Graph: the script that never forgets

The email says the developer's last day was Friday. It is Tuesday. Somewhere there is a nineteen-step checklist, and the honest answer to "has it been done" is that four steps have, the ones in the portal you had open at the time.

Joiner mover leaver automation is the process most worth automating in a small company, for a reason that is not efficiency. It is that a partially completed offboarding is invisible. Nothing alerts. The account still works, which is the whole problem, and nobody will notice until either an audit or an incident.

What joiner mover leaver automation has to do for a leaver

Nine things, and the order matters more than the list.

Disable the account and reset the password to something random nobody holds. Disabling alone does not invalidate an existing session.

Revoke refresh tokens. This is the step people miss and it is the one that matters most on the day. A disabled account with a live refresh token can continue accessing resources until the token expires, which can be hours. Graph exposes this directly and it takes one call.

Remove from all groups, recording which ones first. The record is what lets you reverse a mistake and is also the evidence of what access existed.

Reassign ownership of anything they solely owned: groups, Teams, applications, subscriptions. An orphaned group with no owner cannot be administered without elevation later.

Convert the mailbox to shared, if somebody needs continued access to it.

Remove licences, and here is the trap.

Remove devices or mark them for wipe or retirement depending on ownership.

Remove from distribution lists and external systems your directory does not cover.

Produce a report of every step, with timestamps and outcomes including failures.

The order trap that costs you a mailbox

Convert the mailbox before removing the licence. Not after.

Remove the licence first and the mailbox enters a disconnected state. It is still there, for a limited window measured in weeks, and it is no longer visible to the cmdlets you would use to convert it. So the script reports the licence removal as a success, the conversion step fails or silently does nothing, and the mailbox quietly ages out. By the time somebody asks for access to the leaver's email, the window has closed.

The same class of problem appears elsewhere in this process. Deleting the account before running the offboarding produces a cascade of not-found errors, each of which a naive script may treat as "nothing to do" rather than "the subject of this operation is gone". A leaver script should verify the account exists and is in the expected state before it starts, and refuse to run if it does not, rather than reporting a successful offboarding of an object that was already deleted.

Order the operations so that each one's precondition is still true when it runs, and assert the precondition rather than assuming it.

Idempotency, because it will be run twice

Somebody will run it again. Either because the first run failed halfway, or because two people were asked to do the same offboarding, or because the scheduled job retried.

So every step needs to be safe to repeat. Removing a group membership that is already removed is a no-op, not an error. Disabling a disabled account is fine. Converting an already-shared mailbox should be recognised and skipped rather than attempted and reported as a failure.

The distinction that makes this workable is between "already in the desired state", which is a success, and "could not determine the state", which is a failure. Collapsing those two is how a script ends up reporting green on a run that achieved nothing.

The report is the deliverable

The temptation is to treat the report as logging. It is not, it is the output.

For an auditor, a dated report showing that nine specific access removals occurred, with timestamps and the identity that performed them, is evidence that the leaver process operated. A ticket saying "offboarded as per checklist" is an assertion. The difference matters at Stage 2 of a certification audit, where the question is not whether you have a process but whether it ran, and the book covers what that evidence needs to look like.

So the report should record what was attempted, what succeeded, what was skipped and why, and what failed. Especially what failed. A report that only lists successes is a report somebody has to cross-reference against the checklist to trust.

Write it somewhere immutable enough to be evidence, with the leaver's name, the date, and the operator. A file in a document library with a retention policy is adequate and better than a console transcript nobody kept.

Running it from a runbook

An Automation account runbook or a scheduled pipeline beats a laptop, for the handover reason. A script that only runs from your machine, authenticating as you, stops working the day you are unavailable, which is precisely the day somebody else needs it.

That means a service principal or managed identity with the specific permissions required, which is the least-privilege question, and it means the job has to fail loudly, which is the exit code question. Both are separate notes because both apply to everything else here too.

Microsoft's Graph user management documentation covers the calls; the sequencing above is the part that is not in any reference.

Questions

Should the script delete the account or just disable it?

Disable, and leave deletion as a separate decision after a retention period. Deleting immediately destroys the object the mailbox and the group memberships hang off, and recovering a deleted account is a narrower window than people expect. Disable, strip access, keep the object.

Why revoke tokens if the account is disabled?

Because disabling prevents new sign-ins and does not necessarily invalidate an existing session immediately. Revoking refresh tokens closes that gap. It is one Graph call and it is the difference between access ending now and access ending whenever the token happens to expire.

What permissions does this need?

More than a reporting script, because it writes. User.ReadWrite.All, Group.ReadWrite.All, Directory.AccessAsUser.All for some operations, and Exchange permissions for the mailbox work. This is a case where the write access is genuinely required, which makes it more important than usual to know what holds it and to have an expiry date on the credential.

How do I handle the systems Graph does not cover?

List them explicitly in the script output as manual steps, with the owner of each. A script that silently covers eleven of fourteen systems is worse than one that covers eleven and tells you about the other three, because the first creates false confidence.

Is a mover the same as a leaver with extra steps?

Mostly the reverse: a mover is a leaver from one set of groups and a joiner to another, with nothing removed from the identity itself. The part people get wrong is the removal half, because adding new access is prompted by the person needing it and removing old access is prompted by nothing at all.