Microsoft Entra ID connector (Floh → Entra ID)¶
Built-in connector type entra-id provisions and manages cloud users and
groups in a Microsoft Entra ID (Azure AD) tenant through the Microsoft Graph
v1.0 API, using the OAuth2 client-credentials grant (application
permissions). It is the supported path for directory CRUD when a tenant does
not expose a SCIM endpoint.
Use this connector (not the generic
scimconnector) for Floh → Entra ID user/group lifecycle. For inbound provisioning (Entra → Floh) use Floh's inbound SCIM endpoint (/scim/v2) from Entra's enterprise-application provisioning.
Implementation: packages/server/src/modules/connectors/handlers/entra-id.ts
(+ entra-id-support/). App-registration setup is documented in
Entra ID setup.
Connection configuration¶
Create a connector instance of type entra-id with these fields:
| Field | Type | Required | Secret | Description |
|---|---|---|---|---|
tenantId |
string | Yes | No | Directory (tenant) ID — a GUID or a tenant domain (e.g. contoso.onmicrosoft.com). Wildcard authorities (common/organizations/consumers) are rejected. |
clientId |
string | Yes | No | Application (client) ID of the Entra app registration. |
clientSecret |
string | Yes | Yes | Client secret value for the app registration. |
defaultDomain |
string | No | No | UPN suffix used by createUser when userPrincipalName omits a domain (e.g. contoso.com). |
Example¶
{
"tenantId": "11111111-2222-3333-4444-555555555555",
"clientId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
"clientSecret": "<client-secret-value>",
"defaultDomain": "contoso.com"
}
Test: run the test command — it acquires a token and reads
GET /organization, returning tokenObtained, tenantId, and
organizationName. A 403 here usually means the application permissions have
not been admin-consented yet (see setup).
Required application permissions¶
Grant these application permissions (not delegated) on the app registration and grant admin consent:
| Permission | Used by |
|---|---|
User.ReadWrite.All |
user CRUD |
User-PasswordProfile.ReadWrite.All |
setPassword / first_password_invitation — app-only passwordProfile updates require this in addition to User.ReadWrite.All |
Group.ReadWrite.All |
group CRUD + membership |
Directory.Read.All |
test connectivity / tenant-GUID resolution |
Commands¶
Connectivity¶
| Command | Graph API | Outputs |
|---|---|---|
test |
GET /organization?$select=id,displayName |
tokenObtained, tenantId, organizationName |
Users¶
| Command | Graph API | Notes |
|---|---|---|
listUsers |
GET /users |
Sync-capable. Optional OData filter; paginate with pageToken → nextPageToken (Graph $skiptoken). |
getUser |
GET /users/{userKey} |
userKey = object ID or UPN. |
createUser |
POST /users |
userPrincipalName (domain auto-completed from defaultDomain if omitted), displayName, password (secret), optional mailNickname/givenName/surname/accountEnabled. |
updateUser |
PATCH /users/{userKey} |
Any of displayName/givenName/surname/jobTitle/department/accountEnabled. |
disableUser |
PATCH /users/{userKey} |
Sets accountEnabled: false. |
deleteUser |
DELETE /users/{userKey} |
Soft delete — restorable for 30 days via Entra deleted items. |
setPassword |
PATCH /users/{userKey} |
password (secret); never echoed in outputs. Compatible with the first_password_invitation step. |
Groups¶
| Command | Graph API | Notes |
|---|---|---|
listGroups |
GET /groups |
Sync-capable; same pagination as listUsers. |
getGroup |
GET /groups/{groupKey} |
|
createGroup |
POST /groups |
groupType: security (default) or microsoft365. Static membership only (no dynamic rules). |
updateGroup |
PATCH /groups/{groupKey} |
displayName and/or description. |
deleteGroup |
DELETE /groups/{groupKey} |
Microsoft 365 groups soft-delete (30 days); security groups are removed immediately. |
listGroupMembers |
GET /groups/{groupKey}/members |
Direct members; paginated. |
addGroupMember |
POST /groups/{groupKey}/members/$ref |
Idempotent — an "already a member" 400 returns alreadyMember: true. reconcilesWith: checkGroupMembership. |
removeGroupMember |
DELETE /groups/{groupKey}/members/{memberId}/$ref |
Idempotent — a 404 returns alreadyRemoved: true. |
checkGroupMembership |
GET /groups/{groupKey}/members |
Direct-membership scan (case-insensitive GUID match). Bounded at 500 pages × 999 members (~499k); if a group exceeds that it fails with "undetermined" rather than a false negative — avoid for very large groups. |
memberId for membership commands is the object ID (GUID) of the user or group
to add/remove.
Resource sync¶
listUsers and listGroups are syncCapable. In sync mode they return
normalized ConnectorResourceRecord rows and a nextCursor. User and group
records carry attributes.identityIssuer =
https://login.microsoftonline.com/<tenant-GUID>/v2.0; when the connector is
configured with a tenant domain, the tenant GUID is resolved from
/organization so the issuer matches the token tid used in identity
matching. See Resource sync.
Migrating from the mock test-activedirectory connector¶
Workflow templates that target the in-memory test-activedirectory mock map to
the following entra-id commands:
test-ad |
entra-id |
|---|---|
findUser |
getUser (or listUsers with $filter) |
createAccount |
createUser |
enableAccount / disableAccount |
updateUser (accountEnabled) / disableUser |
listUsers / listGroups |
listUsers / listGroups |
getGroupMembers |
listGroupMembers |
addToGroup / removeFromGroup |
addGroupMember / removeGroupMember |
checkGroupMembership |
checkGroupMembership |
setPassword |
setPassword |
Limitations (v1)¶
- Cloud directory only — no on-prem Active Directory / LDAP, hybrid-sync attributes, certificate auth, B2B guest invitation, license assignment, or dynamic group membership rules.
setPasswordmay fail for federated/synced users whose passwords are mastered on-premises; the Graph error is surfaced viaerrorMap.
Related docs¶
- Entra ID setup — app registration, permissions, admin consent
- Outbound SCIM connector — alternative IdP provisioning path
- Resource sync — syncing users/groups into Floh