---
title: Access packages (MS Graph API)
description: How to query Entra ID access packages and manage user access package assignments using the MS Graph API connector for PingIDM and Advanced Identity Cloud
component: openicf
page_id: openicf:connector-reference:msgraph-access-packages
canonical_url: https://docs.pingidentity.com/openicf/connector-reference/msgraph-access-packages.html
llms_txt: https://docs.pingidentity.com/openicf/llms.txt
docs_for_agents: https://developer.pingidentity.com/build-with-ai/docs-for-agents.md
section_ids:
  msgraph-access-package-uid: Access package UID format
  msgraph-query-access-packages: Query access packages
  msgraph-read-access-package: Read an access package
  msgraph-access-packages-user: Read a user's access packages
  msgraph-update-access-packages-user: Update a user's access packages
  patch: PATCH
  put: PUT
  msgraph-access-package-limitations: Limitations
---

# Access packages (MS Graph API)

The MS Graph API connector supports [Entra ID Entitlement Management](https://learn.microsoft.com/en-us/entra/id-governance/entitlement-management-overview) access packages through two surfaces:

* The `accessPackage` object class: read and query only.

* The `__accessPackages__` attribute on the `user` object: readable on request, updatable using replace semantics.

|   |                                                                                                                                                                                                                                                                                                                                     |
| - | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | Reading or writing access packages requires the `EntitlementManagement.Read.All` permission. Granting or revoking access packages additionally requires `EntitlementManagement.ReadWrite.All`. Add these under the EntitlementManagement item when you [set API permissions](msgraph-conf.html#procedure-prepare-azuread) in Azure. |

## Access package UID format

Each access package can have multiple assignment policies. The connector represents each package+policy pair as a separate object. The `__UID__` and `__NAME__` both use a compound colon-delimited format.

`__UID__` format

```none
packageId:policyId
```

`__NAME__` format

```none
PackageDisplayName:PolicyDisplayName
```

Each object carries the single assignment policy it represents in the `assignmentPolicy` attribute, alongside the access package's own attributes. A package with three assignment policies returns three objects, each with the same package attributes and a different `assignmentPolicy`.

## Query access packages

This command queries all access packages:

```
curl \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"http://localhost:8080/openidm/system/azuread/accessPackage?_queryFilter=true&_fields=__NAME__,displayName"
{
  "result": [
    {
      "_id": "5b7c2fb6-ab91-4642-9b94-d447516efbe6:6e9ff82e-7b81-4047-9d35-395bd382ae98",
      "__NAME__": "TestAccessPackage:SecondPolicy",
      "displayName": "TestAccessPackage:SecondPolicy"
    },
    {
      "_id": "2d90ab77-2623-4557-bec5-144f8eb6d147:055b56c2-322c-40df-b999-87c350133ea4",
      "__NAME__": "Employee:Initial Policy",
      "displayName": "Employee:Initial Policy"
    }
  ],
  "resultCount": 2,
  "pagedResultsCookie": null,
  "totalPagedResultsPolicy": "NONE",
  "totalPagedResults": -1,
  "remainingPagedResults": -1
}
```

|   |                                                                                                                                                                                                                                           |
| - | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | The `accessPackage` object class uses offset-based paging (`$skip`), not the token-based paging (`$skiptoken`) used by other object types in this connector. Only `_queryFilter=true` (list all) and reads by compound UID are supported. |

## Read an access package

Use the compound `packageId:policyId` UID to read a specific access package:

```
curl \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"http://localhost:8080/openidm/system/azuread/accessPackage/5b7c2fb6-ab91-4642-9b94-d447516efbe6:6e9ff82e-7b81-4047-9d35-395bd382ae98"
{
  "_id": "5b7c2fb6-ab91-4642-9b94-d447516efbe6:6e9ff82e-7b81-4047-9d35-395bd382ae98",
  "__NAME__": "TestAccessPackage:SecondPolicy",
  "displayName": "TestAccessPackage:SecondPolicy",
  "description": "Test Access Package",
  "isHidden": false,
  "createdDateTime": "2026-06-16T14:56:03.380Z",
  "modifiedDateTime": "2026-06-16T14:56:03.380Z",
  "catalog": {
    "id": "fe096441-a946-4ae2-a013-3da9132c6b96",
    "displayName": "Employee",
    "catalogType": "userManaged",
    "state": "published",
    "isExternallyVisible": true
  },
  "assignmentPolicy": {
    "id": "6e9ff82e-7b81-4047-9d35-395bd382ae98",
    "displayName": "SecondPolicy",
    "description": "A test policy for access packages",
    "expiration": {
      "duration": "P365D",
      "type": "afterDuration"
    }
  }
}
```

## Read a user's access packages

The `__accessPackages__` attribute is not returned by default. Request it explicitly using `_fields`:

```
curl \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Accept-API-Version: resource=1.0" \
--request GET \
"http://localhost:8080/openidm/system/azuread/__ACCOUNT__/136bfdcd-dd11-4508-bb32-b333b3690ea2?_fields=__NAME__,__accessPackages__"
{
  "_id": "136bfdcd-dd11-4508-bb32-b333b3690ea2",
  "__NAME__": "adama@example.onmicrosoft.com",
  "__accessPackages__": [
    "5b7c2fb6-ab91-4642-9b94-d447516efbe6:6e9ff82e-7b81-4047-9d35-395bd382ae98",
    "2d90ab77-2623-4557-bec5-144f8eb6d147:055b56c2-322c-40df-b999-87c350133ea4"
  ]
}
```

The values match the `accessPackage` UID format: `packageId:policyId`.

|   |                                                                                                                                                                                                                                                                                                            |
| - | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | The connector returns assignments that are current, meaning their state is `Delivered`, `Delivering`, or `PartiallyDelivered`. A package you granted moments ago can appear here while Entra ID is still delivering it. Assignments in the terminal `Expired` and `DeliveryFailed` states aren't returned. |

## Update a user's access packages

Set `__accessPackages__` to the full desired list of `packageId:policyId` compound IDs. The connector computes a diff against the user's current assignments and posts the appropriate `ADMIN_ADD` and `ADMIN_REMOVE` requests to the Entitlement Management API.

|   |                                                                                                                                                             |
| - | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | Always send the complete desired set. The connector replaces the current state with the value you provide. It doesn't accept discrete add or remove values. |

You can update `__accessPackages__` using PATCH or PUT.

### PATCH

Use a JSON patch body with the `replace` operation:

```
curl \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Accept-API-Version: resource=1.0" \
--header "Content-Type: application/json" \
--request PATCH \
--data '[{
  "operation": "replace",
  "field": "__accessPackages__",
  "value": [
    "5b7c2fb6-ab91-4642-9b94-d447516efbe6:6e9ff82e-7b81-4047-9d35-395bd382ae98",
    "2d90ab77-2623-4557-bec5-144f8eb6d147:055b56c2-322c-40df-b999-87c350133ea4"
  ]
}]' \
"http://localhost:8080/openidm/system/azuread/__ACCOUNT__/136bfdcd-dd11-4508-bb32-b333b3690ea2"
```

### PUT

Use a PUT upsert to replace `__accessPackages__` alongside other user attributes:

```
curl \
--header "X-OpenIDM-Username: openidm-admin" \
--header "X-OpenIDM-Password: openidm-admin" \
--header "Accept-API-Version: resource=1.0" \
--header "Content-Type: application/json" \
--header "If-Match: *" \
--request PUT \
--data '{
  "__accessPackages__": [
    "5b7c2fb6-ab91-4642-9b94-d447516efbe6:6e9ff82e-7b81-4047-9d35-395bd382ae98",
    "2d90ab77-2623-4557-bec5-144f8eb6d147:055b56c2-322c-40df-b999-87c350133ea4"
  ]
}' \
"http://localhost:8080/openidm/system/azuread/__ACCOUNT__/136bfdcd-dd11-4508-bb32-b333b3690ea2"
```

|   |                                                                                                                                                                            |
| - | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|   | Access package assignment changes can take several seconds to a few minutes to propagate in Entra ID. A read immediately after an update might not yet reflect the change. |

## Limitations

* The `accessPackage` object class supports read and query only. Create, update, delete, and liveSync aren't supported.

* `__accessPackages__` on the user object supports `replace` semantics only, no `add` or `remove` patch operations.

* The `accessPackage` object class doesn't support search filters. Because each object is a synthetic combination of an access package and one of its assignment policies, no `accessPackage` attribute maps to a single filterable Graph property. Only two query forms work: `_queryFilter=true` to list everything, and a read by the compound UID. Any other filter fails with an `UnsupportedOperationException`.
