
videocam_off
This livestream is currently offline
Check back later when the stream goes live
Assign Graph API App Role to Azure Managed Identity
Feb 5, 2024
Learn how to assign Graph API Application Roles to a managed identity using PowerShell!
Chapters:
00:00 Introduction
00:16 Enable Function App Managed Identity
02:03 Assign Graph API App Role using PowerShell
09:43 Azure Function Code Example
23:10 Summary
Show More Show Less View Video Transcript
0:00
What's up, everyone? Today we're going to continue talking about the Microsoft Graph API
0:04
and how you can use it with managed identities. In this case, we're going to look at an example of an Azure function
0:11
My name is Jeff Brown. This is Jeff Brown Tech. Let's go ahead and get started. Up here on my screen right now, I have a function app named jbt-graph-api-demo
0:21
I'm not going to dive into exactly what a function app is. Just know it is a serverless option out in Azure
0:27
that allows you to run small pieces of code. It could be .NET, Python, Node.js. In this case, we're going to have a PowerShell example
0:35
But what I'm mainly interested in is we can enable a managed identity with this Azure resource
0:41
and then give that managed identity access to the Graph API to perform specific things
0:47
and then our application code can perform those actions. Out here in the function app, let's scroll down under Settings and go to Identity
0:56
And here we have an option of enabling a system-assigned managed identity or a user-assigned
1:01
I'm going to stick with the system-assigned. That way, it is associated with the resource
1:05
If I ever delete this resource, the managed identity associated with it will also be removed
1:11
So it's very simple to enable. We just select this to on and go ahead and save our settings
1:16
just letting you know it's going to register an enterprise application, and then we can grant it permissions. Let's go ahead and say yes
1:26
Okay, and that was successful. We now have a managed identity associated with our Azure function
1:32
You can see we have an object or principal ID here, and then we can also give it Azure role assignments
1:38
What I'm going to do is go ahead and copy this to my clipboard, this object ID
1:43
We'll switch over to another tab. Here I am out in intra-ID
1:47
and I'm just going to go ahead and search my tenant for that object ID
1:53
We can see we have an enterprise application now with the exact same name as the Azure function
1:59
We'll go ahead and select it. Then here we have some more information. We have the application ID. Again, we have the object ID
2:07
What I'm primarily interested in right now is we need to assign this enterprise application
2:12
or the managed identity for our Azure function. We need to give it some permissions out to the Graph API
2:18
Under security, if we click on permissions, we can see right now it does not have any permissions associated with it
2:25
Now, if you've ever worked with application registrations, you know we can go in there
2:29
and easily select different permissions that we need to give to our application registration
2:35
However, that's a little harder to do with enterprise applications. There's nowhere to do it inside the portal here. We have to do it out in Microsoft PowerShell
2:43
There's probably commands to do it inside of the Azure CLI also, but I'm just more familiar with PowerShell
2:49
so that's what I'm going to be using. Let's switch over to Visual Studio Code, where I have an example of using PowerShell
2:55
to grant this managed identity permissions inside of the Graph API. Here we are out in Visual Studio Code, and I have some PowerShell commands here
3:03
I've already ran the first one where I've connected out to Microsoft Graph
3:07
and I've put a specific scope here of directory read-write-all because I'm going to be making changes inside of my directory out in Azure
3:15
Now, the first thing we need to do is get the managed identity object that we just created
3:21
We just need to get its information and save it to a variable here
3:24
You can do that two different ways. The first example uses the object ID along with service principal ID parameter to find the managed identity
3:33
or you can do a filter based on the display name. I still have copied in my clipboard here the object ID, so let's go ahead and put that in here
3:43
I'll press F8, and that ran successfully. Let's just go ahead and double-check what it's pulled back here, make sure we've got the right one
3:53
We can see the display name there is dbt-graph-api-demo. That's the name of our managed identity, which reflects the name of our Azure function
4:01
Next, we need to get another service principal, and this one is going to be the Microsoft Graph Enterprise app object
4:08
Now, you can see I already have an app ID here that equals this specific GWT
4:13
This GWT here is going to be the same inside of every Azure tenant
4:17
This is not specific to my tenant, so this is hard-coded, and you don't need to worry about looking it up or anything
4:24
This will be the same for your tenant. I'll go ahead and select line 10 here and run it, and that was successful
4:32
Let's also go take a look at what we have in here. You can see the display name is Microsoft Graph
4:38
and that represents Microsoft Graph inside of our tenant. Now, we have our managed identity for our Azure function
4:46
and then we have the Microsoft Graph Enterprise app object. Let's go ahead and scroll down a little bit more
4:51
What we need to do next is, what permissions do we want to assign to the managed identity
4:58
In this case, we're going to be updating user profile information on user accounts
5:03
We have user read-write-all. We want to be able to read and write all the user profiles inside of our tenant
5:10
Let's go ahead and run this and save that to a variable
5:14
Let me try that again. Now, what we need to do is find an application role that has that same permission
5:21
so we can assign that application role to our managed identity. We're going to do that by looking at the app roles that are associated with our graph service principle
5:32
We're going to find one where the value of that app role equals the permission we want
5:36
and our allowed member types we were looking for an application type permission
5:42
The other type we would look at is delegated, but delegated acts on behalf of a signed-in user
5:47
In this case, we want a permission that works without a signed-in user inside of an application
5:51
That's why we're only looking at allowed member types containing application. To give you a better idea of what we're looking for
5:57
let's actually take a look at the app roles that are associated with the Microsoft Graph Enterprise object
6:03
I'm just going to highlight Graph SPN and look at the app roles property
6:08
You can see it gives a description of what these different app roles do
6:12
We can manage workforce integration. We can read-write virtual appointments. A lot of these bottom ones here are for Microsoft Teams
6:20
It allows the app to read user profiles without a signed-in user. That almost sounds pretty close to what we're looking for
6:25
Let's actually take a look at just one of these. I'm going to pipe it out to format listing
6:31
We're just going to take a look at the first one in the list
6:35
Again, we have allowed member types application. That's the type of one we're going to be searching for later in our filter
6:40
Look at the description. It allows the app to read access reviews, reviewers, decisions, settings in the organization without a signed-in user
6:46
That's great. But look down here at the value, accessreview.read.all. That's why we have our permission set to user read-write-all
6:55
That's the permission we're looking for. We're going to be filtering on that value and finding an application role that has that same permission
7:03
Hopefully you can see here we're going to search all of our application roles for graph API
7:07
We're going to find one where the value equals the type of permission that we want
7:11
Then we just want to make sure it's application type. Let's go ahead and highlight all of these lines here
7:21
Go ahead and execute it. Looks like we got a response. Let me go ahead and clear out the screen
7:26
Let's look at app role. I'm going to pipe that to format listing so we can just read it a little easier
7:33
Here we go. Let's look at the description. Allows the app to read and update user profiles without a signed-in user
7:39
Sounds exactly like what we're looking for. That matches our value, user.readwrite.all
7:45
This role here is exactly what we want to assign to our managed identity
7:50
Our function app can read and update user profiles. Now that we have our app role, we're going to create a hash table here that we're going to pass to our next command
8:01
The principal ID is our managed identity, the ID of it. Our resource ID is our graph service principal, the ID of it
8:09
Then we have our app role ID that we just found. We're going to reference its ID
8:15
Let me go ahead and execute this so we have that hash table saved
8:19
Then we're going to splat it onto our command here on line 26
8:24
We have to reference the service principal ID again and the managed identity
8:28
and then pass it our hash table in the body parameter. Go ahead and execute this
8:36
That looks like that was successful. Actually, let's switch back to our list here
8:43
Here is our managed identity. We just granted it some permissions inside a graph API
8:48
We'll go ahead and refresh this. There it is right there. We have Microsoft Graph
8:53
It can read and write user profiles. We just granted that out inside of PowerShell
9:01
If you ever need to remove permissions in here, you can go into Review Permissions
9:06
It gives you a couple of options. I want to control access to this application. Maybe it's suspicious, I want to investigate, or it's malicious and I'm compromised
9:14
This may not actually represent what you need, but there is commands here. If you choose one of these, it will show you how to remove all the permissions
9:22
associated with this enterprise app. Then you can get more granular and maybe just remove one or two
9:27
that you don't need inside the application anymore. Again, all this is done inside of Azure PowerShell
9:32
Hopefully, they incorporate something here inside the portal to where you can manage it that way
9:38
It would be so much easier, just like you see in application registrations. Let's go ahead and switch back to our Function app
9:44
Our Function app has a managed identity. That managed identity has permissions out to the Graph API
9:50
to read and update user profiles. Let's go ahead and start jumping into the code in order to do that
9:58
First thing I want to show you, though, is let's go to App Files
10:02
Let's take a look at some of the application files associated with this Function app
10:06
Just like when you use PowerShell on your local system, this Function app also has a profile that loads
10:13
every time the Function app is restarted. Let's change this dropdown here and go into Profile
10:20
What I want to show you here first is on line 14. Basically, it's checking to see if this Function app
10:27
in the environment variables has a managed identity secret or MSI, managed service instance or managed service identity
10:35
That's, I think, an old acronym for it. What it's going to do is automatically connect out to your Azure tenant
10:42
using the managed identity. That's why you just, on line 16, it just has dash identity
10:47
It's saying, hey, connect out to Azure using this Function app's managed identity
10:52
If you're using PowerShell inside your Function app and you're not going to be using a managed identity
10:56
or connecting out to Azure PowerShell, you can remove line 14 through 17, just comment it out or delete it
11:02
But in our case, we do need that, and I'll show you why inside of our code here in just a second
11:08
Just showing here that inside the profile, it's automatically going to connect out to our Azure tenant
11:14
using managed identity. Let's go back to Overview. I already have a Function created inside of this Function app
11:23
called Update User Profile. Let's go ahead and select it. Then we're going to go to Code and Test
11:32
Let's go take a look at the code inside of this Function. I've changed this a little bit from the default Function
11:38
that's created when you create a PowerShell 1. First, on line 7, I'm just outputting to the log stream
11:44
that the Function is processing a request, just basically showing that it's started
11:49
We have a couple of parameters coming in here on line 4, request trigger metadata
11:54
The request is incoming information that's passed when the Function is called
11:59
We'll see that in a minute here when we go to test this. The first thing on line 9 is we're going to be passing
12:05
into this Function the user principal name in the request body. We're going to save that to the variable UPN
12:13
Which specific profile property we want to update. Think about on user profiles, we have things like job title
12:19
or department, maybe office location, things like that. Then on line 11, we have a new value
12:25
That's the new value that we want to update for that property. So say department, we're going to update that
12:30
to something else. Then on line 13, we're just outputting to the log stream
12:35
exactly what it's going to do. We're going to update this user principal name or UPN
12:40
this profile property, and what new value we're going to set it to
12:46
Line 16 is where the automatic connection out to our Azure tenant inside the profile comes into play
12:52
We need a token to the Graph API in order to call the Graph API
12:59
We need that token that says what permissions we have. You may have seen this in other videos I did
13:05
if you've been following my series on Graph API. We can get that token a lot easier than what I did
13:12
in previous examples by using the get az-access-token command. We can only run that if we're automatically connected
13:20
out to our Azure tenant, which is what I just showed you earlier inside the profile script
13:26
It's connecting automatically to our Azure tenant using the managed identity. We put a resource URL in here to get out to
13:33
graph.microsoft.com, and I'm only pulling back the token that we get back when we call this
13:40
I don't need the rest of the information that's in there, so that's why it's in parentheses .token
13:45
and we've saved that to a variable here. Next we need to build our request header
13:51
This contains our access token and what content types we're expecting back or maybe even passing to it
13:58
where we're using JSON. Then we also need to build our request body
14:03
We're going to say what profile property we're going to update and its new value
14:09
We're passing that to the Graph API for a specific user. Then we're going to convert that hash table there
14:16
into JSON when we pass it. Finally, after all of that, we've got our token
14:22
we've got our request headers. With our access token, we're building the body
14:26
of our request, what we're trying to do when we call the Graph API
14:30
Now here on line 32, we're actually going to use invoke rest method
14:35
In this case, we're doing a patch because we're updating an existing object
14:39
We've got our URI. Now, noticing our URI contains our UPN variable
14:44
so that's the user we're going to be updating. We're passing in our headers and our body
14:49
The headers have our authorization token and the body says what we're wanting to update
14:54
for this user. Now, I could introduce some more error checking here
14:59
If this did fail to update, the function app would fail and it would show the result of that
15:06
However, in this case, we're just going to say successfully updated this user's profile property
15:12
to this new value. We'll save it to body. And down here on line 37, so on function apps
15:20
what we can do is it can take things in and then it can also push responses
15:26
So it's pushing back out the output binding of saying, you know, this worked, HTTP status okay
15:32
and the body message. All right, so that's what our function app is doing
15:38
We're getting a token and then we're calling the Graph API to update someone's user profile
15:45
So real quick, I'm going to change this. We're not using App Insights on this
15:49
Let's switch this over to file system logs and let's run a test of this
15:58
So you can see our HTTP method is going to be post. The key is just authorization to call this Azure function
16:05
We have our query and our headers. We don't need to add anything here. But here is our body
16:09
So this is what we're passing to this function app. And let me scroll back up here
16:14
This lines up with everything we see on lines 9, 10, and 11
16:18
We're passing at the UPN of the user account we want to update. The profile property we're going to update this user's department
16:24
and what the new value is is going to be IT. So it's actually, I always like to double check
16:30
and make sure that this is working as expected. So let's actually switch back out to here
16:36
We'll go back to intra ID. Let's search for my name. We've got my user account here
16:45
Let's go under properties. And we can see my current department is accounting
16:51
So that's not correct. We need to change that. So we have our body
16:56
Our new value is going to be IT. Now keep an eye right down here on our output window, the file stream
17:03
So let's go ahead and run this. Okay, so this tab switched over from input to output
17:11
And we can see the response we got back was successfully updated user
17:16
their department to IT. This matches up with line 34. And then that output actually happens in line 37 through 40
17:26
So that's the same message there. I want to correlate those two
17:31
So let's go ahead and close this. And let's take a look at our output log here
17:36
Do you see some red? So let's see what's going on there
17:40
So we can see right here this is where it's connecting out to our Azure tenant. That's happening in our profile
17:45
We see update user profiles processing a request. So we see that lines up with line 7 here
17:53
So this is our output telling us we're going to update Jeff, the department to IT
17:58
That lines up with line 13. Then we get an unauthorized. Let's see what's going on here
18:05
Okay, after spending a little bit of time troubleshooting this and trying to figure out what was going on
18:10
and why I was getting unauthorized, jumping out to PowerShell, trying to graph explore everything else
18:16
finally realized what was going on. So here on line 16 I'm saving the result of my token request to $token
18:25
But in my header here I was referencing a variable that does not have a token in it
18:31
So that's why it was coming back unauthorized. Perils of copy and paste
18:35
I probably had this as an example from a blog post or something like that
18:39
and just wasn't paying attention. So kudos to you if you saw that and were yelling at the computer screen
18:46
as to why it wasn't working before I spent the last half hour or so trying to figure it out
18:51
So let's go ahead and save our updated code here. I haven't tested this
18:56
This should work now that we're actually referencing the token that we're getting back
19:01
So let's go back to test and run. I'm going to clear this out right here
19:07
Let's go back to input. Let's change. I was testing with another user
19:13
So let's actually go back to my original user here. I changed the value to information technology
19:19
I was just trying some different things here, experimenting. So let's go ahead and run this
19:23
And hopefully now that we're using the correct variable reference this will actually work
19:31
Again, this is what I was mentioning earlier. Even though it was failing, our success message, our output here
19:36
showed it was successful. So that's why you probably want to put some try-catches in there
19:40
do some actual error handling in order to show if it actually failed
19:44
instead of just blasting out that it was successful. So let's close this
19:49
Hey, there's no red here. The update user file is processing a request showing we're updating
19:55
the property or the department to information technology. It succeeded. And no red text this time
20:02
So let's go back out to my user account. I always show the department was accounting
20:07
We will go ahead and refresh here. Back to properties. And looky there, information technology
20:16
It helps when you reference the correct things and what you're trying to do
20:20
in order to make it work. So good thing it was something small like that
20:25
I was going back out to the managed identity. I granted it more permissions because it kept coming back unauthorized
20:31
So I was just trying to figure out what was going on. All right, so now that that is working
20:38
hopefully this shows you how we use the managed identity for an Azure resource
20:45
We granted it Graph API permissions. We got an access token here in order to use that to call out to Graph API
20:52
all that fun stuff. You might be saying, there are native commands in PowerShell
20:57
to do these same things. That's absolutely true. This is just me using PowerShell to provide examples of how to do this
21:06
The same principles applies if you're using JavaScript, Java, Python. You will want to get a token and make a URL request like this
21:16
The last thing I want to cover, if you try to run this yourself
21:21
you may run into an issue coming back saying the getAzAccessToken is not a valid command
21:28
So inside of functions, if you're using commands from other PowerShell modules
21:33
you have to include those modules inside your Azure function. And I'll show you real quick how to add those
21:41
So if we go back to the function app, let's go into app files. We were here earlier looking at our profile
21:48
There is another file here called Requirements. And inside of this one is where you can specify the names
21:56
of PowerShell modules that are out in the PowerShell gallery and what version you want to include inside your function app
22:04
I don't want to download the entire AZ PowerShell module and all the different modules and everything that are included in it
22:11
So that specific command to get a token is inside the az.accounts module
22:18
The latest version is 2.something right now, so I just said 2.star to always give me the latest version
22:23
So that's included in here. I do have a blog post on how to add these into your function apps
22:30
I'll include that down in the comments. Once you include that in here, you have to go back to Overview
22:37
and either do a restart or a stop and start of your function app
22:41
because it will run the requirements script at that time and pull down the modules needed that you included in there
22:49
So just if you're following along and you run into that saying
22:53
that that command to get a token was not valid, it's included in another module and you have to include that module
22:59
inside your function app so it can download and use it. Pretty much like Azure Automation accounts, if you've ever used those
23:06
you can include modules in there. So that does it for this video. A little bit longer
23:12
Apologies for the small snafu there of the wrong reference, but I had to go back and fix that
23:18
But hopefully this helped you out in what you're doing and can give you some more tools on how to use function apps
23:24
managed identities, Graph API, all that fun stuff. Thank you for watching, and we'll see you next time
23:36
Microsoft Mechanics www.microsoft.com www.microsoft.com www.microsoft.com www.microsoft.com www.microsoft.com www.microsoft.com www.microsoft.com www.microsoft.com www.microsoft.com www.microsoft.com www.microsoft.com www.microsoft.com www.microsoft.com
#Computers & Electronics
#Programming

