Monday, 18 January 2021

Building a Microsoft Teams bot for AppSource: Posting an Adaptive Card carousel as a welcome message

In November 2020, I was happy to release my side project "Snooze Bot" as a free app on the Microsoft Teams store: https://appsource.microsoft.com/en-us/product/office/WA200002297 

I had been working on it for a few weeks. The fact that all of us were under lockdown gave me some extra time in the evenings and weekends to focus on learning the Microsoft Teams platform and create an app on it which addressed a gap which I noticed in my day to day use. 

We all get a lot of Teams messages daily and need a way to manage them or come back to them at a later time. Snooze bot helps us do exactly that. It lets us Snooze message which we want to deal with later. When a message is snoozed, we get an option to select the duration after which Snooze Bot should remind us about the message. When the time arrives, the bot will send you personal message in teams reminding about the snoozed message.


If you haven't checked out Snooze Bot yet, feel free to install it and give it a try. I am happy to hear any feedback and potential improvements. 

One of my goals when creating the app was to learn about the Microsoft Teams developer platform and also blog about the interesting things I came across. So in this series of posts, let's outline some Microsoft Teams development concepts which I found really useful. The first one being posting an Adaptive Card carousel as a welcome message when the bot is added by the user.

It's always recommended as a good practice to send a welcome message when the user adds the bot. According to the Microsoft docs: 

In personal contexts, welcome messages set your bot's tone. The message includes a greeting, what the bot can do, and some suggestions for how to interact (for example, “Try asking me about …”). If possible, these suggestions should return stored responses without having to sign in.

Also, sending the welcome message one of the requirements before the app is accepted in AppSource by the validation team.

So we can send a simple chat message from the bot to the user as a welcome message. So why go for an Adaptive Card carousel? This is because adding too much information in a single message can get overwhelming for the user and they might be tempted to just skip it. Also if your bot has a lot of functionality you need a way to efficiently present that information to the user. This is where carousels created by Adaptive Cards some into play:  


So let's have a look at the code which helps us send the welcome message in Snooze Bot

The Adaptive Card json:


First, we need to define the Adaptive cards which will show up in the welcome message. I am storing mine as json files in my solution. The cards contain helpful text and also links to images which show the functionality of Snooze Bot

The Bot:


Next, the actual bot code itself. Since I am using .NET Core for this bot we will need the Adaptive cards nuget package:


And here is the code where we do the following things:

1) Capture the OnMembersAddedAsync event from the Bot Framework
2) Get the Adaptive cards from the json files 
3) Insert the adaptive cards into a Bot Framework carousel and send it to the user

And that's it. Whenever a user will download and install the app, the welcome message will be sent to them introducing your bot and it's funtionality. Hope you found the post useful!

Tuesday, 5 January 2021

Microsoft 365 multi-tenant apps: Working with application permissions in Microsoft Graph

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. 

When the admin navigates to this url, they will see the consent prompt:


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:

Azure Active Directory > Enterprise Applications > All applications and searching for our app there.


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:

Microsoft.Graph

And the new preview version of Microsoft.Graph.Auth found here:

Microsoft.Graph.Auth

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:


Thanks for reading! Hope this helps.

Friday, 4 September 2020

Microsoft Teams messaging extensions using SPFx: Getting the message data with Microsoft Graph

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."

https://docs.microsoft.com/en-us/microsoftteams/platform/messaging-extensions/what-are-messaging-extensions

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.

(click to zoom)

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


First, to get the SPFx powered message action working, we need to configure it in the Teams manifest.

Notice that the fetchTask property is set to false. This makes the task module defined in the manifest to be displayed. (If fetchTask is set to true, the Teams will go to the Bot messaging endpoint to get the task module dynamically)

Also notice that the url for a SharePoint Framework Task module is slightly different compared to a Teams tab

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:

Hope you found the post useful! Here is the SPFx webpart code on GitHub: https://github.com/vman/spfx-teams-message-action

