Table of Contents
Which to Choose for PowerShell Development?
I’m sometimes asked why people should bother using the Microsoft Graph PowerShell SDK to develop PowerShell scripts. The arguments against the SDK are that it’s buggy, doesn’t have great documentation, and adds an extra layer on top of Graph API requests. I can’t deny that the SDK has had recent quality problems that shook developer confidence.
I cannot advance a case that Microsoft’s documentation for the Graph PowerShell SDK cmdlets is good because it’s not. Some improvements have been made over the last year, but the examples given (copied mostly from the Graph documentation) are too simple, if they exist at all. There’s also the small fact that the Graph PowerShell SDK cmdlets share some foibles that make them less useful than they should be.
Given the problems, why continue to persist with the Graph PowerShell SDK? I guess the reason is that the SDK cmdlets are easier to work with for anyone who’s used to PowerShell development. For instance, the Graph SDK automatically performs housekeeping operations like retrieving an access token, renewing the token (only needed for long-running scripts), and pagination. None of these operations are complex. Once mastered, the same code can be copied into scripts to take care of these points.
Call me mad, I therefore persist in writing scripts using Graph PowerShell SDK cmdlets. However, times exist when it’s necessary to use a Graph API request, including when:
- Microsoft’s AutoRest process has not processed a new API to create a cmdlet.
- The data returned by a cmdlet is not as complete as the underlying Graph API request. This shouldn’t happen, but it does.
- It’s necessary to retrieve properties that a cmdlet doesn’t support.
Let’s look at examples of the last two points.
Fetching Attendee Data with Microsoft Graph PowerShell SDK Cmdlets and API Requests
I’ve used the List CalendarView API in situations like reporting usage statistics for room mailboxes. Here’s an example of retrieving calendar events between two dates.
$Uri = ("https://graph.microsoft.com/V1.0/users/{0}/calendar/calendarView?startDateTime={1}&endDateTime={2}" -f $Organizer.Id, $StartDateSearch, $EndDateSearch)
The resulting URI fed to the Invoke-MgGraphRequest cmdlet looks like this:
$Uri https://graph.microsoft.com/V1.0/users/4adf6057-95da-430a-8757-6a58c85e13d4/calendar/calendarView?startDateTime=2024-03-28T12:56:37&endDateTime=2024-05-29T12:56:37 $Items = Invoke-MgGraphRequest -Method Get -Uri $Uri | Select-Object -ExpandProperty Value
You might ask why I use Invoke-MgGraphRequest (a cmdlet from the Microsoft Graph PowerShell SDK) rather than the general-purpose Invoke-RestMethod cmdlet. It’s because I start scripts off with the Graph PowerShell SDK and only go to standard Graph API requests when necessary.
In any case, the attendees of a meeting are returned like this:
attendees {System.Collections.Hashtable, System.Collections.Hashtable, System.Collections.Hashtable, System.Collections.Hashtable}
The attendee data are available in individual hash tables and are easy to access:
$Items[0].attendees Name Value ---- ----- emailAddress {[address, Sean.Landy@office365itpros.com], [name, Sean Landy]} status {[response, none], [time, 01/01/0001 00:00:00]} type required emailAddress {[address, Lotte.Vetler@office365itpros.com], [name, Lotte Vetler (Paris)]} status {[response, none], [time, 01/01/0001 00:00:00]}
Get-MgUserCalendarView is the equivalent cmdlet in the Microsoft Graph PowerShell SDK. This command does the same job as the List CalendarView API request above.
[array]$CalendarItems = Get-MgUserCalendarView -UserId $Organizer.id -Startdatetime $StartDateSearch -Enddatetime $EndDateSearch -All Attendees : {Microsoft.Graph.PowerShell.Models.MicrosoftGraphAttendee, Microsoft.Graph.PowerShell.Models.MicrosoftGraphAttendee} $calendarItems[0].Attendees Type ---- required required
The attendee data is incomplete. No information is available about the attendees’ email addresses and display names. That’s why my scripts use the API rather than the cmdlet.
How the Microsoft Graph PowerShell SDK Cmdlets Return Data
When you run a Graph PowerShell SDK cmdlet, the returned data ends up in an array, which is convenient for further PowerShell processing. You’ll note that I use the -All parameter to fetch all available objects.
$AllUsers = Get-MgUser -All $AllUsers.gettype() IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True Object[] System.Array
Things are a little more complicated with Graph API requests. We get an array back, but the array contains a hashtable. The actual data that we might want to process is in the record with the Value key. We also see an @odata.nextlink to use to fetch the next page of available data:
$Uri = "https://graph.microsoft.com/v1.0/users" $Data = Invoke-MgGraphRequest -Method Get -Uri $Uri $Data.GetType() IsPublic IsSerial Name BaseType -------- -------- ---- -------- True True Object[] System.Array $Data Name Value ---- ----- @odata.context https://graph.microsoft.com/v1.0/$metadata#users value {44c0ca9c-d18c-4466-9492-c60c3eb78423, bcfa6ecb-cc5b-4357-909c-1e0e450864e3, da1288d5-e63c-4118-af62-3280823e04e1, de67dc4a-4a51-4d86-… @odata.nextLink https://graph.microsoft.com/v1.0/users?$skiptoken=RFNwdAIAAQAAABc6Z3NjYWxlc0BkYXRhcnVtYmxlLmNvbSlVc2VyX2UwNjIzZjE0LTUzM2QtNDhmYS1hODRl…
In most cases, I simply create an array of the data and then go ahead and process the information as normal for an array:
[array]$Data = $Data.Value
The Invoke-MgGraphRequest cmdlet supports output to a PowerShell object.
$Data = Invoke-MgGraphRequest -Method Get -Uri $Uri -OutputType PsObject
The output data is the same but it’s in the form of an array rather than a hash table:
$Data | Format-List @odata.context : https://graph.microsoft.com/v1.0/$metadata#users @odata.nextLink : https://graph.microsoft.com/v1.0/users?$skiptoken=RFNwdAIAAQAAABc6Z3NjYWxlc0BkYXRhcnVtYmxlLmNvbSlVc2VyX2UwNjIzZjE0LTUzM2QtNDhmYS1hODRlLTljOTg0MDhkNDgxYbkAAAAAAAAAAAAA value : {@{businessPhones=System.Object[]; displayName=12 Knocksinna (Gmail); givenName=12; jobTitle=; mail=12knocksinna@gmail.com; mobilePhone=; officeLocation=; preferredLanguage=; surname=Knocksinna; userPrincipalName=12knocksinna_gmail.com#EXT#@RedmondAssociates.onmicrosoft.com; id=44c0ca9c-d18c-4466-9492-c60c3eb78423}, @{businessPhones=System.Object[]; displayName=Email Channel for Exchange GOMs; givenName=Teams; jobTitle=; mail=5e2eb5ab.office365itpros.com@emea.teams.ms; mobilePhone=; officeLocation=; preferredLanguage=; surname=Email Channel for GOM List;
Once again, the data to process is in the Value record.
I usually don’t bother outputting to a PowerShell object, perhaps because I’m used to dealing with the hash table.
Mix and Match
The important thing to remember is that a PowerShell script can mix and match Graph API requests and Graph PowerShell cmdlets. My usual approach is to start with cmdlets and only use Graph requests when absolutely necessary. I know others will disagree with this approach, but it’s one that works for me.
Make sure that you’re not surprised about changes that appear inside Microsoft 365 applications by subscribing to the Office 365 for IT Pros eBook. Our monthly updates make sure that our subscribers stay informed.