<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: SRINIVAS VARUKALA</title>
    <description>The latest articles on Forem by SRINIVAS VARUKALA (@svarukala).</description>
    <link>https://forem.com/svarukala</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F557368%2F91ab0579-ba69-47c5-ac9f-5e68752d418e.jpeg</url>
      <title>Forem: SRINIVAS VARUKALA</title>
      <link>https://forem.com/svarukala</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/svarukala"/>
    <language>en</language>
    <item>
      <title>Manage Azure AD Enterprise Applications permissions using Microsoft Graph PowerShell</title>
      <dc:creator>SRINIVAS VARUKALA</dc:creator>
      <pubDate>Mon, 03 Oct 2022 22:16:08 +0000</pubDate>
      <link>https://forem.com/svarukala/manage-azure-ad-enterprise-applications-permissions-using-microsoft-graph-powershell-222m</link>
      <guid>https://forem.com/svarukala/manage-azure-ad-enterprise-applications-permissions-using-microsoft-graph-powershell-222m</guid>
      <description>&lt;p&gt;Recently I was asked this question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is it supported to revoke permissions (selectively) for an enterprise application? &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This blogpost shows how to manage enterprise applications permissions. Note that Azure Portal UI doesn't allow these actions, so you have to rely on some scripting. Hence this blog post.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is an enterprise application?
&lt;/h2&gt;

&lt;p&gt;I will take a snippet from one of &lt;a href="https://dev.to/svarukala/introducing-the-new-pnp-powershell-based-on-net-core-3-1-and-learn-how-it-s-authentication-works-pn7"&gt;my old posts&lt;/a&gt; to save some time.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fem6p0hlkq9q3fourr8kk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fem6p0hlkq9q3fourr8kk.png" alt="snippet explaining enterprise application in Azure AD"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  To list permissions of an Enterprise Application
&lt;/h3&gt;

&lt;p&gt;To get the list of permissions granted for a given service application you can use &lt;a href="https://gist.github.com/svarukala/503dba34a8f5e805d770be682a66ad33" rel="noopener noreferrer"&gt;script posted to my GitHub Gist&lt;/a&gt;. Here is example output:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1kl0dq94d83m3k0rmyku.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1kl0dq94d83m3k0rmyku.png" alt="Sample out of PS script"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  To revoke existing permissions of an Enterprise Application
&lt;/h3&gt;

&lt;p&gt;Let's take Waldo App as an example. Here is the enterprise application of Waldo app. Note the object id of this service principal.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqeqima6wnl02576oxmgb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqeqima6wnl02576oxmgb.png" alt="Sample output of PS script"&gt;&lt;/a&gt;&lt;br&gt;
Next, view the permissions granted for this app. You can see the permissions in two tabs: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Admin consent and &lt;/li&gt;
&lt;li&gt;User consent.
&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fukpshkcu5cs1jnn1oivg.png" alt="Image showing AAD app permisisons"&gt;
To get the permissions grant for the Waldo app, run below cmdlet with its Object Id.
```powershell
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Get-MgServicePrincipalOauth2PermissionGrant -ServicePrincipalId 090beef1-f5b6-4f35-9326-6d8596e42942&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**ConsentType** column in the output signifies if its the Admin consent (AllPrincipals) or User consent (Principal) permissions.
![Sample output of PS script](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/em5a8cvu1dtf5e9m6kzt.png)

#### Revoke all the permissions
You can remove all the permissions completely using below cmdlet and passing the ID value from the output as the GrantId:
```powershell


Remove-MgOauth2PermissionGrant -OAuth2PermissionGrantId 8e4LCbb1NU-TJm2FluQpQjqF4W-UGkRLtzPexGZThns


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h4&gt;
  
  
  Revoke the permissions selectively
&lt;/h4&gt;

