Field notes · automation

Account provisioning in systems never built for you

The badge system, the time clock, the vendor portals: a 3-tier approach to account provisioning beyond AD, and why I only automate the top two systems.

·7 min read

The core onboarding post creates the directory account, and the Microsoft 365 post gets the cloud half settled. Then reality arrives: the new hire also needs a login in the time-and-attendance platform, the email-security portal, the badge system, and whichever vendor application their department lives in. Account provisioning in those systems is where the elegant part of the automation ends, because none of them were built to be provisioned by you.

I mean that literally. These are systems whose vendors imagined an administrator clicking through their web console, one user at a time, forever. Some have an API bolted on later. Some can import a CSV. Some offer nothing but the click path. The trick is not forcing them all into automation. It is sorting them honestly into tiers and automating only where the volume justifies the fight.

Key takeaways

  • Every outside system gets an inventory row first: what it is, how an account is created and removed, and who does it.
  • Sort systems into three tiers: API, file import, or documented manual step. Do not fight a system into a higher tier than it supports.
  • Automate the one or two systems with real volume. A documented manual step is a fine tier for the rest.
  • Evidence beats memory: every outside-system account, created or removed, gets a confirmation pasted into the ticket.
  • The provisioning inventory and the revocation inventory are the same list read in opposite directions.

Where do you even start?

With the inventory, the same move this whole lifecycle keeps making. In the offboarding posts I keep an access inventory of every system where a person can hold a login. Provisioning reads that exact list forward: for each row, how does an account get created, by whom, and how do you know it worked? If you built the revocation inventory already, the provisioning column is an afternoon of filling in the reverse direction.

Writing it down, I found the same kind of thing the revocation side found. Two systems on my list had exactly one person who knew how to create an account, and one of those people was not me. The bus factor is the number of people who can disappear before a process stops working. A row that reads “ask the one person who knows” is a bus factor of one with a login page.

What are the three tiers of account provisioning?

API, file import, and documented manual step. Every outside system lands in one, and the tier decides how much automation it gets.

Tier What it looks like Where it fails
API A REST endpoint you can script Auth quirks, thin docs, breaking changes without notice
File import The admin console accepts a CSV of users Format drift, silent partial imports
Manual, documented A written click path with screenshots, owner named The document goes stale; the owner leaves

The mistake I made early was treating the manual tier as a failure. It is not. A documented manual step that takes four minutes per hire, executed from a checklist with a named owner, is a perfectly good process for a system that sees six hires a year. The failure is the undocumented manual step, the one that lives in somebody’s head and gets reinvented every time.

Which systems deserve real automation?

The ones where volume and pain intersect, which for me was exactly two. Rank your inventory by accounts touched per year and automate from the top. In my experience the small-shop curve is steep: a couple of systems touch every single hire and departure, and the rest see a handful of events a year. The math stops working fast. An hour of clicking per year does not repay a week of fighting an undocumented API.

For the top of the curve, the API tier looks like this shape, whatever the vendor: authenticate with a stored credential, send the new user, and treat anything other than an explicit success as a failure to investigate. In PowerShell that is Invoke-RestMethod with the credential pulled from a secure store, never pasted into the script:

# The shape of every vendor-API provisioning call I have written:
# stored credential, explicit success check, evidence into the ticket.
$headers = @{ Authorization = "Bearer $(Get-StoredApiToken -Name 'TimeClockApi')" }
$body = @{ employeeId = $req.EmployeeId; name = $req.DisplayName; role = 'staff' } |
    ConvertTo-Json

$resp = Invoke-RestMethod -Uri "$apiBase/users" -Method Post `
    -Headers $headers -Body $body -ContentType 'application/json'

if (-not $resp.id) { throw "Time clock API accepted the call but returned no user id." }
Add-TicketNote -Ticket $req.Ticket -Note "Time clock user $($resp.id) created."

The last two lines are the point. A vendor API that returns 200 has not necessarily done what you asked, so the script demands positive evidence (the created user’s id) and writes that evidence where the departure side will one day look for it.

What did the file-import tier teach me?

That the documented format and the working format are different documents. One system’s import spec listed three required columns. The working import needed a fourth, present in the vendor’s own sample file but mentioned nowhere, and rows without it imported silently as half-configured users. How did I find out? From the half-configured users, not from an error, because the import reported success either way.

So the file tier gets the same discipline as the API tier: after every import, read back what landed. Count the users, spot-check the one you just added, and paste the evidence into the ticket. A silent partial import discovered at day-one login is a bad first impression for the new hire and a worse one for the system that was supposed to prevent it.

How does this tie back to offboarding?

Every row you add here is a row the ghost hunt will walk in reverse, so build the row complete: creation column and removal column, together, the day the system enters the building. The badge system taught me this from the other side. Its removal step had a named owner and its creation step did not, so badges appeared informally and disappeared formally. Reconciling that took longer than writing both columns on day one would have.

The evidence habit pays twice, too. The ticket note that says “time clock user 4127 created” is exactly what makes the departure ticket checkable later. Provisioning without evidence becomes the ghost access the offboarding posts spend all their time hunting.

What broke, and what I would change

I automated the wrong system first. The email-security portal had the friendliest API, so it got scripted first, even though it saw maybe four account events a year. The time platform, which touches every hire, stayed manual for months because its API was unpleasant. I optimized for the fun of the integration instead of the volume of the work. I would not do that again: rank by events per year, automate from the top, and let the friendly-but-rare API stay a documented manual step.

And I learned to stop chasing completeness. My inventory has rows that will never be automated, and that is the honest end state for a small shop. The win was never “no clicking.” It was that every system has a written path, a named owner, and evidence when it runs, so that nothing depends on the one person who knows.

FAQ

How do you handle account provisioning in systems without an API?

Sort them honestly: if the admin console imports a CSV, script the file and verify what landed; if it is click-only, write the click path down with a named owner and put it on the onboarding checklist. A documented manual step is a legitimate tier, not a failure.

Which outside systems should you automate first?

Rank the inventory by accounts touched per year and start at the top. One or two systems typically touch every hire and departure; those repay automation. A system with six events a year does not repay a week against an undocumented API, however friendly it looks.

How do you know a vendor API or import actually worked?

Demand positive evidence: the created user’s id from the API response, or a read-back of the imported rows, pasted into the ticket. Success responses lie by omission, and a silent partial import surfaces as a half-configured user on day one.

Do provisioning and offboarding need separate inventories?

No, one inventory with two columns: how an account is created, and how it is removed, each with an owner. Fill both columns the day a system enters the environment. Every provisioning row you skip documenting becomes ghost access the offboarding process has to hunt later.