Note:

This approach currently works with Teams messages in personal chats, group chats and top level messages in Teams channels. There seems to be a gap right now where this approach does not work for replies posted to top level teams messages. I have opened up a GitHub issue about this and will post a follow up soon. https://github.com/OfficeDev/microsoft-teams-library-js/issues/398 

Monday, 20 July 2020

Microsoft Teams Bot Framework: Mention a user in an Adaptive Card

Microsoft Teams announced support for Adaptive Cards 1.2 recently. With that, a nifty feature to allow mentioning users in Adaptive Cards posted in Teams was also introduced. This allows us the ability to send a notification to the user and can draw their attention towards the card.

User gets a notification of the mention:


(click to zoom)

Other users are able to contact the user directly from the mention in the card:


(click to zoom)


In the docs, there is a great example of the JSON we need to send to Teams to post the card containing the mention. So in this post, lets see how we can do this when using the Bot Framework .NET Core SDK:

For this code to work, we will need the following Nuget packages:




Quick note: I noticed that the user mentioned is only notified when the Adaptive card is first created. If you update the same adaptive card later and mention the same user again, they are not notified. This is probably for the best as the Adaptive card might be updated several times and if you got a notification every time, it might be really annoying to the user.

Hope you found this post useful!

Wednesday, 24 June 2020

Using .NET Standard CSOM and MSAL.NET for App-Only auth in SharePoint Online

So after long last, the .NET Standard version of SharePoint Online CSOM was released yesterday! The official announcement can be found here: https://developer.microsoft.com/en-us/microsoft-365/blogs/net-standard-version-of-sharepoint-online-csom-apis/

One of the key differences compared to the .NET Framework CSOM was that the authentication is completely independent of CSOM library now. Previously, there were native classes like SharePointOnlineCredentials which were used for auth, but they have been removed now.

Since .NET Standard CSOM now uses OAuth for authentication, it's up to the developer to get an access token and pass it along with the call to SharePoint Online. The CSOM library does not care how the access token was fetched. 

So in this post, let's have a look at getting an Application authentication (aka App-Only) access token using MSAL.NET and use it with the new .NET Standard CSOM to get data from SharePoint Online.

When making app-only calls to SharePoint Online, we can either use an Azure AD app registration (with the Client Certificate) or we can use SharePoint App-Only authentication created via the AppRegNew.aspx and AppInv.aspx pages. (There are other workarounds available but that would be out of scope for this post) I go into more details about this in my previous post: Working with Application Permissions (App-Only Auth) in SharePoint Online and the Microsoft Graph

The recommended approach is to go with an Azure AD App Registration and the Client Certificate approach so that is what we will be using. To do that, first we will need to create an App Registration in the Azure AD portal and configure it with the Certificate, SPO API permissions etc. Here is a detailed walk-through on this in the Microsoft docs: https://docs.microsoft.com/en-us/sharepoint/dev/solution-guidance/security-apponly-azuread 

Let's have a look at a few important bits of my Azure AD app registration:

The certificate:


The consented SharePoint permissions:



Once the Azure AD App Registration is configured correctly, we can start looking at the code. 

We will be using a .NET Core 3.1 Console app project for this along with the following nuget packages:



And finally, here is the code which uses MSAL.NET to get the access token and attaches it to the .NET Standard CSOM requests going to SharePoint:


Note: Make sure that you are using the right way to access the certificate as per your scenario. Here, for demo purposes, I have installed the certificate to my local machine and I am accessing it from there. In production scenarios, it's recommended to store the certificate in Azure Key Vault. More details here

And when I run the code, I am able to get the title of my SharePoint site back:
 

Hope you found this post useful! I am very glad .NET CSOM Standard is finally available and we are able to use it .NET Core projects going forward. This is going to make things so much easier!

Monday, 22 June 2020

Using the Microsoft Search API (preview) to query SharePoint content