&lt;p&gt;In order to remove the permissions (aka scopes) selectively. Here is an example. Let's say I want to remove "User.Read" and "Directory.Read.All" scopes but retain other scopes. Create an array for the scopes to be removed:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$permissionsToRemove&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@(&lt;/span&gt;&lt;span class="s2"&gt;"User.Read"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Directory.Read.All"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Get the grants using the object id:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$grant&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Get-MgServicePrincipalOauth2PermissionGrant&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ServicePrincipalId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;090beef1-f5b6-4f35-9326-6d8596e42942&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$grant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Scope&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here is the sample output of the scopes that are already granted:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Place.Read.All offline_access User.Read User.ReadBasic.All User.Read.All Directory.Read.All Calendars.ReadWrite openid ChatMessage.Send Chat.ReadBasic Chat.Create&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Create a new array of scopes by removing the unwanted scopes.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$permissionsToRemove&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$permissionsToRemove&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToLower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$newPermissions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$grant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Scope&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$permissionsToRemove&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToLower&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$newScope&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$newPermissions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-join&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;" "&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Finally update the grant with the new scopes using the grant id.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Update-MgOauth2PermissionGrant&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-OAuth2PermissionGrantId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$grant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Scope&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$newScope&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here is a sample run of the above cmdlets:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3kub1i50rpw6uaezgso1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3kub1i50rpw6uaezgso1.png" alt="Sample output of PS script"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After removing the 2 scopes here is the current permissions:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjlx7vb2lor9mfq75udra.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjlx7vb2lor9mfq75udra.png" alt="Image showing changed permissions of the app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  To grant permissions to an enterprise application
&lt;/h3&gt;

&lt;p&gt;Below is a sample script that shows how to add Microsoft Graph permissions to an enterprise application. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# The object id of the enterprise application &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$ObjectId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"b29d7573-2f76-49b6-b660-cc85b34fe516"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;# Add the correct Graph scope to grant (e.g. User.Read)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$graphScope&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sites.Selected"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Connect-MgGraph&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Scope&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;AppRoleAssignment.ReadWrite.All&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Get the Microsoft Graph service principal&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$graph&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Get-MgServicePrincipal&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Filter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AppId eq '00000003-0000-0000-c000-000000000000'"&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Get the graph app role for the scope that we want to grant&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$graphAppRole&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$graph&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AppRoles&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;?&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Value&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-eq&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$graphScope&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Prepare the app role assignment&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$appRoleAssignment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"principalId"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$ObjectId&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"resourceId"&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$graph&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Id&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"appRoleId"&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$graphAppRole&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Id&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="c"&gt;# Grant the app role&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;New-MgServicePrincipalAppRoleAssignment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ServicePrincipalId&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$ObjectID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-BodyParameter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$appRoleAssignment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Format-List&lt;/span&gt;&lt;span class="w"&gt;



&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The above script uses the &lt;strong&gt;AppRoleAssignment.ReadWrite.All&lt;/strong&gt; which is a privileged permission. You can grant application permissions using app grant consent policy which doesn't require the privileged permissions. Here is a &lt;a href="https://winsmarts.com/microsoft-teams-productivity-tips-dd96ea8024ea" rel="noopener noreferrer"&gt;blog post from Sahil Malik&lt;/a&gt; that goes into details of how its done.&lt;/p&gt;

</description>
      <category>powershell</category>
      <category>microsoftgraph</category>
      <category>azure</category>
    </item>
    <item>
      <title>Use Microsoft Graph to Set Granular Permissions to SharePoint Online Sites for Azure AD Application</title>
      <dc:creator>SRINIVAS VARUKALA</dc:creator>
      <pubDate>Tue, 09 Feb 2021 06:57:53 +0000</pubDate>
      <link>https://forem.com/svarukala/use-microsoft-graph-to-set-granular-permissions-to-sharepoint-online-sites-for-azure-ad-application-4l12</link>
      <guid>https://forem.com/svarukala/use-microsoft-graph-to-set-granular-permissions-to-sharepoint-online-sites-for-azure-ad-application-4l12</guid>
      <description>&lt;p&gt;I recently put out a Twitter post about the upcoming capability &lt;br&gt;
that allows you to apply granular permissions to SharePoint Online Sites for Azure AD Applications.  &lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1349747161504882688-873" src="https://platform.twitter.com/embed/Tweet.html?id=1349747161504882688"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1349747161504882688-873');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1349747161504882688&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;This capability is now live and available in the Microsoft Graph API (both v1.0 and Beta). Find the API reference on &lt;a href="https://docs.microsoft.com/en-us/graph/api/resources/permission?view=graph-rest-1.0" rel="noopener noreferrer"&gt;Microsoft Graph documentation&lt;/a&gt;. I have seen a lot of our customers rely on the legacy 'Access Control Services' (ACS) based apps (apps registered using /_layouts/15/appregnew.aspx page) to meet the site level/granular permissions requirements. With this update you can now stop doing that and embrace the modern way using Microsoft Graph.&lt;/p&gt;

