Extensible Identity Flows: How ConductorOne Finally Made Joiner Provisioning Bend to Your Rules
Ali Falahi, Solutions Engineer
Share
Content
Stay in touch
The best way to keep up with identity security tips, guides, and industry best practices.
Every Joiner flow starts simple. None of them stays that way.
I’ve spent years helping organizations automate identity provisioning, and the conversation always starts the same: “We just need to set up accounts for new hires.” AD account, Google Workspace, the right Okta groups. Classic Day 1 stuff.
Then reality kicks in.
The username policy? First initial plus last name, but if that’s taken, append a number. And make sure the sAMAccountName stays under 20 characters, because Active Directory has enforced that limit since the NT 4.0 days, and it’s not going anywhere. The Common Name needs proper capitalization derived from the email. Mail-enabled groups need specific attributes set at creation time, not after. Different departments route through different approval chains.
This isn’t unique to any one platform or tool. It’s the nature of Joiner flows. Browse any identity community (Reddit’s r/sysadmin, PowerShell boards) and you’ll find the same patterns everywhere. Someone asks “how does your script handle duplicate sAMAccountNames?” and the replies are a mix of incrementing suffixes, middle initial fallbacks, and people admitting they just handle it manually when it comes up. Someone else posts about needing sAMAccountNames that are never reused, even after an employee leaves. Another thread details the nightmare of provisioning across hybrid environments where the HRIS, AD, and cloud IdP all disagree on what a “department” means.
The tooling doesn’t matter. Whether teams are using PowerShell runbooks triggered by an Azure Automation account, Python scripts kicked off by a webhook, or a cron job on a server that nobody remembers setting up, the underlying challenge is the same: Joiner flows accumulate business logic that eventually outgrows whatever system is trying to express it.
For a long time, the options for handling this overflow were all variations of the same compromise: build it outside the governance boundary. External scripts, standalone Lambda functions, webhook chains stitched together across services. They work, until they don’t. And when they break, there’s no audit trail, no version history, and usually no documentation from the person who built them two years ago.
We wanted to solve that problem. So we built something.
The Joiner problem nobody warns you about
Here’s the thing nobody mentions at identity conferences: the hard part of JML isn’t the “M” or the “L.” It’s the “J.”
Movers and Leavers are conceptually simpler. You’re changing or removing things that already exist. Joiners are generative. You are constructing identity from scratch across multiple systems, and every organization has completely different rules for how that works.
I’ve seen it at every customer engagement. The conversation always starts the same way:
“We just need to automate new hire provisioning.”
And then 45 minutes into the discovery call, you learn:
Username generation has 6 fallback rules and a uniqueness check against both AD and Google
Group membership is derived from some combination of department, location, and cost center that doesn’t map cleanly to any existing structure
Attribute mapping between the HRIS and the target systems requires string manipulation that makes your head spin
Conditional provisioning means some users get Salesforce, some get Jira, some get both, and the logic lives in a spreadsheet that someone in HR updates quarterly
There’s always a special case for contractors, interns, or executives that breaks every assumption
This isn’t a configuration problem. This is the kind of problem where you need the freedom to describe exactly what should happen, in your own terms, for your own environment.
What if your identity platform could just… do what you tell it?
This is where Functions come in, and more importantly, where the idea of extensible identity flows becomes real.
Functions is a capability inside ConductorOne that lets you inject custom logic directly into your automation workflows. Instead of fighting with expression languages or building external scripts, you describe the behavior you need and it runs right inside the platform. Same audit trail. Same governance. Same observability as everything else.
But here’s the part that changes the game for admins: you don’t have to write the code yourself.
ConductorOne includes a Functions Author Agent, an AI assistant that generates the function for you. You describe what you need in plain language:
“When a new hire joins, generate a username from their first initial and last name. If it’s taken, add a number. Make sure it’s under 20 characters for AD. Capitalize the Common Name properly.”
The agent writes the function. You review it. You deploy it as a step in your automation. Done.
No TypeScript expertise required. No hiring a developer to build a webhook. You’re describing the solution you want, and the platform handles the implementation. For identity admins who have been stuck choosing between “not quite flexible enough” and “now I need an engineering team,” this is the sweet spot.
The Joiner challenges this actually solves
Let me walk through the real-world problems I keep running into with customers and how extensible identity flows address each one.
Username generation that actually works
Every org has a username policy. Every username policy has edge cases. The typical approach is to try jsmith, then jsmith1, then jsmith2, until something’s available. It is a simple concept, but impossible to handle with a basic expression because you need to check what already exists in real time.
With Functions, the logic can query ConductorOne’s user directory mid-flow to find the next available username. It respects your naming conventions, handles the AD character limits, and derives display names correctly. When the next edge case shows up (“oh, we also need to handle hyphenated last names”), you just tell the agent what to change.
CEL can handle straightforward attribute transformations well, and for many orgs, that’s enough. But when you need to check for username conflicts across live directory data, or chain together multiple fallback rules that depend on query results, you’ve moved past what expressions are designed for. That’s exactly the gap Functions fills.
Tell the agent: “Generate a username from first initial and last name, check if it’s taken, increment if needed, and capitalize the Common Name properly.” It builds the logic, queries real data at runtime, and handles all the edge cases in one step.
Issuing a Temporary Access Pass on Day 1 with Entra ID
Conditional provisioning, group assignment, app access — ConductorOne handles all of that natively. But there’s a class of Day 1 problems that require reaching outside the platform into the identity infrastructure itself. Issuing a Temporary Access Pass (TAP) in Entra ID is a perfect example.
A TAP is a time-limited, one-time passcode that lets a new hire bootstrap their MFA enrollment without needing a pre-existing credential. It’s exactly what you want on Day 1 — but Entra doesn’t issue one automatically when an account is provisioned. Normally, that means a manual step, a separate script, or an IT tech doing it by hand.
With Functions, you can close that gap entirely. A function step at the end of your Joiner flow calls the Microsoft Graph API, issues a TAP for the new user, and — depending on your process — delivers it to the manager via Slack or email, or logs it for IT. The new hire shows up on Day 1 and can enroll in MFA immediately, without any human in the loop.
This is what “extending the platform” actually means in practice. ConductorOne orchestrates the provisioning. Functions reach into Entra at exactly the right moment and do the thing the platform can’t do on its own. All logged, all governed, all in one place.
Writing back to the HRIS after provisioning
Most identity platforms treat the HRIS as a read-only source of truth: data flows out of it and into downstream systems. But provisioning generates data too, and that data often needs to go back.
The classic example: a new hire’s work email is generated during provisioning, derived from their name and whatever username logic your org uses. But the HRIS still has their personal email on file. IT tickets pile up because the onboarding system is sending welcome emails to a Gmail address. HR can’t find the person in the directory because the email in Workday doesn’t match what’s in AD or Google.
With Functions, you can flip the flow. Once ConductorOne provisions the account and the work email is confirmed, a function step calls the HRIS API and writes the value back. The same goes for employee ID, manager, department code, or any other attribute that gets finalized during provisioning rather than before it. The HRIS becomes a living record, not just a starting point.
This is exactly the kind of bidirectional integration that connectors aren’t designed for. Connectors read. Functions write back, conditionally, with the right data, at exactly the right moment in the flow.
Ordering equipment through your ticketing system at provisioning time
Notifications, Slack messages, manager confirmations — ConductorOne handles those natively as part of the automation. But there’s a category of post-provisioning action that goes beyond notification: triggering work in systems that live outside the identity world entirely.
Equipment ordering is the clearest example. When a new hire is provisioned, IT needs a laptop and peripherals waiting for them on Day 1. That request typically lives in a ticketing system like ServiceNow or Jira Service Management, and it needs to carry specific details: the new hire’s name, start date, location, role, and whatever hardware profile maps to their department.
A Function step at the end of the Joiner flow can hit the ticketing system’s API directly, create the equipment request with the right fields pre-populated from the provisioning data, and assign it to the correct IT queue — all without a human copy-pasting from an onboarding spreadsheet. By the time anyone looks at the ticket, the account is provisioned and the hardware request is already in the queue.
This is the kind of cross-system orchestration that used to require a dedicated integration platform or a custom script nobody owns. With Functions, it’s just another step in the flow.
Why keeping it inside the platform matters
I joke around about the duct-tape solutions, but here’s what genuinely keeps me up at night: the governance gap.
Every time a customer builds identity logic outside the platform, every external script, every standalone webhook, every “I’ll just throw this in a Lambda” — that’s logic living outside the audit trail. The CISO can’t see it. The compliance team can’t review it. When it breaks at 2 AM, nobody knows what it was supposed to do, since it was written by someone who left the company 2 years ago.
Functions close that gap. Everything runs inside ConductorOne’s secure, sandboxed environment:
Execution is logged: every invocation is captured and visible in the UI
Secrets are managed: API keys stored securely, not hardcoded in scripts on someone’s laptop
Versions are tracked: you can see what changed and when
Access is controlled: functions operate within defined roles and permissions
For compliance-conscious organizations (so… everyone at this point), this is a big deal. Your custom identity logic gets the same governance treatment as every other part of your access management.
The “I’m not a developer” objection
Let’s address this directly, because I hear it in almost every conversation.
“This sounds great, but my team doesn’t write code.”
I get it. And that’s exactly why we built the Functions Author Agent. The workflow looks like this:
You describe what you need in plain language. “When someone joins from the Engineering department, add them to these groups and provision these apps. If they’re a contractor, skip the VPN.”
The agent generates the function, a working implementation based on your description
You review and deploy: plug it into your automation as a step
It runs inside ConductorOne, fully logged, governed, and observable
If something needs to change, you tell the agent. It updates the function. You’re the identity expert driving the outcome. The agent handles the implementation details.
This isn’t “learn to code to manage identity.” It’s “describe what your organization needs and let the platform do it.”
What’s actually in the box
For the curious, here’s what Functions provides under the hood:
Secure runtime: code runs in an isolated, sandboxed environment inside ConductorOne
Pre-authenticated access: Functions can query users, apps, entitlements, and grants through the ConductorOne SDK without managing credentials
Secrets management: securely store and access API keys for external integrations
Outbound networking: connect to approved external APIs through an explicit allowlist
Real-time logging: see exactly what happened during execution, right in the UI
Built-in testing: validate functions before deploying to production
Event triggers: fire on user lifecycle events, access reviews, schedules, and more
npm package support: use popular libraries for things like date formatting, data validation, and API clients
Wrapping up
If you’re building Joiner flows and you’ve hit the wall — the wall where the platform’s built-in expressions can’t handle your username policy, where conditional provisioning is turning into a maintenance nightmare, and where critical identity logic lives in scripts outside your governance boundary — there’s a better way now.
Extensible identity flows give you the freedom to describe exactly what your organization needs, and ConductorOne handles the rest. No external scripts. No engineering team required. Just your rules, running inside your identity platform, fully governed and fully visible.
The ceiling that used to exist between “what we can configure” and “what we actually need”? It’s gone.
Check out the docs to learn more, or book a demo and I’ll walk you through how this applies to your specific Joiner flows. Fair warning: I might ask to see your current CEL expressions. Purely for research purposes.