Wednesday, September 30, 2020

Quick Tip: Avoid limitations when reading SharePoint list items using Microsoft Graph

Microsoft introduced in latest version of CSOM a new tenant property Tenant.DisableCustomAppAuthentication which can be used to disable the capability to grant permissions to app authentication in SharePoint. Unfortunately, new Office 365 tenants have this property set to true by default. I'm not sure if that is a glitch, a bug or intentional. Does Microsoft want us to use Graph instead of SharePoint REST in the future? Anyway, from now on I want to use Graph whenever possible.

I’m developing a Microsoft Teams bot currently which uses SharePoint REST to retrieve data from SharePoint lists, create webhooks etc. Since accessing SharePoint lists and sites is already possible from Microsoft Graph we decided internally to replace the existing SharePoint REST implementation with Graph APIs. Unfortunately, I faced a limitation while retrieving data from a SharePoint list which I want to share with you in this blog post.

The goal

I tried to retrieve a SharePoint list item based on its ID. Along with that, I also tried to read additional content such as the item’s fields. This is what the request looks like:

https://graph.microsoft.com/v1.0/sites/1b3c1c82-8d58-4348-b13f-02898a1c662f/
lists/221395e9-cfca-4ac6-ba33-8b678fbc7836/items/407
?$select=Id&$expand=Fields($select=Title,_ModerationStatus,_ModerationComments)

The limitation

I was retrieving data from a SharePoint list which has content approval settings turned on. During my tests, if I tried to read those items that have an approval status other than Approved (e.g.: Rejected or Pending), the API threw the following error:

System.Exception: {
   "error":{
      "code":"itemNotFound",
      "message":"The specified list was not found",
      "innerError":{
         "date":"2020-09-30T14:33:44",
         "request-id":"56d110a8-4b07-4f37-857d-0dff276742bd",
         "client-request-id":"56d110a8-4b07-4f37-857d-0dff276742bd"
      }
   }
}

This error message is definitely misleading since I was able to retrieve other items from the same list.

The workaround

By turning the content approval settings off, I was able to retrieve all items from that list without limitations. However, that wasn’t the solution I was looking for ๐Ÿ˜€ I tried the same request using the Microsoft Graph Explorer and succeeded. I ended up with the conclusion that the API permission Sites.Manage.All was missing from my AAD app configurations. According to this Microsoft Graph documentationSites.Read.All should be enough to read SharePoint list items. Anyway, after adding the missing permission to my AAD app, I was finally able to read all list items from that list.

I hope it helps!

Saturday, August 1, 2020

Simplifying Proactive Messaging in Microsoft Teams with Microsoft Graph's new API permissions

Proactive Messaging enables you to send notifications in Microsoft Teams without user interaction. Basically, a user or team doesn’t need to interact with the bot to receive its notifications. However, this functionality requires the bot to be installed as a personal app or in a team that the user is a member of. Otherwise, the bot doesn’t have the needed information to proactively contact the user.

There has already been guidance explaining how to combine Microsoft Teams with Microsoft Graph to fulfill the installation requirement. Previously, this process had to be executed manually or programmatically using Graph permissions such as User.ReadWrite.All or Directory.ReadWrite.All that allow any application to be installed. In case of users installing the app manually to receive bot notifications, the proactive concept was no longer complete.

The table turns and the process becomes simpler! Microsoft Graph API introduces two new application permissions TeamsAppInstallation.ReadWriteSelfForUser.All and TeamsAppInstallation.ReadWriteSelfForTeam.All. Those new scoped API permissions allow a Teams app to read, install, upgrade, and uninstall ITSELF for any user/team, without a signed-in user ๐Ÿ’› How cool is that!!! This is how it looks in Azure AD:

To use these new permissions, add a webApplicationInfo key to your team’s app manifest. It must include your Azure AD app ID and the resource URL for the app. Ensure your Azure AD app has the application permission that will fulfill your requirement. E. g.: TeamsAppInstallation.ReadWriteSelfForUser.All if you want to manage notifications for users. This is how webApplicationInfo looks:

"webApplicationInfo": {
  "id":"692107eb-eca3-45da-b81b-e336c762d5e4",
  "resource":"api://692107eb-eca3-45da-b81b-e336c762d5e4"
}

Microsoft has also provided detailed information about the usage of this new approach which is still in public preview. Check out this documentation for more information.

I gave this new approach a try with a bot I've been developing at my company and must say that it works very well. Thanks for the Microsoft Graph Teams team for providing us with these two new application permissions ๐Ÿ’› 

Monday, July 13, 2020

New approach to manage membership of Microsoft Teams using Microsoft Graph Teams API

I am so proud of the Microsoft Graph Teams team ๐Ÿ’› It took a while, but the MS Graph Teams team released a new approach to manage Teams membership directly from a dedicated Graph teams API ๐Ÿ˜ฏ

Background