&lt;p&gt;In this blog I will show how this is accomplished using PowerShell and Microsoft Graph REST API calls.&lt;/p&gt;

&lt;p&gt;The current implementation feels like a MVP('minimal viable product') of a long term solid plan from Microsoft. Therefore, at the moment there is no UI/UX for admins to manage (add/remove permissions at) the individual site level permissions. It's only possible through Microsoft Graph API. You will need two apps to accomplish this.&lt;/p&gt;

&lt;p&gt;First is the Azure AD application (let's call it 'client-app') registered that needs to be given app-only permissions to select few sites (as against to all sites in the tenant). First step is to navigate to the "API Permissions" for that app. Select "Application permissions" box. Then select "Sites.Selected" permission scope listed under "Sites" category.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzcroszjn5a5ulm1dbsyd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fzcroszjn5a5ulm1dbsyd.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You need another Azure AD application (let's call it 'admin-app') that has 'Sites.FullControl.All' app-only permissions. Assume this is created and managed by your IT Admins. IT admin will use this application (as a helper utility) to call Microsoft Graph and assign 'client-app' the permissions (read or write) for each site that it needs access. In this way the 'client-app' will be able to make queries only to those set of sites. Microsoft graph is updated to support this capability. To add/remove permissions at site collection level, you need to use the /sites/{site-id}/permissions REST API.&lt;br&gt;
Here is an example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com,ab37ac98-a777-4444-b90f-20e2a8728caf,faaf89c8-4444-4639-978f-39e07847b61a/permissions" rel="noopener noreferrer"&gt;https://graph.microsoft.com/v1.0/sites/contoso.sharepoint.com,ab37ac98-a777-4444-b90f-20e2a8728caf,faaf89c8-4444-4639-978f-39e07847b61a/permissions&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here is a sample PowerShell script to enumerate existing site level permissions using Microsoft Graph&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;NOTE: When we enumerate the granular permissions, its not showing the role (read/write) details. It's showing the unique perm id, display name and client app id. This might be a temporary gap in the functionality.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here is a script sample to add granular permissions to a site using Microsoft Graph:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Here is a script to remove all granular permissions or selected granular permissions based on the app id:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;I created a sample script to finally test this out. This sample script enumerates all the lists in a given SPO site. Below image shows the script failing before giving the granular permissions:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2g1o1z51j0rarz88s7ne.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2g1o1z51j0rarz88s7ne.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now I ran the Add-SPOSiteGranularPermission script giving my client-app 'read' permissions to the site. Here is the output from the script:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbrvt9lxnu24ycumpw2mn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbrvt9lxnu24ycumpw2mn.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Below image shows the same script now successful after giving the granular permissions on the site:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fid8qmcg6tuvstguzvo3y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fid8qmcg6tuvstguzvo3y.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the sample script that I used for my sample run:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Some of the limitations I see in the current state:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Thie granular permissions is limited to application permissions (app-only) for an Azure AD app. &lt;/li&gt;
&lt;li&gt;There is no UI/UX to manage the granular permissions&lt;/li&gt;
&lt;li&gt;There is currently no endpoint to list out sites that has granular permissions enabled for a given AAD app&lt;/li&gt;
&lt;li&gt;Site collection level seems to be the most granular it can go.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://twitter.com/Fizzlenik" rel="noopener noreferrer"&gt;Jeremy Kelley (Microsoft PM)&lt;/a&gt; presented on this topic in February's Microsoft Graph community call. He answered a bunch of questions and also hinted that this capability is just beginning and it has a long way to go. You can access the recording &lt;a href="https://developer.microsoft.com/en-us/office/blogs/microsoft-graph-community-call-february-2-2021/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Finally I am working on a full blown script to manage the site level permissions in bulk. You can retrieve, add, remove permissions by supplying a CSV file as input. I will update the link here once that is ready.&lt;/p&gt;

