If you’ve ever managed user accounts in a growing organization, you know the hassle of keeping identities in sync across systems. One small mismatch can lead to access issues, security gaps, or compliance headaches. Automating identity lifecycle management is one of the best ways to help maintain a clean directory, reduce security risks from over-privileged or orphaned accounts, and help with compliance and regulatory standards.
In this article I am going to dive into Microsoft Entra ID API-driven inbound provisioning, including a high-level overview covering similarities and differences between it and the HR inbound provisioning options with Workday and SuccessFactors. I will also offer some practical tips and scripts, so hopefully you don’t end up spending nearly as much time as I did with troubleshooting and debugging issues.
API-driven inbound provisioning lets you push identity data from any authoritative source, including HR systems, payroll apps, spreadsheets, or even custom databases, directly into Entra ID. At its core, it uses the SCIM (System for Cross-domain Identity Management) standard to package and accept data via a bulk upload API endpoint. This means that you can build or use a client to extract data from your source, transform it as needed, and submit it to Entra ID’s provisioning service. The service then synchronizes the data with an Entra ID tenant or an on-premises Active Directory (AD) domain. The synchronization service is the same underlying service used by HR driven provisioning for Workday and SuccessFactors, just with pre-built connectors.
Practical Tips for Working with Provisioning
Microsoft has some great getting started articles for setting up inbound API provisioning. After the initial setup, I would suggest testing it out by going through the tutorials with curl or Graph Explorer. Once you are up and running, and you start to build your own client that will post data to the /bulkload endpoint, there are a few things to watch for.
Be Careful with How You Craft SCIM Batches
Be careful with your SCIM objects and batches. Ensure that your payloads validate against the SCIM schema. Microsoft has some example SCIM objects that can be used in the curl and Graph Explorer examples.
Case sensitivity is important when you are building your SCIM request. If you look closely at the examples from Microsoft, you will notice that all the attributes in the JSON payload are camel case, except for the operations element that contains all the users in the serialized “Bulk Request”
"schemas": ["urn:ietf:params:scim:api:messages:2.0:BulkRequest"],
"Operations": [
{
"method": "POST",
"bulkId": "00aa00aa-bb11-cc22-dd33-44ee44ee44ee",
"path": "/Users",
"data": {
"schemas": ["urn:ietf:params:scim:schemas:core:2.0:User",
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User"],
"externalId": "701984",
"userName": "bjensen@example.com",
"name ….
If you happen to be serializing your objects using camelCase, note that one attribute has to be uppercase. The following C# code snippet configures the JSON serializer to output valid
JSON. var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
Empty strings and null values will also cause errors when submitting SCIM requests.
Normally, you POST the batch to the endpoint and you receive a 202 response, which means the batch was accepted by Entra. Submitting an empty string for an attribute in the SCIM request will return the desired 202 response, but you will find that nothing is provisioned and there are no entries in the provisioning logs.
Managing the Provisioning Apps and Schemas
Partner with Microsoft experts you can trust
If it’s time to take that first step toward leveling up your organization’s security, get in touch with Ravenswood to start the conversation.
Once you configure the provisioning enterprise application in the Entra portal, it can be difficult to work with when you are making numerous changes, especially when you add new attributes and configure mappings. Automating these changes helps with naming consistency and moving configurations between development, test, and production environments. Automation also assists with configuring the service from scratch. In the Entra portal, you will find an option to download the provisioning schema. This schema is also accessible via the Graph API. It is simply a JSON document, which can easily be managed and updated with PowerShell.
The PowerShell function below allows you to add attributes to the schema for an AD or API-driven provisioning connection. This is particularly useful when you have attributes in your on-premises AD schema that are missing from the Entra schema configuration. Configure the ServicePprincipalId parameter to the GUID of your provisioning app registration.
Function Add-SchemaAttribute {
param(
[Parameter(Mandatory)]
[string]$ServicePrincipalId,
[Parameter()]
[ValidateSet("API", "On Premises Active Directory")]
$Set = "API",
[Parameter(Mandatory = $true)]
[string[]]
$AttributeName
)
$ServicePrincipal = Get-MgServicePrincipal -ServicePrincipalId $ServicePrincipalId
$job = Get-MgServicePrincipalSynchronizationJob -ServicePrincipalId $ServicePrincipal.Id
$uri = "https://graph.microsoft.com/v1.0/servicePrincipals/$($ServicePrincipal.Id)/synchronization/jobs/$($job.Id)/schema"
# The get-mgschema cmdlets don't return all the things that we need, use graph directly instead
$schemaJson = Invoke-MgGraphRequest -Method GET -Uri $uri
# Each "connector" has a directory, one for AD and one for the provisioning service API
$apiDir = $schemaJson.directories | Where-Object name -eq $Set
$apiObject = $apiDir.objects | Where-Object { $_ -is [hashtable] -and $_.attributes }
foreach ($attr in $AttributeName) {
Write-Verbose "Processing attribute $attr"
if ($apiObject.attributes | Where-Object name -eq $attr) {
Write-Verbose "Attribute $attr already exists"
}
else {
$apiObject.attributes += @{
name = $attr
type = 'String'
anchor = $false
caseExact = $false
multivalued = $false
required = $false
}
}
}
$putURI = "https://graph.microsoft.com/v1.0/servicePrincipals/$($ServicePrincipal.Id)/synchronization/jobs/$($job.Id)/schema"
$body = ($schemaJson | ConvertTo-Json -Depth 100)
Invoke-MgGraphRequest -Method PUT -Uri $putURI -Body $body -ContentType 'application/json'
}
The PowerShell function below allows you to back up the provisioning schema. This can be particularly useful if you choose to store your configuration in a version control system like Git.
Function Save-Schema {
param(
[Parameter()]
[string]$ServicePrincipalId)
$Path = ".\Schema-$(Get-Date -Format 'yyyyMMdd-HHmmss').json"
$ServicePrincipal = Get-MgServicePrincipal -ServicePrincipalId $ServicePrincipalId
$job = Get-MgServicePrincipalSynchronizationJob -ServicePrincipalId $ServicePrincipal.Id
$uri = "https://graph.microsoft.com/v1.0/servicePrincipals/$($ServicePrincipal.Id)/synchronization/jobs/$($job.Id)/schema"
$schemaJson = Invoke-MgGraphRequest -Method GET -Uri $uri
$schemaJson | ConvertTo-Json -Depth 100 | Out-File -FilePath $Path -Encoding utf8
}
You can use these PowerShell functions as inspiration to build additional automation for your provisioning project. For example, you could automate management of attribute mappings or export the entire configuration to compare tenants.
Filtering
You must consider how record filtering is implemented by the synchronization application. Because we control what data is sent to the API, it’s important to filter data that you submit to the inbound API and to configure filtering in the Entra provisioning application. In your client application, you can filter records before submitting them to the provisioning service. This design reduces load on Entra. If your source system (e.g. a human resources application) tracks when records were last updated, you could use a watermark to only submit records that have been recently updated to Entra. The filtering strategy that you choose will depend on your source data and business requirements.
In Entra’s provisioning configuration, you can use scoping options to apply rules such as only processing users in certain departments or with a specific attribute. Combining filtering in your client application with scoping in Entra improves efficiency by preventing unnecessary synchronization cycles that could make your provisioning application work overtime.
Conclusion and Summary
API-driven inbound provisioning is a great option for organizations needing flexible identity syncing beyond standard HR connectors. It mirrors the automation of Workday or SuccessFactors setups but gives you substantially more control, especially for custom sources, while supporting synchronization directly to Entra ID or AD for a variety of joiner, mover, and leaver scenarios.
If you are interested in taking advantage of provisioning using Entra or Entra IGA in general, please reach out; Ravenswood Technology Group has the expertise to help.