Currently, changes made to the team’s membership had to be made to the Microsoft 365 group backing a team via the Microsoft Graph API “groups/members” / “groups/owners”. Those membership changes don’t apply immediately to the team and must be synced to the Teams service in order for newly added users to access the team. Unfortunately, the sync mechanism can take up to 24 hours according Microsoft. From my experience, it can take even longer.

The new approach

New teams Graph APIs have been introduced in order to remove the dependency on the group Graph API. Basically, it is no longer required to use group Graph APIs to manage the Teams membership since the new beta/teams/{teamId}/members API is available and it supports CRUD operations!!!

Those APIs support both, delegated and applications permissions ๐Ÿฅณ

List all team members and owners

You can use this API to retrieve all members and owners of a TEAM within the same call ๐Ÿ˜ฏ That would be perfect if this API would support pagination!!! ATM, this is a known limitation! However, in many cases it might improve performance since you only need one call to retrieve owners and members of a team instead of different calls to first retrieve the group and then retrieve the owners/members. As far as I have tested, this API returns up to 100 users within one request. This is how the request to retrieve all owners and members looks:

GET https://graph.microsoft.com/beta/teams/{teamsId}/members

Check out the corresponding Microsoft Graph documentation for more information! (List all team members and owners)

Add a user

It will add users immediately (always happend during my tests) to the team or return a corresponding error code if the adding process fails. This is a great improvement since the sync delay issue between group and team will no longer be an issue as described previously. This is what the request to add new users to the team looks like:

POST https://graph.microsoft.com/beta/teams/{teamsId}/members

{
   "@odata.type": "#microsoft.graph.aadUserConversationMember",  
   "roles": [""],
   "user@odata.bind": "https://graph.microsoft.com/beta/users/666a3fb6-d598..."  
}

Check out the corresponding Microsoft Graph documentation for more information! (Add members or owners to a team)

Update a user

This API updates the user’s role in a team and is only supported on private channels. For instance, you can use this API to promote an user from member to owner:

PATCH https://graph.microsoft.com/beta/teams/{id}/channels/{id}/members/{id}

{
   "@odata.type": "#microsoft.graph.aadUserConversationMember",
   "roles": ["owner"]
}

Check out the corresponding Microsoft Graph documentation for more information! (Update team members or owners)

Delete a user

This API removes a user from a team ๐Ÿ˜Š

DELETE https://graph.microsoft.com/beta/teams/{teamsId}/members

Check out the corresponding Microsoft Graph documentation for more information! (Delete team members or owners)

Summary

I have already had different discussions with partners and customers about the problem caused by the sync delay. This API is a big improvement in the Graph teams API. Just love it!!!

Note that this API is still in beta and seems to be still under development. During my tests, I was facing different errors like getting Bad Request when trying to delete a user from a team. However, this will be the way to go and I am very happy that Microsoft is introducing this new approach. I am really looking forward to seeing the improvements that the Microsoft Graph Teams team will be adding to those new APIs.

Sunday, June 21, 2020

Extra! Extra! Name property no longer supported by Graph Tabs API

It is difficult to create reliable solutions for Microsoft 365 if changes are applied suddenly to Graph APIs without Microsoft informing customers beforehand or if information is not available in the Microsoft documentation. I am not talking about beta APIs. Although, there are Graph APIs that have been in beta for years (e.g.: POST beta/teams) and could be considered in some cases production APIs. I am talking about v1.0/production APIs ๐Ÿ™

Last Monday, June 15th, a colleague from the testing team reported that parts of a solution stopped working properly:

Reason: After a debugging session, I observed that the Graph API to retrieve properties of a specific tab, stopped returning the name property which was a property used in different parts of my solution.

Fix: Fixing this bug wasn't challenging since the same value stored in the name property was also available in the displayName property. Basically, the solution was to replaced the name property with the displayName property.

Problem: After some research, I figured out that the name property was deprecated since 2018. Unfortunately, this information was removed from the Microsoft Graph tab documentation already in 2018. Moreover, at least until end of 2019, the Graph documentation about retrieving tab properties included the name property as part of the response example. According to the Graph tab documentation, this is what the tab request and response used to be:
GET https://graph.microsoft.com/v1.0/teams/{id}/channels/{id}/tabs/{id}
{
  "id": "tabId",
  "name": "My Contoso Tab",
  "teamsAppId": "06805b9e-77e3-4b93-ac81-525eb87513b8",
  "configuration": {
    "entityId": "2DCA2E6C7A10415CAF6B8AB6661B3154",
    "contentUrl": "https://www.contoso.com/Orders/.../tabView",
    "websiteUrl": "https://www.contoso.com/Orders/...",
"removeUrl": "https://www.contoso.com/Orders/.../uninstallTab"
}, "webUrl": "https://teams.microsoft.com/..."
}
I mean, there was no indication that the name property wasn't secure to use!!!