&lt;p&gt;Please leave any comments, questions or feedback.&lt;/p&gt;

</description>
      <category>azure</category>
      <category>sharepoint</category>
      <category>powershell</category>
      <category>m365</category>
    </item>
    <item>
      <title>Learn How Authentication Works in the latest PnP.PowerShell Module</title>
      <dc:creator>SRINIVAS VARUKALA</dc:creator>
      <pubDate>Mon, 11 Jan 2021 02:12:00 +0000</pubDate>
      <link>https://forem.com/svarukala/introducing-the-new-pnp-powershell-based-on-net-core-3-1-and-learn-how-it-s-authentication-works-pn7</link>
      <guid>https://forem.com/svarukala/introducing-the-new-pnp-powershell-based-on-net-core-3-1-and-learn-how-it-s-authentication-works-pn7</guid>
      <description>&lt;p&gt;The transition to the new PnP.PowerShell for Microsoft 365 module should be smooth but if you see errors like 404 unauthorized or consent error, this blog might help you understand the underlying authentication mechanism behind PnP.PowerShell.&lt;/p&gt;

&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Microsoft 365 Patterns and Practices (PnP) team published a new PowerShell module for M365 that runs on .NET Core 3.1/.NET Framework 4.6.1 and PowerShell Core v7.1. This means this module is platform agnostic. This also means the legacy SharePointPnPPowerShellOnline module will eventually be discontinued. The reason for this shift is well explained by &lt;a href="https://github.com/pnp/powershell" rel="noopener noreferrer"&gt;PnP PowerShell team&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;If you are wondering what is the best way to switch from SharePointPnPPowerShellOnline to Pnp.PowerShell, you got covered by awesome folks like &lt;a href="https://www.toddklindt.com/blog/Lists/Posts/Post.aspx?ID=881" rel="noopener noreferrer"&gt;Todd Klindt - also explains reason  for the switch&lt;/a&gt; and &lt;a href="https://www.spjeff.com/2021/01/03/how-to-switch-to-pnp-powershell-ps7-from-sharepointpnppowershellonline-ps5-1/" rel="noopener noreferrer"&gt;Jeff Jones - bonus quick short video&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  PnP PowerShell Authentication with Service Principal
&lt;/h1&gt;

&lt;p&gt;The transition to the new PowerShell module should be smooth but I am seeing some folks (&lt;a href="https://docs.microsoft.com/en-us/answers/questions/218696/pnp-error-when-connect-pnponline.html" rel="noopener noreferrer"&gt;example&lt;/a&gt;) getting stuck with errors like this one:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fu9lm0q44afk45ol79ln1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fu9lm0q44afk45ol79ln1.png" alt="Connect-PnPOnline Error"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Connect-PnPOnline : AADSTS65001: The user or administrator has not consented to use the application with ID&lt;br&gt;
'31359c7f-bd7e-475c-86db-fdb8c937548e' named 'PnP Management Shell'. Send an interactive authorization request for this user and resource&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are two pieces of information in this error that is relevant to this blog.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;31359c7f-bd7e-475c-86db-fdb8c937548e&lt;/strong&gt; &amp;lt;-- The application ID&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PnP Management Shell&lt;/strong&gt; &amp;lt;-- The name of the application &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One of the important changes is the way authentication works in the new module. The final step after installing the PnP.PowerShell module is to run this command:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Register-PnPManagementShellAccess&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This PS command will create an Azure AD Enterprise Application (a service principal) with an ID (31359c7f-bd7e-475c-86db-fdb8c937548e) and the name of this application is "PnP Management Shell". You can navigate to your Azure Portal &amp;gt; Azure Active Directory &amp;gt; Enterprise Applications. You can see the app there.&lt;/p&gt;

&lt;p&gt;Note: The same enterprise application with ID 31359c7f-bd7e-475c-86db-fdb8c937548e is also used by &lt;a href="https://pnp.github.io/cli-microsoft365/" rel="noopener noreferrer"&gt;CLI for Microsoft 365&lt;/a&gt; too. &lt;/p&gt;

