The Office 365 CLI is great tool for a variety of scenarios. Specifically, I find it super useful for CI/CD and automation. Since there is no official Microsoft Graph Azure DevOps task yet, the Office 365 CLI comes to the rescue when we want to make a call to the Graph from an Azure DevOps pipeline. Most recently, I was using it to deploy a Teams app when I noticed something interesting.
As I was calling the Office CLI from an automated script, I could not use the default deviceCode flow to login as there was no user interaction possible. So the other options were to use the username/password flow or use a custom certificate. I chose the former as it was very convenient and I could store the password securely in a Azure DevOps secret variable.
Now before using the Office CLI on a tenant, we need to consent to the AAD multi-tenant app used internally for authenticating to Office 356 services. This app is called "PnP Office 365 Management Shell" and the consent process is also described in the docs here: https://pnp.github.io/office365-cli/user-guide/connecting-office-365/
If this consent hasn't happened yet, we get the following error:
Error: AADSTS65001: The user or administrator has not consented to use the application with ID '31359c7f-bd7e-475c-86db-fdb8c937548e' named 'PnP Office 365 Management Shell'. Send an interactive authorization request for this user and resource.
If we go ahead with the default consent experience, we are presented with this screen where the CLI AAD app requests a large number of permissions on the tenant:
Now in my case, I did not need all the scopes the CLI was requesting. I only needed AppCatalog.ReadWrite.All to deploy my Teams app.
So that got me thinking, one of the benefits of using the Microsoft identity platform (also called AAD v2) is that apps can request consent to specific scopes:
With the Microsoft identity platform endpoint, you can ignore the static permissions defined in the app registration information in the Azure portal and request permissions incrementally instead, which means asking for a bare minimum set of permissions upfront and growing more over time as the customer uses additional app features. To do so, you can specify the scopes your app needs at any time by including the new scopes in the scope parameter when requesting an access token - without the need to pre-define them in the application registration information.
https://docs.microsoft.com/en-us/azure/active-directory/azuread-dev/azure-ad-endpoint-comparison#incremental-and-dynamic-consent
So armed with this knowledge, I tested whether I could request (and grant) only AppCatalog.ReadWrite.All to the PnP Office 365 Management Shell app.
Turns out it is indeed possible by going to this URL in a browser as an admin:
You will notice that in the client_id param, we are specifying the client id of the PnP Office 365 Management Shell app and in the scope param, we are only requesting the needed scope i.e. AppCatalog.ReadWrite.All
This time, we are presented with a significantly reduced scopes for consent. Notice only the "Read and write to all app catalogs" permission is present along with the couple of default permissions.
After granting the consent and then navigating to Azure AD -> Enterprise Applications -> All applications -> PnP Office 365 Management Shell -> Permissions, you will see that only the requested scope was granted:
This way, you can only request the scopes you need and also include multiple scopes by separating them with a space
If you are like me and would like to automate the consent process as well, that is possible by using the Azure CLI:
00000003-0000-0000-c000-000000000000 is the Application ID of the Microsoft Graph resource in AAD. This will be the same for all tenants.
Hope you found the post useful!
As I was calling the Office CLI from an automated script, I could not use the default deviceCode flow to login as there was no user interaction possible. So the other options were to use the username/password flow or use a custom certificate. I chose the former as it was very convenient and I could store the password securely in a Azure DevOps secret variable.
Now before using the Office CLI on a tenant, we need to consent to the AAD multi-tenant app used internally for authenticating to Office 356 services. This app is called "PnP Office 365 Management Shell" and the consent process is also described in the docs here: https://pnp.github.io/office365-cli/user-guide/connecting-office-365/
If this consent hasn't happened yet, we get the following error:
Error: AADSTS65001: The user or administrator has not consented to use the application with ID '31359c7f-bd7e-475c-86db-fdb8c937548e' named 'PnP Office 365 Management Shell'. Send an interactive authorization request for this user and resource.
If we go ahead with the default consent experience, we are presented with this screen where the CLI AAD app requests a large number of permissions on the tenant:
Now in my case, I did not need all the scopes the CLI was requesting. I only needed AppCatalog.ReadWrite.All to deploy my Teams app.
So that got me thinking, one of the benefits of using the Microsoft identity platform (also called AAD v2) is that apps can request consent to specific scopes:
With the Microsoft identity platform endpoint, you can ignore the static permissions defined in the app registration information in the Azure portal and request permissions incrementally instead, which means asking for a bare minimum set of permissions upfront and growing more over time as the customer uses additional app features. To do so, you can specify the scopes your app needs at any time by including the new scopes in the scope parameter when requesting an access token - without the need to pre-define them in the application registration information.
https://docs.microsoft.com/en-us/azure/active-directory/azuread-dev/azure-ad-endpoint-comparison#incremental-and-dynamic-consent
So armed with this knowledge, I tested whether I could request (and grant) only AppCatalog.ReadWrite.All to the PnP Office 365 Management Shell app.
Turns out it is indeed possible by going to this URL in a browser as an admin:
You will notice that in the client_id param, we are specifying the client id of the PnP Office 365 Management Shell app and in the scope param, we are only requesting the needed scope i.e. AppCatalog.ReadWrite.All
This time, we are presented with a significantly reduced scopes for consent. Notice only the "Read and write to all app catalogs" permission is present along with the couple of default permissions.
After granting the consent and then navigating to Azure AD -> Enterprise Applications -> All applications -> PnP Office 365 Management Shell -> Permissions, you will see that only the requested scope was granted:
This way, you can only request the scopes you need and also include multiple scopes by separating them with a space
Automating the consent:
If you are like me and would like to automate the consent process as well, that is possible by using the Azure CLI:
00000003-0000-0000-c000-000000000000 is the Application ID of the Microsoft Graph resource in AAD. This will be the same for all tenants.
Hope you found the post useful!