I have already seen one discussion on GitHub about this change! Basically, there are people trying to understand and fix this issue. I hope this blog post brings some clarity to this problem and helps you fixing this bug.

Monday, June 15, 2020

Awarded the Microsoft MVP in Office Development - 2020/2021

I was on a team meeting today, when suddenly the doorbell rang and I started asking myself: Is this the package I have been waiting for or are kids playing ding dong ditch out there again? I decided to open the door and boy... Such a positive surprise! It was the ♫ Mister Postman  (FedEx dude) with a package for me. The content of the package you might ask? Nothing other than the MVP award!

The Most Valuable Professional (MVP) award is a landmark in my career. This is definitely time to say thank you to people who have helped me, motivated me and inspired me over the last few years:

- My Wife: Thank you for being part of my life and for supporting me. This is our achievement!!!
- Yannick Plenevaux: I see you as my mentor. Thank you very much for the support you gave me!
- Chris O'Brien: Although your sessions are always very well attended and you get a lot of questions afterwards, you still find time for every question. Thank you for being such an example and inspiring me to start blogging!
- Vesa Juvonen and Waldek Mastykarz: You guys have been doing an awesome community work. The Microsoft 365 community is very strong because of your commitment. I am very proud to be part of it! #SharingIsCaring
- Jeremy Thake and Paul Schaeflein: Microsoft 365 Developer Podcast is another example of community contribution. Thank you for teaching me and motivating me with your podcast!
- Valo: At the time of this written, we have 11 MVP's working for Valo. The power behind this company is tremendous because we work as a team and everyone is equal. I am very proud to be a member of Valo. Special thanks to Henrik Blรฅfield and my colleagues from Valo Teamwork team. It is a pleasure to work with you guys! I've learned a lot from you ๐Ÿ˜€
- Thanks everyone for all the likes and comments in the social media! You guys rock!!!


Now, it is time to put this MVP award on a special place the kids can't reach and celebrate this achievement with family, friends, colleagues and the community.

Wednesday, May 27, 2020

Speaking at Valo Online Summit EU 2020

I'm looking forward to speaking at Valo Online Summit EU 2020 on June 3-4, 2020.

The Valo Online Summit EU is an event exclusively dedicated to Valo Partners but this year the keynote ending the actual Summit will be open to everyone. Jeff Teper, the father or SharePoint, Corporate Vice President of Microsoft, will speak in this free keynote about the latest innovations and experiences in the Microsoft 365 world on June 4th at 5 p.m. (CET). This is a big opportunity for Valo partners, their customers and other community members to join this free keynote and learn directly from Jeff! 


I will also contribute to the Valo Online Summit's success with a session about "Extending the Valo Teamwork power using SharePoint Site Designs and Site Scripts". Valo Teamwork helps customers to find and manage Microsoft 365 groups, Microsoft teams and SharePoint sites. My session covers an integration between Valo Teamwork and SharePoint Site Designs. The presentation's history is based on the idea of creating project workspaces with predefined configuration.

I am very happy to be a part of the speakers team! Let's get in touch if you're also going to participate in this event!

Event information:

Thursday, May 7, 2020

Identifying if a Teams personal app runs from Teams Web or Teams Desktop Client

I’ve recently built a Teams personal app that required a different authentication mechanism when running from Teams desktop client. It took me a while to figure out how to distinguish between Teams desktop client and Teams web. As far as I’ve researched, there is no supported way yet to clearly identify where a personal app is running. Hence, I decided to write this blog post which shows how to identify if a Teams personal app runs from Teams web or Teams desktop client.



In my case, I tried to embed a SharePoint page as a Teams personal app. This page had a SharePoint Framework web part which did not run from Teams desktop client without a special authentication algorithm. For different reasons, it was not feasible to utilize this new authentication logic in both scenarios: Teams web and Teams desktop client. Well, it was clear. I had to find a way around this!

After intensive platform comparison, I observed two special characteristics. The document interface (loaded web page representation in the browser) and the navigator interface (identity of the browser):

1. document.referrer

When a Teams personal app runs from Teams web, the referrer can contain a value like: https://teams.microsoft.com/_. When the same personal app runs from Teams desktop client the referrer will then be: https://teams.microsoft.com/iframe-container.html. However, the referrer might change depending on operations executed from the embedded page. Hence, the referrer property is not reliable and didn’t help me in that case. 

2. navigator. userAgent

When a Teams personal app runs from Teams desktop client the userAgent always contain the Teams agent. This is a value which consists of Teams/{version} such as Teams/1.3.00.12058. In case of Teams web, the userAgent property doesn't contain the Teams agent. That made the tweak in my case! 


Summary:

Checking if the value "Teams/" existed in the userAgent property, helped me to identify when my personal app was running from Teams web or Teams desktop client. I am constantly looking for an out of the box solution and hope that Microsoft will provide it soon!

I hope this blog post helps you not spending hours trying to understand the uniqueness of those two platforms.