Tag Archives: azcli 2.0

Graph, the endpoint to end all endpoints

‘Sup PSHomies,

My first introduction to Graph was at the Microsoft Technical Summit here in the Netherlands at the RAI march 23,24 this year. It was actually the last session of the day. The intended audience was for developers… I thought to myself, why not? I might pick up a thing or two… It blew my mind!

Then at the PSConfEU at Hannover, Jeffrey Snover’s Keynote ‘State of Union’ really made me reassess my career, so much so that I left my previous employment of 23 years for a much smaller company, Methos! Methos is owned by Jeff Wouters aka “The scripting dutchman”. It’s like Jeffrey Snover said in his keynote: “With transformative change, you need to change the way you think about things.” Graph is one of those transformative thing… Here’s how I got started.

So Jeff came up with an idea:

Jeff : Say Urv, I got something I’d like you to look into…

Me: Sure, what’s up?

Jeff: Would it be possible to get a hierarchy of the entire company from Azure Active Directory?

Me: I don’t see why not?

Jeff: Once you have the data the next step is to visualize it!

Me: Like an organizational chart?

Jeff: Uhuh! (Grinning) Here’s a tip, look into Graph…

I should have known… 🙂 Now at Methos we don’t have an on-premise Active Directory so Azure AD it is. Now as part of my “transformative change” I asked for a Macbook pro. I thought: “Hey if you’re gonna go through the change (wait that doesn’t sound right, you know what I mean) might as well go big or go home! Jeff didn’t disagree, he only grinned and let out a subtle evil laugh… I got a sneaky suspicion that I’m in for a surprise… My other Transformative change: PowerShell Core!

I installed Azure CLI 2.0 as a first attempt. To get Azure AD users:

az ad user list

This gave me a JSON list with few attributes. Maybe I need to add a property list or something. Let do a quick help, Hmmm… Ok… Next approach.

Let’s install AADPreview Core. Say, Get-AzureADUser doesn’t have a -Properties attribute… Huh? So that’s why Jeff was grinning about… Well played Jeff…

So here’s the deal, I’m using PowerShell core, not all modules are applicable. The good thing is that I could login to Graph Explorer.  Graph explorer gives you the opportunity to explore the endpoint and browse around.

“Hey look at that, there’s a get manager query! Maybe there’s a way to get all the data and filter out what I need (Like I always do with PSCustomObject)”

I saw one of the examples use a $select option so I decided to give it a try.

$select=displayName, GivenName, surName, department, officeLocation, jobTitle, userPrincipalName, id, manager

I decided to give it a try. Ok that narrowed things down a bit still no manager in sight! Turns out manager is a special kind of property, a Navigation Property. You can extract it from the MetaData

$graphMetadata = [XML](Invoke-WebRequest -Uri 'https://graph.microsoft.com/v1.0/$metadata').Content
$userMetaData = $graphMetadata.Edmx.DataServices.Schema.EntityType.Where{ $_.Name -eq 'User' }
$userMetaData

Graph-NavigationProperty

Ah, that’s why manager is omitted, it isn’t part of the collection! My next challenge was to retrieve the JSON payload. That’s where I ran into the accessToken dilemma.

 

AccessToken

From the online resources I inspected, authentication is the key to access (see what I did there?). I found Mark Kraus’s module PSMSGraph quite useful to follow his trail of thoughts. It didn’t work for me because I’m using PowerShell core. Now it’s time to read the document! When all else fails RTFM!

I ran into some snags with my application registration, so my next step was to reach out to the Graph team! They are on twitter just no tweets… yet! That got me in contact with Dmitry Pimenov, the PM of Microsoft Graph! I explained him my dilemma and he showed me an easy way to get the accesstoken for proto-type purposes. Finally! I can get some data to play with!

