Skip to main content

MS Graph API: Query Groups & Planners

· 3 min read
Stephan Hochdörfer
Head of IT Business Operations

When we migrated to Office 365 some years ago, the Microsoft Graph API hooked me immediately. The API exposes almost all information stored in Office 365 and allows us to connect 3rd party tools to the data stored in Office 365.

As we have realized in the past, the API is good but far away from perfect. Not all the needed data is exposed or can be updated via the API. Sometimes, it can be pretty complicated to query the data you are interested in, especially when using the official SDKs.

Today's task is part of the latter category. For some house cleaning, I am interested in listing all planner boards in our org across all Office 365 groups.

It should be a relatively simple and straightforward task. According to the API docs, all I need is a request to the /groups endpoint, iterate over the response objects and query /groups/{group-id}/planner/plans. However, with the SDK from Microsoft, this is not that straightforward.

Iterating over all groups can be done like this:

use Microsoft\Graph\Generated\Groups\GroupsRequestBuilderGetRequestConfiguration;
use Microsoft\Graph\Generated\Models\Group;
use Microsoft\Graph\GraphServiceClient;
use Microsoft\Kiota\Authentication\Oauth\ClientCredentialContext;

$tenantId = '';
$clientId = '';
$clientSecret = '';

$tokenRequestContext = new ClientCredentialContext($tenantId, $clientId, $clientSecret);
$graphServiceClient = new GraphServiceClient($tokenRequestContext);

$groupsRequestConfig = new GroupsRequestBuilderGetRequestConfiguration();
$groupsRequestConfig->queryParameters = GroupsRequestBuilderGetRequestConfiguration::createQueryParameters();

try {
$groups = $graphServiceClient->groups()->get($groupsRequestConfig)->wait();
foreach ($groups->getValue() as $group) {
/** var Group $group */
} catch (Exception $e) {
echo $e->getMessage();

How to get the references to the planner objects for a specific group? There's a $group->getPlanner() method, but it always returns null. The method seems to only return data when the $expand query parameter is used to fetch additional data in one API request. However, that does not work for all connected objects.

I tried several approaches but never could make the logic work. The only way of getting it to work was the following - maybe "dirty" - implementation:

foreach ($groups->getValue() as $group) {
/** var Group $group */
$url = ''.$group->getId().'/planner/plans';
$plans = $graphServiceClient->planner()->withUrl($url)->get()->wait();
if ($plans === null) {

$additionalData = $plans->getAdditionalData();
if(!isset($additionalData['value']) or count($additionalData['value']) === 0) {

foreach($additionalData['value'] as $value) {


The $graphServiceClient->groups() client can instantiate groups with the byGroupId() method. Sadly, no such helper method exists for the $graphServiceClient->planner() client. So, the only option I had was to use the withUrl() method and pass the fully-qualified URL to the API endpoint to it.

Not ideal, but it works. Similar to the $group->getPlanner() method, which returned no data, the $plans->getPlans() method also returned no data. By inspecting the object, I realized the data I am interested in is stored as "additional data". That's why I am calling $plans->getAdditionalData() and iterating over that array to get all the data I am interested in.

In hindsight, it would have been easier not to use the official SDK to connect to the API but to query the two endpoints in question directly.