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
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.