#region Get Access token from Grap Explore Session
$accessToken = #Get the AccessTokenquick & Dirty using developers tool trick by Dan Silver https://twitter.com/dansilver82/status/872520061843415040
#endregion
#region Format request Header
$requestHeader = @{
"Authorization" = "Bearer " + $accessToken #For now get AccessToken from Developers tools console
"Content-Type" = "application/json"
}
#endregion
#region Get Users Tenant properties to query with Graph
$userProperties = @(
'displayName'
'GivenName'
'surName'
'department'
'officeLocation'
'jobTitle'
'userPrincipalName'
'id'
)
$iwrUsersParams = @{
Uri = "https://graph.microsoft.com/v1.0/users?`$select=$($userProperties -join ',')"
Method = 'Get'
Headers = $requestHeader
}
$iwrUsersResults = Invoke-WebRequest @iwrUsersParams
$iwrUsersObjects = $iwrUsersResults.Content | ConvertFrom-Json
$iwrUsersObjects.value
#endregion
#region Get Manager per Object ID
$iwrUsersObjects.value |
ForEach-Object {
try {
$obj = $_
$iwrManagerParams = @{
Uri = 'https://graph.microsoft.com/v1.0/users/{0}/manager' -f $obj.ID
Method = 'Get'
Headers = $requestHeader
}
$userManager = Invoke-WebRequest @iwrManagerParams
$iwrManagerObject = $userManager.Content | ConvertFrom-Json
[PSCustomObject]@{
ObjectID = $obj.Id
UserPrincipalName = $obj.userPrincipalName
DisplayName = $obj.DisplayName
GivenName = $obj.givenName
SurName = $obj.surname
JobTitle = $obj.jobTitle
Department = $obj.department
Manager = $iwrManagerObject.displayName
}
}
Catch {
[PSCustomObject]@{
ObjectID = $obj.Id
UserPrincipalName = $obj.userPrincipalName
DisplayName = $obj.DisplayName
GivenName = $obj.givenName
SurName = $obj.surname
JobTitle = $obj.jobTitle
Department = $obj.department
Manager = $null
}
}
}
#endregion
#region Get Access token from Grap Explore Session
$accessToken = $null
#endregion
#region Format request Header
$requestHeader = @{
"Authorization" = "Bearer " + $Accesstoken #For now get AccessToken from Developers tools console
"Content-Type" = "application/json"
}
#endregion
$iwrUsersParams = @{
Uri = "https://graph.microsoft.com/beta/users?`$expand=Manager"
Method = 'Get'
Headers = $requestHeader
}
$iwmUserResults = Invoke-RestMethod @iwrUsersParams
$iwmUserResults.value |
ForEach-Object {
[PSCustomObject]@{
DisplayName = $_.displayname
GivenName = $_.givenname
SurName = $_.surname
JobTitle = $_.jobtitle
Department = $_.department
Manager = $_.manager.displayname
}
}

My first attempt was straight forward: get the user, get the manger for each user and then create a PSCustomObject. This isn’t optimal ofcourse. So I sent Dmitry a DM asking what is the optimal way of retrieving manager in an ODATA query. That’s when he explained that the Beta version supports the $expand option in the ODATA query.

Quick side step: Microsoft Graph supports ODATA queries, not all queries are supported yet, it’s a work in progress. ODATA is definitely something to look into… Here’s a link to get you started… Transformative change… 😉

Alright, so the /Beta returned  quite a bit more than I bargained for, but at least I didn’t have to do multiple queries. That’s when I decided to trying something new… JMESPath Query!

JMESPath

While using az I noticed a link to JMESpath query, what’s that?  JMESPath is a query language for JSON. With it, you can extract and transform elements from a JSON document… Sweet! Kinda like what we do with PSCustomObject, just on JSON. Here’s where having a Mac wasn’t a liability after all… I even found a vscode extension for jmespath queries! Many ways to Rome… Transformative change… (Jeffrey Snover was right, you need to rethink the way you think about things)

Using this query on the JSON document gave me the following results:

value[].{DisplayName: displayName, GivenName: givenName, SurName: surname, Department: department, JobTitle: jobTitle, Manager:manager.displayName, Location:officeLocation}

JSOn-ManagerResults

Alright! Prototyping mission accomplished! What I thought was straight forward had quite a few curves, but I wouldn’t have it any other way!

Take away:

Learn to love Graph! Be open to learning new and interesting way to accomplish tasks in a new way. Transformative change  won’t be easy, but it’ll be worth it!!!

Hope it’s worth something to you…

Ttyl,

Urv