Grouper Datadog provisioner
The info on this page applies to releases after 3/20/2026 (TODO add versions)
- 1 External system
- 2 Links
- 3 Overview
- 4 Provisioning attributes
- 5 CRUD operations
- 6 Behavioral notes
External system
Grouper Datadog external system
Links
Datadog UI: https://us5.datadoghq.com/ (varies by site)
Overview
The Datadog Provisioner manages users, roles, teams, role memberships, and team memberships in a Datadog organization via the Datadog API v2.
Datadog uses JSON:API format for all v2 endpoints. All IDs are UUIDs.
The provisioning type is membershipObjects, meaning Grouper independently manages three object types in Datadog:
Entities -- Datadog users (people who can log in)
Groups -- Datadog roles or teams (depending on provisioner configuration)
Memberships -- Associations between users and roles, or between users and teams
Because Datadog assigns its own UUIDs to users, roles, and teams, the provisioner uses group and entity link (target attribute value caches) to track the Datadog-assigned IDs for each Grouper object.
The provisioner class is edu.internet2.middleware.grouper.app.datadog.DatadogProvisioner.
Service accounts (users with service_account=true) are automatically filtered out of all user retrieval operations. They are invisible to the provisioner.
Provisioning attributes
Advice
Provisioning type is hardcoded to membershipObjects
Use group and entity link (since there are UUIDs in the target for groups and entities that need to be looked up)
User (entity) attributes API
The provisioner supports the following entity attributes. Deleting an entity through the provisioner performs a soft-delete (disable via disabled=true), not a permanent removal.
Grouper Attribute Name | Type | Required? | Datadog API Field | Description |
|---|---|---|---|---|
| String (UUID) | Yes |
| Datadog-assigned user UUID. Read-only / select-only. Do not translate from Grouper. Cache it in an attribute value cache for linking. |
| String | Yes |
| Email address. Also used as the user's "handle" in Datadog. Used for entity matching and for the upsert check on create (409 conflict). |
| String | No |
| Display name of the user. |
| String | No |
| Job title. |
Role (group) attributes API
Grouper Attribute Name | Type | Required? | Datadog API Field | Description |
|---|---|---|---|---|
| String (UUID) | Yes |
| Datadog-assigned role UUID. Read-only / select-only. Cache it in an attribute value cache for linking. |
| String | Yes |
| The display name of the role in Datadog. |
Team (group) attributes API
Grouper Attribute Name | Type | Required? | Datadog API Field | Description |
|---|---|---|---|---|
| String (UUID) | Yes |
| Datadog-assigned team UUID. Read-only / select-only. Cache it in an attribute value cache for linking. |
| String | Yes |
| The display name of the team. |
| String | Yes |
| URL-friendly slug. Must be unique across the org. Typically derived from the team name (lowercase, hyphens). |
| String | No |
| Short summary of the team. |
| String | No |
| Longer description (supports markdown). |
| Set<String> (multi-valued) | No | n/a (virtual) | Multi-valued attribute containing Datadog user UUIDs of team admins. Not sent to the Datadog API directly; instead, changes to this attribute trigger |
Entity and group matching
Matching tells the provisioner how to find existing Datadog objects that correspond to Grouper objects.
Entity matching
Generally you should have id as the first search/match attribute, then email as a fallback.
Searching/Matching Attribute | Datadog Lookup Method | Notes |
|---|---|---|
|
| Direct lookup by Datadog-assigned UUID. Fastest method. |
|
| Search by email address using list endpoint with filter. Returns matches in the data array. |
CRUD operations
The provisioner supports the following operations.
Object | Operation | Supported? | Notes |
|---|---|---|---|
Entity (User) | Retrieve all | Yes | Retrieves all users, filtering out service accounts |
Retrieve one | Yes | By id or email | |
Insert | Yes | Invite user. Upsert behavior on 409 conflict (re-enable and update) | |
Update | Yes | PATCH with only changed attributes | |
Delete | Yes | Soft delete (PATCH with disabled=true), not permanent removal | |
Role (Group) | Retrieve all | Yes | Paginated list of all roles |
Retrieve one | Yes | By id | |
Insert | Yes | Returns 200 (not 201) | |
Update | Yes | PATCH with changed attributes | |
Delete | Yes | Permanent deletion (204) | |
Team (Group) | Retrieve all | Yes | Paginated list of all teams |
Retrieve one | Yes | By id | |
Insert | Yes | Returns 201 | |
Update | Yes | PATCH with changed attributes | |
Delete | Yes | Permanent deletion (204) | |
Role Membership | Retrieve by role | Yes | Lists all users in a role (paginated) |
Insert | Yes | Adds user to role | |
Delete | Yes | Removes user from role. Note: uses DELETE with a request body (unusual) | |
Team Membership | Retrieve by team | Yes | Lists all members of a team (paginated). Each membership has a "role" attribute (admin or member) |
Insert | Yes | Adds user to team with default role "member" | |
Update role | Yes | PATCH to change a member's role between "admin" and "member". Used by the admin metadata feature. | |
Delete | Yes | Removes user from team (204) |
Behavioral notes
Entity create is upsert
When creating (inviting) a new user, the provisioner POSTs to /api/v2/users. If Datadog returns a 409 conflict (user with that email already exists), the provisioner:
Looks up the existing user by email using
GET /api/v2/users?filter=email@example.comPATCHes the user to set
disabled=false(re-enable) and updates the name
Only if no conflict occurs does the POST create succeed as a new invite.
Entity delete is soft delete (disable)
When the provisioner deletes an entity, it PATCHes the user with disabled=true rather than calling DELETE. The user remains in Datadog in a disabled state and can be re-enabled later (e.g., by the upsert flow on a subsequent create).
Service accounts are excluded
Datadog service accounts (identified by service_account=true in the API response) are automatically filtered out of all user retrieval operations (list all and get single). They are invisible to the provisioner.
Role create returns 200
Unlike most REST APIs, Datadog's create role endpoint returns HTTP 200 (not 201) on success. The provisioner handles this.
Remove user from role uses DELETE with body
Removing a user from a role uses DELETE /api/v2/roles/{role_id}/users with a JSON request body containing the user ID. This is an unusual API pattern (DELETE with a body) but is how the Datadog API works.
Team membership roles and admin metadata
Each team membership in Datadog has a "role" attribute that is either "admin" or "member". When the provisioner adds a user to a team, the default role is "member".
How it works
Enable the datadogAddTeamAdminMetadata configuration option on the provisioner. This adds a metadata field called md_adminGroupName (labeled "Team admin group name") on each provisionable team group. Set this metadata value to the Grouper group path of an admin group (e.g., my:folder:teamAdmins). Members of that admin group who are also members of the team group will be promoted to the "admin" role in Datadog.
The admins target group attribute must be configured as a multi-valued attribute. The DatadogProvisioningTranslator automatically populates this attribute by resolving the admin group's members to their Datadog user UUIDs (from entityAttributeValueCache0). No JEXL translation expression is needed for this attribute.
Admin group membership requirement
Members of the admin group must also be members of the provisionable team group to receive the admin role in Datadog. The provisioner only manages team membership for members of the provisionable group -- the admin metadata only controls whether existing team members are promoted to admin or kept as regular members. A recommended pattern is to add the admin group as a member of the team group so that all admins are automatically team members.
Multiple provisioner runs needed
When a new team admin user is first provisioned, the admin role promotion requires two provisioner runs:
First run: Creates the team in Datadog and adds the user as a team member with the default "member" role. The translator populates the
adminsattribute on the Grouper side.Second run: The provisioner compares the Grouper-side
adminsattribute against the target and detects the difference. It then callsPATCH /api/v2/team/{teamId}/memberships/{userId}to promote the user to "admin".
Once a user is already a team member, subsequent changes to the admin group (adding or removing admin members) take effect on the next provisioner run.
JSON:API format
All Datadog v2 endpoints use JSON:API format. Request and response bodies use the structure:
{
"data": {
"type": "users",
"id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
"attributes": {
"name": "John Doe",
"email": "john.doe@example.com"
}
}
}
List endpoints return data as an array. Single-object endpoints return data as an object.
Paging
All list endpoints use page[size] (max 100) and page[number] (0-indexed). The provisioner pages through results until fewer than page[size] results are returned.