PowerShell and the Microsoft Graph API : Part 2 – Starting to explore

In the previous post I looked at logging on to use Graph – my msftgraph module has a Connect-MsGraph function which contains all of that and saves refresh tokens so it can get an access token without repeating the logon process. It also refreshes the token when its time is up. Once I have the token, I can start calling the rest API. Everything in graph has a URL which looks like
https://graph.microsoft.com/version/type/id/subdivision

Version is either “V1.0” or “beta” ; the resource type might be “user” or “group”, or “notebook” and so on and a useful one is “me”; but you might call user/ID to get a different user. To get the data you make an HTTP GET request which returns JSON; to add something it is usually a POST request with the body containing JSON which describes what you want to add, updates happen with a PATCH request (more JSON), and DELETE requests do what you’d expect. Not everything supports all four – there are a few things which allow creation, but modification or deletion are on someone’s to do list.

The Connect-MsGraph function runs the following so the other functions can use the token in whichever way is easiest:

    if ($Response.access_token) {
        $Script:AccessToken   = $Response.access_token
        $Script:AuthHeader    = 'Bearer ' + $Response.access_token
        $Script:DefaultHeader = @{Authorization = $Script:AuthHeader}
    }
    

– by using the script: scope they are available throughout the module, and I can I run

    $result = Invoke-WebRequest -Uri "https://graph.microsoft.com/v1.0/me" -Headers $DefaultHeader
    

Afterwards, $result.Content will contain this block of JSON
{ "@odata.context": "https://graph.microsoft.com/v1.0/$metadata\#users/$entity", "businessPhones":[], "displayName": "James O'Neill", "givenName": "James", "jobTitle": null,"mail": "xxxxx@xxxxxx.com", "mobilePhone": "+447890101010", "officeLocation":null, "preferredLanguage": "en-GB", "surname": "O'Neill", "userPrincipalName":"xxxxx@xxxxxx.com", "id": "12345678-abcd-6789-ab12-345678912345" }

It doesn’t space it out to make it easy to read! There’s a better way: Invoke-RestMethod creates a PowerShell object, like this:

Invoke-Restmethod -Uri "https://graph.microsoft.com/v1.0/me" -Headers $DefaultHeader

@odata.context : https://graph.microsoft.com/v1.0/$metadata\#users/$entity
businessPhones : {}
displayName : James O'Neill
givenName : James
jobTitle :
mail : xxxxx@xxxxxx.com
mobilePhone : +447890101010
officeLocation :
preferredLanguage : en-GB
surname : O'Neill
userPrincipalName : xxxxx@xxxxxx.com
id : 12345678-abcd-6789-ab12-345678912345

Invoke-RestMethod automates the conversion of JSON into a PowerShell object; so this:

    $D = Invoke-Restmethod -Uri "https://graph.microsoft.com/v1.0/me/drive" -Headers $DefaultHeader
    

will let me refer to $D.webUrl to get the path to send a browser to in order to see my OneDrive. It is quite easy to work out what to do with the objects which come back from Invoke-RestMethod; arrays tend to come back in a .value property, some data is paged and gives a property named @odata.nextLink, other objects – like “me” give everything on the object. Writing the module, I added some formatting XML so PowerShell would display things nicely. The work is discovering URIs that are available to send a GET to, and what extra parameters can be used – this isn’t 100% consistent – especially around adding query parameters to the end of a URL (some don’t allow filtering, some do but it might be case sensitive or insensitive, it might not combine with other query parameters and so on) and although the Microsoft documentation is pretty good, in some places it does feel like a work in progress. I ended up drawing a map and labelling it with the functions I was building in the module – user related stuff is on the left, teams and groups on the right and things which apply to both are in the middle. The Visio which this is based on, and a PDF version of it are in the Repo at https://github.com/jhoneill/MsftGraph

Graph API Relationships

Once you can make your first call to the API the same techniques come up again and again , and future posts will talk how to get PowerShell formatting working nicely, and how to create JSON for POST requests without massive amounts of “text wrangling” But, as you can see from the map, there are many rabbit holes to go down, I started with a desire to post a message to a channel in Teams. Then I saw there was support for OneDrive and OneNote and work I had done on them in the past called out for a re-visit. Once I started working with OneDrive I wanted tab completion to expand files and folders, so I had to write an argument completer… and every time I looked at the documentation I saw “There is this bit you haven’t done” so I added more (I don’t have anywhere to experiment with Intune so that is conspicuous by its absence, but I notice other people have worked on that), and that’s how we end up with big software projects … and patterns I used will come up in those future posts.


<<Previous post       Next Post>>