&lt;p&gt;This is a service principal with a fixed ID and name. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How does it have a fixed name/id and how is it different from Azure AD App registration?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Consider contoso.onmicrosoft.com tenant is where a AAD App is registered with the ID (31359c7f-bd7e-475c-86db-fdb8c937548e) and name "PnP Management Shell". And this app is configured as a multi-tenant app. Sine this is multi-tenant app, whenever a user uses it the first time from a different tenant (say fabrikam.onmicrosoft.com), a corresponding service principal is created in the 'enterprise applications' section in their tenant with the same ID/Name. It's basically a projection of the app with the same permissions but requested and consented in the users (in this example its fabrikam.onmicrosoft) tenant. The Register-PnPManagementShellAccess command is doing exactly this. &lt;/p&gt;
&lt;h1&gt;
  
  
  How to consent to minimal permissions for the PnP PowerShell Enterprise App
&lt;/h1&gt;

&lt;p&gt;When you run the Register-PnPManagementShellAccess command, it will prompt you to consent to a large number of permissions. See picture below.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcy8332s0at01bizj880r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcy8332s0at01bizj880r.png" alt="PermsConsent"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is a way to take control on the permissions, minimize the permissions for the PnP PowerShell app for reasons like a shared environment or may be to use PnP PowerShell in Azure DevOps CICD pipeline. To do that instead of running the Register-PnPManagementShellAccess command, you need to create the Service Principal on your own and request the minimal permissions needed for your scenario. Here is an &lt;a href="https://gist.github.com/svarukala/1d0c07d1706e378f45f7d715844ba585" rel="noopener noreferrer"&gt;Azure CLI script sample&lt;/a&gt;:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h1&gt;
  
  
  Caveat with previously consented SharePointPnPPowerShellOnline app
&lt;/h1&gt;

&lt;p&gt;If you've previously installed SharePointPnPPowerShellOnline and used it to log into your M365 tenant at some point, then that would have already created the Azure AD Enterprise app with the same ID but a different name ("PnP Office 365 Management Shell"). You can navigate to your Azure Portal &amp;gt; Azure Active Directory &amp;gt; Enterprise Applications to see the service principal.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frpt602dnzhdvy6y5ml21.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Frpt602dnzhdvy6y5ml21.png" alt="legacy pnp app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this case running Register-PnPManagementShellAccess can be optional. The cmdlets work just fine. But not all commands work. Here is an error for Get-PnPSiteTemplate for example:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fb6rnhodavv27tz6g0m8z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fb6rnhodavv27tz6g0m8z.png" alt="Cmdlet error"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To fix the above issue I ran the Register-PnPManagementShellAccess command but that didn't help. I had to delete the existing legacy enterprise app. To delete, you need to open the enterprise application, go to properties and select delete. Then run the Register-PnPManagementShellAccess command.&lt;/p&gt;

&lt;h1&gt;
  
  
  Use your own Azure AD App with PnP PowerShell
&lt;/h1&gt;

&lt;p&gt;The steps to create your Azure AD App to use with PnP PowerShell is &lt;a href="https://pnp.github.io/powershell/articles/authentication.html" rel="noopener noreferrer"&gt;documented here&lt;/a&gt;. &lt;strong&gt;However, based on my research and understanding, you can use it for App-Only access alone&lt;/strong&gt;. To use with user login (i.e. delegated access) you have to use the service principal corresponding to PnP Management Shell.&lt;/p&gt;

&lt;h1&gt;
  
  
  Issue with uninstalling module in PowerShell Core 7.1
&lt;/h1&gt;

&lt;p&gt;When I tried to uninstall a module using Uninstall-Module command, although it runs without any errors, the module actually is not deleted. When I navigate to the module locations, I can see the folder corresponding to the module. To check the PS Module Path you can run the command &lt;strong&gt;$env:PSModulePath&lt;/strong&gt;.&lt;br&gt;
I had to manually delete the module folder to get rid of the module. This might be an isolated issue but calling it out here if anyone hits that issue.&lt;/p&gt;

&lt;p&gt;Happy scripting!&lt;/p&gt;

&lt;p&gt;Hope that helps!&lt;/p&gt;

</description>
      <category>microsoft365</category>
      <category>pnp</category>
      <category>powershell</category>
      <category>azure</category>
    </item>
  </channel>
</rss>
