{"id":8059,"date":"2026-10-01T10:52:55","date_gmt":"2026-10-01T08:52:55","guid":{"rendered":"https:\/\/blog.redbaronofazure.com\/?p=8059"},"modified":"2026-10-01T11:26:47","modified_gmt":"2026-10-01T09:26:47","slug":"entra-administrative-units-instead-of-group-readwrite-all","status":"publish","type":"post","link":"https:\/\/blog.redbaronofazure.com\/?p=8059","title":{"rendered":"Entra Administrative Units instead of Group.ReadWrite.All"},"content":{"rendered":"\n<p>What do you do if you have an app that needs to create and manage its own groups? You can grant the app the Graph API permission Group.ReadWrite.All, but that would mean it can manage <em>any<\/em> group in the tenant and that is a security concern as it gives the app too much power. <\/p>\n\n\n\n<p>Instead, you can grant the app&#8217;s service principal (SP) the <em><strong>Groups Administrator<\/strong><\/em> role scoped to an <strong><em>Administrative Unit<\/em><\/strong> (AU). This way the app can create groups and add\/remove users to it but only within the AU. The SP can not manage groups that are outside the AU. <\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized is-style-rounded\"><img loading=\"lazy\" src=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Entra-AU-SP-groups-1024x555.jpg\" alt=\"\" class=\"wp-image-8060\" width=\"512\" height=\"278\" srcset=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Entra-AU-SP-groups-1024x555.jpg 1024w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Entra-AU-SP-groups-300x163.jpg 300w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Entra-AU-SP-groups-768x416.jpg 768w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Entra-AU-SP-groups-1536x832.jpg 1536w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Entra-AU-SP-groups.jpg 1703w\" sizes=\"(max-width: 512px) 100vw, 512px\" \/><\/figure>\n\n\n\n<h2>Creating the Administrative Unit<\/h2>\n\n\n\n<p>In the Entra portal, goto <em>Roles &amp; admins<\/em>, then <em>Admin units<\/em> and click <em>+ Add<\/em> and a create a new AU like this.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><img loading=\"lazy\" src=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-081647-1024x196.png\" alt=\"\" class=\"wp-image-8061\" width=\"768\" height=\"147\" srcset=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-081647-1024x196.png 1024w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-081647-300x57.png 300w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-081647-768x147.png 768w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-081647.png 1170w\" sizes=\"(max-width: 768px) 100vw, 768px\" \/><\/figure>\n\n\n\n<p>Click into the new AU and select <em>Roles and administrators<\/em>, find role <em>Groups Administrator<\/em> and click on it.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><img loading=\"lazy\" src=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-081807.png\" alt=\"\" class=\"wp-image-8062\" width=\"657\" height=\"311\" srcset=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-081807.png 876w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-081807-300x142.png 300w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-081807-768x363.png 768w\" sizes=\"(max-width: 657px) 100vw, 657px\" \/><\/figure>\n\n\n\n<p> Next, click <em>+ Add assignments<\/em> and find your app that needs to manage its own groups<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><img loading=\"lazy\" src=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-082135-1024x298.png\" alt=\"\" class=\"wp-image-8063\" width=\"768\" height=\"224\" srcset=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-082135-1024x298.png 1024w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-082135-300x87.png 300w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-082135-768x223.png 768w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-082135.png 1046w\" sizes=\"(max-width: 768px) 100vw, 768px\" \/><\/figure>\n\n\n\n<p>Now, your app&#8217;s service principal have the scoped permission to create and manage groups within the AU.<\/p>\n\n\n\n<h2>Create and manage groups via the Service Principals scoped permission<\/h2>\n\n\n\n<p>First, authenticate the SP using client credentials<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"powershell\" class=\"language-powershell\"> $auth = Invoke-RestMethod -Method Post -Uri \"https:\/\/login.microsoftonline.com\/$tenantId\/oauth2\/v2.0\/token\" `\n    -ContentType \"application\/x-www-form-urlencoded\" `\n    -Body @{ grant_type=\"client_credentials\"; client_id=$ClientId; client_secret=$ClientSecret; scope=\"https:\/\/graph.microsoft.com\/.default\" }\n\n$authHeader =@{ 'Content-Type'='application\/json'; 'Authorization'='Bearer ' + $auth.access_token }    \n<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<h3>Prove the SP doesn&#8217;t have too much power<\/h3>\n\n\n\n<p>Next, to prove that the SP doesn&#8217;t have too much power, try to create the group outside of the AU. This attempt fails with <em>Authorization_RequestDenied<\/em> and <em>Insufficient privileges<\/em> as it should.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"powershell\" class=\"language-powershell\">$groupName = \"App-GroupOne\"\n$bodyG = @\"\n{\n  \"displayName\": \"$groupName\",\n  \"mailNickname\": \"$groupName\",\n  \"mailEnabled\": false,\n  \"securityEnabled\": true,\n  \"description\": \"SP with AU scoped permission creating a new group via Graph API\",\n  \"@odata.type\": \"#microsoft.graph.group\",\n  \"owners@odata.bind\": [\"https:\/\/graph.microsoft.com\/v1.0\/directoryObjects\/$spObjectId\"]\n}\n\"@\n<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"1024\" height=\"195\" src=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-095435-1024x195.png\" alt=\"\" class=\"wp-image-8064\" srcset=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-095435-1024x195.png 1024w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-095435-300x57.png 300w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-095435-768x147.png 768w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-095435.png 1273w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h3>Create the group within the AU<\/h3>\n\n\n\n<p>To create a group using the SP&#8217;s scoped permission to the AU, we need to call the AU&#8217;s Graph endpoint <em>https:\/\/graph.microsoft.com\/v1.0\/<strong>directory\/administrativeUnits<\/strong>\/$auId\/members<\/em> and not the standard <em>\/groups<\/em> endpoint. We pass the same request body.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"powershell\" class=\"language-powershell\">$group = Invoke-RestMethod -Headers $authHeader -Method \"POST\" -Uri \"https:\/\/graph.microsoft.com\/v1.0\/directory\/administrativeUnits\/$auId\/members\" -Body $bodyG<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<h3>Manage the new group<\/h3>\n\n\n\n<p>Once the new group is created, the SP can use the standard \/groups endpoint to add\/remove members. In the below example we add the SP as a member, but this could be a user&#8217;s UPN.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"powershell\" class=\"language-powershell\">$bodyGM = @\"\n{\"@odata.id\":\"https:\/\/graph.microsoft.com\/v1.0\/directoryObjects\/$spObjectId\"}\n\"@\nInvoke-RestMethod -Headers $authHeader -Method \"POST\" -Uri \"https:\/\/graph.microsoft.com\/v1.0\/groups\/$($group.id)\/members\/`$ref\" -Body $bodyGM\n<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<h3>Get details of the group<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"powershell\" class=\"language-powershell\">Invoke-RestMethod -Headers $authHeader -Method \"GET\" -Uri \"https:\/\/graph.microsoft.com\/v1.0\/groups\/$($group.id)?`$select=id,displayName,description<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"1024\" height=\"112\" src=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-100638-1024x112.png\" alt=\"\" class=\"wp-image-8065\" srcset=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-100638-1024x112.png 1024w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-100638-300x33.png 300w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-100638-768x84.png 768w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-100638.png 1408w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h2>Where is the group visible?<\/h2>\n\n\n\n<p>If you create a group within an AU, where is it visible in the Entra portal? It shows up in two places. First, it is visible amongst the other groups and there is no obvious difference.  <\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><img loading=\"lazy\" src=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-110357.png\" alt=\"\" class=\"wp-image-8075\" width=\"306\" height=\"163\" srcset=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-110357.png 408w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-110357-300x160.png 300w\" sizes=\"(max-width: 306px) 100vw, 306px\" \/><\/figure>\n\n\n\n<p>If you view the group details and select the Administrative units menu option, you see that it exists within an AU.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><img loading=\"lazy\" src=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-110542.png\" alt=\"\" class=\"wp-image-8076\" width=\"444\" height=\"276\" srcset=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-110542.png 592w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-110542-300x186.png 300w\" sizes=\"(max-width: 444px) 100vw, 444px\" \/><\/figure>\n\n\n\n<p>Reversely, if you view the AU itself, you will see the group listed as an AU member.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><img loading=\"lazy\" src=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-110742.png\" alt=\"\" class=\"wp-image-8077\" width=\"425\" height=\"236\" srcset=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-110742.png 566w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-110742-300x166.png 300w\" sizes=\"(max-width: 425px) 100vw, 425px\" \/><\/figure>\n\n\n\n<h2>The bad news<\/h2>\n\n\n\n<p>If you think this looks good, there is one downside. The app will need the <em>Directory.Read.All<\/em> application permission as documented by Microsoft (link <a rel=\"noreferrer noopener\" href=\"https:\/\/learn.microsoft.com\/en-us\/entra\/identity\/role-based-access-control\/manage-roles-portal?tabs=admin-center#service-principals-and-guest-users\" target=\"_blank\">here<\/a>). So you trade not granting the app <em>Group.ReadWrite.All<\/em> for instead granting it <em>Directory.Read.All<\/em>. If you don&#8217;t grant the app <em>Directory.Read.All<\/em>, the creation of group and managing it will fail as the SP hasn&#8217;t the ability to verify member objects.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" width=\"766\" height=\"127\" src=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-103801.png\" alt=\"\" class=\"wp-image-8067\" srcset=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-103801.png 766w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-103801-300x50.png 300w\" sizes=\"(max-width: 766px) 100vw, 766px\" \/><\/figure>\n\n\n\n<p>There is another option here and that is to create a custom role that works like Directory.Reader.All and scope that role to the SP within the AU. This More about that in a later post.<\/p>\n\n\n\n<h2>The group owner solution<\/h2>\n\n\n\n<p>If you are not too keen on granting the SP <em>Directory.Read.All<\/em> permission, there is another possible solution. If the groups can be pre-created, ie not dynamically created within the app, you can make the SP an owner of the group(s). Group owners can manage group membership without having Group.ReadWrite.All.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><img loading=\"lazy\" src=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-104740.png\" alt=\"\" class=\"wp-image-8068\" width=\"592\" height=\"223\" srcset=\"https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-104740.png 789w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-104740-300x113.png 300w, https:\/\/blog.redbaronofazure.com\/wp-content\/uploads\/2026\/10\/Screenshot-2026-10-01-104740-768x289.png 768w\" sizes=\"(max-width: 592px) 100vw, 592px\" \/><\/figure>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"powershell\" class=\"language-powershell\">$group2 = Invoke-RestMethod -Headers $authHeader -Method \"GET\" -Uri \"https:\/\/graph.microsoft.com\/v1.0\/groups?`$filter=DisplayName eq 'App-GroupTwo'\"\n\nInvoke-RestMethod -Headers $authHeader -Method \"POST\" -Uri \"https:\/\/graph.microsoft.com\/v1.0\/groups\/$($group2.value[0].id)\/members\/`$ref\" -Body $bodyGM<\/code><\/pre>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>What do you do if you have an app that needs to create and manage its own groups? You can grant the app the Graph API permission Group.ReadWrite.All, but that would mean it can manage any group in the tenant and that is a security concern as it gives the app too much power. Instead, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[464,453],"tags":[466,465],"_links":{"self":[{"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=\/wp\/v2\/posts\/8059"}],"collection":[{"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=8059"}],"version-history":[{"count":10,"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=\/wp\/v2\/posts\/8059\/revisions"}],"predecessor-version":[{"id":8085,"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=\/wp\/v2\/posts\/8059\/revisions\/8085"}],"wp:attachment":[{"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=8059"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=8059"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.redbaronofazure.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=8059"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}