Monday, 18 January 2021
Building a Microsoft Teams bot for AppSource: Posting an Adaptive Card carousel as a welcome message
Tuesday, 5 January 2021
Creating multi-tenant (SaaS) apps in Microsoft 365 has been possible for a while now. Azure AD multi tenant apps allow us to host our custom applications in an Azure AD/M365 "home" tenant while enabling the apps to also have access to resources hosted in other tenants. To know more about multi-tenant apps, head over to the Microsoft docs: https://docs.microsoft.com/en-in/azure/active-directory/develop/single-and-multi-tenant-apps
Hosting applications in a home tenant as SaaS has a lot of advantages particularly for ISVs when it comes to product based applications. Users are able to consume the apps directly by signing into them instead of the conventional way of an admin having to deploy the product to the customer tenant first. It makes life easy for the admins as well as they don't have to go through complex deployment scripts and instructions. Moreover, after the application is deployed, new features and bug fixes can be rolled out to the application "on the fly" as opposed to releasing feature packs and hotfixes which again have to be installed manually.
So in this post, we are going to have a look at using the Microsoft Graph API in such apps configured to be multi tenant.
(Multi tenant apps also allow users with personal Microsoft accounts to sign into them but that is a topic for another day! Also, in this post we will only focus on the application permissions i.e. granting permissions to applications without a user context)
Configure an app to be a multi-tenant in the home tenant's Azure AD
1) When creating a multitenant app registration, make sure that the "Accounts in any organizational directory" is selected. Also, we need to add a redirect url as this will be the url the admin will be redirected to after successfully granting consent to our application. Ideally, this would be the landing page of your application but in the screenshot I am just using the AAD home as an example:
2) Assign required permissions. In this case, we are going to demo the code to get all the Microsoft 365 Groups on the tenant and also the root SharePoint Online site, so selecting the relevant permissions here:
3) Create a client secret and record it along with the client id. We will need this later in our code.
Granting consent to a multi tenant app in other "consumer" tenant
Next, let's have a look at how the multi tenant app hosted in it's home tenant can be granted permission to access resources in other tenants.
What we will have to do is to construct a url for admin consent which would be unique to our application. An Azure AD admin of the other tenant will need to navigate to the url and then consent to granting the permissions to our app on the tenant. The Azure AD url will have the following structure:In the link above, replace the client id with the client id of your multi tenant Azure AD app. Also, notice that we are using the /.default static scope which means that all permissions configured in the app will be requested for consent.
Once the consent is granted, the multitenant app will have permissions to access the resources on the other tenant. This can be checked by going to:
This confirms that the multi tenant app has permissions on this tenant. Also this process can be repeated on any number of Azure AD/M365 tenants.
Use the Microsoft Graph API to get Microsoft 365 data from the consumer tenant
With everything setup and also the admin consent granted, let's have a look at the Microsoft Graph code to get data from the consumer tenant.
In this code, I am using the .NET SDK for Microsoft Graph found on nuget here:
And the new preview version of Microsoft.Graph.Auth found here:
And finally here is the code to get all the Microsoft 365 Groups and the SharePoint root site url of the consumer tenant. For the sake of simplicity, I am using a .NET Core console application:And we are able to get the data from the consumer tenant back:
Friday, 4 September 2020
With SPFx 1.11, one of the things possible now is that SharePoint Framework web parts can be exposed as Microsoft Teams messaging extensions. So what are messaging extensions exactly? According to the Teams docs:
"Messaging extensions allow users to interact with your web service through buttons and forms in the Microsoft Teams client. They can search, or initiate actions, in an external system from the compose message area, the command box, or directly from a message. You can then send the results of that interaction back to the Microsoft Teams client, typically in the form of a richly formatted card."
As a Microsoft 365 Developer, messaging extensions are a great way to invoke custom code right in the Teams client. This opens up the possibility of users interacting with your application right in the context of their conversations without having to leave Teams.
The SPFx docs give a nice overview of how to setup web parts so that they are exposed as compose extensions. This enables the custom SPFx webpart to be invoked from the "Compose new message" box in Teams: https://docs.microsoft.com/en-us/sharepoint/dev/spfx/build-for-teams-expose-webparts-teams#expose-web-part-as-microsoft-teams-messaging-extension
In this post, we are going to be talking about SPFx webparts being hosted in task modules which show up in "message actions" i.e. invoking custom code on messages which are already posted in Teams. This could be either in channels or in personal or group chats.
Now behind the scenes, when a message action is invoked on a message, we want to get the message context passed to our SharePoint Framework web part. By message context, I mean properties like teams id and channel id in which the action was invoked. If the message action was invoked in a personal chat or a group chat, then we need to know the chat id instead. And finally, we need the data about the message itself e.g. message id, message body, who posted the message etc. so that we can then send the information to our application right from the SPFx webpart.
Now if we were using the Bot Framework to power our message action (and task module), then getting these properties is straightforward as every time the message action is invoked, the Bot Framework sends this information to our messaging endpoint: https://docs.microsoft.com/en-us/microsoftteams/platform/messaging-extensions/how-to/action-commands/create-task-module?tabs=json#example-fetchtask-request
When using SharePoint Framework however, we have to take a longer route. When the message action would be invoked on a Teams message: Although we get the context information like team id, channel id and chat id, all we will get about the message itself is just the id. No other data about the message like the body, user etc will be available. Getting all these other details would be up to us. Let's see how we do that:
Teams app manifest
SPFx and Microsoft Graph:
Although we won't have the message data directly provided to us in SPFx, we would have all the context information necessary to fetch the data. As part of the microsoftTeams context object, we will have the teamId, channelId, chatId and the parentMessage. We can then use these details along with the Microsoft Graph to get the message details:
Before we go through the code, make sure that the SPFx solution has the Chat.Read permissions on the Microsoft Graph configured in the package-solution.json file. This will allow us to read the Teams messages on behalf of the currently logged in user
And finally, here is the SPFx code to get the message details on which the message action was invoked:
Monday, 20 July 2020
User gets a notification of the mention:
Other users are able to contact the user directly from the mention in the card:
Wednesday, 24 June 2020
Monday, 22 June 2020
- The API only works with delegated access for now i.e. with a user context. Application permissions are not supported.
- When searching SharePoint Online content, we are not able to specify fields to return in the result. Only a default set of fields can be returned.
- There is no custom sorting available as of now when it comes to SharePoint content. The content is sorted by default by relevance.
Tuesday, 26 May 2020
This post generated some good discussion on twitter with Yannick Plenevaux regarding the ideal cases when to use this approach as opposed to other approaches of state management like using the useState or useReducer hooks. Have a look here: https://twitter.com/yp_code/status/1265244244077416448