Continuing my Graph/ PowerShell journey…
Now I want to remove users from an Azure AD group. And turns out there’s no cmdlet in the Microsoft.Graph module for that. Bummer, eh!
No worries, one of the cooler cmdlets in that module is Invoke-MgGraphRequest
which as you probably guessed makes calls to the Graph API. Why not just use Invoke-RestMethod
you might ask? Well, you could… but this one takes care of the authentication etc. You are already connected via Connect-MgGraph
so why bother now getting access tokens etc. via Invoke-RestMethod
and then passing that in the header to another Invoke-RestMethod
and so on…? It’s a small convenience, sure…
Anyways, removing members is discussed in this API document. You have to send a DELETE
request to /groups/{id}/members/{id}/$ref
. No big deal, first I made a loop along these lines:
1 2 3 4 5 6 |
foreach ($Member in $RemoveMembers) { Write-Output "Removing $Member from the group" $UserObj = Get-MgUser -UserId "$Member" Invoke-MgGraphRequest -Method Delete -Uri "https://graph.microsoft.com/v1.0/groups/$($GroupObj.Id)/members/$($UserObj.Id)" } |
I have the group Id in $GroupObj.Id
from an earlier part of the code, and I get the user Id from Get-MgUser
. I am intentionally skipping the $ref
part as I can’t put that in PowerShell because it will get interepreted as a variable. I was expecting some error due to this but I got an access denied and that threw me off:
1 |
{"error":{"code":"Authorization_RequestDenied","message":"Insufficient privileges to complete the operation.","innerError":{"date":"2021-07-09T10:14:09","request-id":"1acd7644-97c7-46ce-92d9-7d550de6d3ec","client-request-id":"1acd7644-97c7-46ce-92d9-7d550de6d3ec"}}} |
At this point I should have just tried tacking on a $ref
and seeing if that works, but I stupidly didn’t. I thought maybe I had missed some permissions to the application so went down that track.
To ensure there’s no bug in the cmdlet I decided to try another way. I could have used Invoke-RestMethod
instead I went with PostMan. I’ve been slowly building up a collection of queries in PostMan as it’s just easier there.
For instance, I have the following collections for various things I am trying out (SharePoint Online, Group Sync). In these collections I have the queries I want to try out. You can see for instance I have 4 queries in the Group Sync collection.
The nice thing with PostMan is that I can define variables, and put those into separate environments. Thus for instance, notice my query above to get an access token doesn’t have the tenant Id or app Id anywhere… all these are stored as variables. I can have an environment for my test tenant (for instance) where the tenantId variable points to that; so when I want to send a query to that tenant I merely switch environments. Or I could have separate environments for each of my app Ids – for instance my SharePoint work and Groups work have separate app registrations; so I have separate environments with the app Ids and secrets for these.
When I run the above query and get an access token, I can right click that and store it as a variable. Then in subsequent queries I can send that variable as the authorization token. So cool! :)
Anyways, if I were to run the DELETE
request using PostMan, with the same app registration, it fails with the same error.
But at least with PostMan I can tack on $reg
(as it is not a variable in PostMan) and when I do that the query succeeds.
Nice! So the $ref
was the issue.
Thus I modified the loop above and now it works:
1 2 3 4 5 6 7 8 |
foreach ($Member in $RemoveMembers) { Write-Output "Removing $Member from the group" # need this for the graph query below; it needs a $ref tacked on at the end. By setting the variable to '$ref' it does not get interpreted as a variable. $ref='$ref' $UserObj = Get-MgUser -UserId "$Member" Invoke-MgGraphRequest -Method Delete -Uri "https://graph.microsoft.com/v1.0/groups/$($GroupObj.Id)/members/$($UserObj.Id)/$ref" } |