The new Microsoft Search API (preview) has been available in the Graph beta endpoint for a while now. If you haven't had a chance to look at the API yet, the docs explain it quite nicely:

"The Microsoft Search API provides one unified search endpoint that you can use to query data in the Microsoft cloud - messages and events in Outlook mailboxes, and files on OneDrive and SharePoint - that Microsoft Search already indexes."

And it's also currently planned that Microsoft Teams search will also be transitioned to use Microsoft Search in the future: https://twitter.com/williambaer/status/1273644094904872960



Considering everything, it looks like Microsoft Search will play an important role in Microsoft 365 solutions going forward. Given this, I decided to check out the Graph API .NET SDK late last year to try and search SharePoint files. But quickly stumbled on a roadblock which did not allow the API to work with the SDK: https://github.com/microsoftgraph/msgraph-beta-sdk-dotnet/issues/43 

Fortunately, the issue was fixed recently and we are able to use the .NET Graph SDK for testing. 

The code:

Let's see how can we search SharePoint Online content using the new Microsoft Search API:

For this code to work, you will need the Microsoft.Graph.Beta nuget package:

We are going to use the KQL sytax with the Microsoft Graph Search API to query SharePoint Modern pages in a tenant. Once the query completes, we will display the page name and page url in the console:

Considerations:

Although it works great, there are a few considerations currently:

  • 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.

Hope you found this post useful and helps you get started with the Microsoft Search API. To read up more on the Microsoft Search API in Graph, have a look here:

Tuesday, 26 May 2020

Create a custom React hook to mimic class component's setState behaviour

I have been playing around with react hooks recently, and slowly wrapping my head around how they work. Hooks are a great way to create reusable functionality, but there are some instances where I think class components fared better. Let's talk about one such scenario, and also how to get around it when using hooks.


When working with react state in a class component, if we wanted to update a specific property on the state object, all we had to do was call the setState method and pass in an object containing only the updated property and value. The resultant state would be the previous state merged with the new property value.

It works a bit differently with hooks. When using the default useState hook, the new object passed in entirely replaces the previous state. So all the properties in the previous state are overwritten. So every time you want to update a state object, the onus is on the developer to keep track of the previous state and update the new state based on that. Here is how this behaviour manifests in code:

As you can see in the code the value of the FirstName property is lost when we update the LastName property. 

There is a way to set the state based on previous state by using the functional update pattern: https://reactjs.org/docs/hooks-reference.html#functional-updates

But that means that every time we used to just use this.setState in class components, we now have to use

setState(prevState => {
  return {...prevState, ...updatedValues};
});

This would be additional overhead for us devs which I wanted to check if we could avoid.

Fortunately with the reusable nature of hooks, we can simply create a custom hook which mimics the class components setState behaviour and merges the new state with the previous state. And we can use this custom hook any time we want to merge the state.

Here is how the previous code would look when using our custom hook:


The only change in code is that we have replaced react's useState with our custom useCustomState. This gives us back the ability to merge state objects instead of replacing them. (I have also changed the variable names from state and setState to customState and setCustomState respectively to make it explicit that we are using the custom hook. But you can just as well keep using the state and setState names)

So how does our custom hook work? It's built as a wrapper on top of the useState hook's functional update pattern. Any time a state object is passed to the setCustomState function, it internally uses the functional update pattern and merges the new state with the previous state. Let's have a look at the code:


This automates the overhead of using the functional pattern. It is no longer the the developers responsibility, and instead, is done by the custom hook.

But wait, what if there is a scenario where a new state depends on the previous state? Our custom hook does not yet expose a way for the developer to update the state based on the previous state. Let's fix that. We can update our hook to add a method which accepts a function. This function will receive the previous state from react.  

And consuming the new hook can be done by passing in a function updater: 

Hope that helps! For the sake of completeness here is the full code for our custom hook:

Full solution including the custom hook and the consuming code can be found here: https://github.com/vman/spfx-react-hooks-customstate

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