This sample code demonstrates how to create a real world line of business application with Office 365 services and the Microsoft Graph API. The application in this sample includes components that run in Microsoft Teams, Outlook, and inside SharePoint Framework web parts.
The sample application makes setting up, attending, and following up on meetings easy and productive.
To see an end to end demonstration of the functionality in this sample, watch this video.
- Install the following PowerShell modules
- Install .Net Core 3.0 SDK and a text editor like Visual Studio or Visual Studio Code
- Install Node.js Download
- Create a custom domain name.
- Please read the notes about the customHostname parameter in the Deploy the ARM Template with PowerShell section in this document. It provides more details and context regarding the custom domain name.
- There are free short-term domain name services available on the Internet, like https://www.freenom.com/ where you can get a free custom domain name.
- Office 365 Tenant and a Global Admin account
- Azure Subscription and an Admin account
We've automated as much of the installation process as possible with PowerShell and an ARM template. At a high level, here are the steps. To install the sample, follow the steps in the next section, Deployment Steps.
- Register for the Actionable Email Developer Dashboard
- Clone the code in this repository to your local environment.
- Create the AAD Application.
- Deploy the ARM Template with the PowerShell Azure (Az) Module to create all the resources in Azure and configure the Logic app.
- Compile the server code with Visual Studio 2019 or VS code, then deploy the dotnet core Web Site and Web Job.
- Create the Teams App and deploy it.
- Create tabs for the Meeting Capture web app and the Pending Meetings SPFx web part.
- Compile the SharePoint SPFx web part with npm and package it into a SharePoint package (sppkg), then deploy it to the SharePoint App Catalog with the PNP PowerShell module.
-
Register for the Actionable Email Developer Dashboard
- Login to the Actionable Email Developer Dashboard registration page with an Office 365 account. Fill in the form and submit it. Reference the table and screenshot below as you fill out the form.
Item Description Friendly Name Feel free to fill in whatever you want here. Sender email address Office 365 email account used to send Actionable Messages Target URLs SharePoint site for storing documents in the Office 365 account Scope of submission Select Organization - Make sure you Check the App Developer Agreement, and click the Save button to submit the form.
- In the tenant where you configured the Actionable Email Developer Dashboard, log in to the Office 365 Tenant administrator's email and find the registration confirmation email. Record the following information:
Item Key Provider Id (originator) originatorId Sender email address from which Actionable Emails will originate EmailSender -
Clone the code in this repository to your local environment.
-
Create the AAD Application
- Sign in to your Azure Account through the Azure portal.
- Click Azure Active Directory.
- Click App registrations.
- Click New registration.
- Name the application.
- In the Supported account types section, select Accounts in any organizational directory (Any Azure AD directory - Multitenant).
- Click Register.
- Click Add an Application ID URI.
- Click Set.
- Add a Redirect URI
- In the Application ID URI text box enter the URI.
Use the following format and replace the <YOUR.CUSTOM.DOMAIN> placeholder with the custom domain you created.
api://<YOUR.CUSTOM.DOMAIN>/<GUID GENERATED BY AZURE>
- Click Save.
- Click Add a scope.
- In the Scope name textbox enter
access_as_user
. - In the Who can consent toggle, select Admins and users.
- In the Admin consent display name textbox enter
Teams can access the user's profile.
. - In the Admin consent description textbox enter
Allows Teamsto call the app web APIs as the current user.
. - In the User consent display name textbox enter
Teams can access your user profile and make requests on your behalf.
. - In the User consent description textbox enter
Enable Teams to call this app's APIs with the same rights that you have.
. - Click Add scope.
-
Click Add a client application.
-
In the Client ID textbox, enter
1fec8e78-bce4-4aaf-ab1b-5451cc387264
. -
Check the Authorized scopes checkbox.
-
Click Add application.
-
Click Add a client application.
-
In the Client ID textbox, enter
5e3ce6c0-2b1f-4285-8d4b-75ee78787346
. -
Check the Authorized scopes checkbox.
-
Click Add application.
-
Click API permissions.
-
Click Microsoft Graph.
-
In the search box, enter the name of each permission in the list below, then check the checkbox next to it, and finally click Update permissions.
Note: Here is an example of doing this with the Calendars.Read permission.
-
Repeat the process until all of the permissions in this list are added.
- Calendars.Read
- Contacts.Read
- Group.ReadWrite.All
- Mail.Send
- Notes.Create
- Notes.ReadWrite
- Notes.ReadWrite.All
- offline_access
- OnlineMeetings.ReadWrite
- openid
- People.Read
- People.Read.All
- profile
- User.Read
- User.Read.All
- Click Grand admin consent for YOUR TENANT NAME.
- Click Yes.
When you are finished, the screen will indicate all permissions have been granted.
-
Deploy the ARM Template with PowerShell
- Open the
ARMParameters.json
file and update following values.
Key Description webAppName Free, but must be globally unique, 2-60 characters, valid characters include 0-9, a-z, A-Z, and _. If you enter the name fy19demo, the default web site url will be https://fy19demo.azurewebsites.net customHostname This parameter is the custom domain name you created in the prerequisites section. For example, if you created a custom host named mygreatdemo.tk then you would enter mygreatdemo.tk for this parameter. Note: Before deploying the ARM template, you need to create a CNAME record in DNS to map the custom domain name to the webAppName name parameter (listed above). For example, if you use fy19demo for the webAppName parameter then the default web site name will be fy19demo.azurewebsites.net. Therefore, you need to create a CNAME in DNS to map the customHostname parameter to fy19demo.azurewebsites.net. sqlAdministratorLogin SQL Server administrator account name. sqlAdministratorLoginPassword SQL Server administrator password. sqlDatabaseName SQL Server database name actionMessageOriginatorId originatorId generated in step Register for Actionable Email Developer Dashboard clientId Client Id generated in the Create the AAD Application step. clientSecret Client Secret generated in the Create the AAD Application step. tenantId Tenant Id recorded in the Create the AAD Application step. - Run the Powershell command
Connect-AzAccount
. - Run the script
.\DeployTemplate.ps1
. When prompted, enter the name of the resource group to create. - Since Teams does not support http access, we need to add SSL/TLS certificates to the custom domain. If the custom domain already has a certificate, you can follow the official Microsoft documentation here. If you don't have a certificate, you can follow this process to create one and secure your custom domain with it.
- Login to the Azure Portal. Find the resource group the script just created.
- In the resource group, locate the Logic app resource named SendActionMessage and click it.
- Click Edit on the top menu.
- Select the Connections step and click Add new to update the email to the EmailSender value you recorded in the Register for the Actionable Email Developer Dashboard step.
- Click Save .
- Open the
-
Compile and deploy the server code
- In the command line, change to the .\Source\FY19GraphShowcaseDemo directory.
- Run the script
.\PublishWebapp.ps1
. When prompted, enter the same resource group name and webAppName you entered in previous steps.
-
Create the Teams App
-
In the command line, change to the .\Source\FY19GraphShowcaseDemo\MeetingCaptureWebApp\Manifest directory.
-
Open the
manifest.json
file with a text editor. -
Update following values.
Key Value Description configurationUrl https://{customHostname}/configure/ Replace {customHostname} with your customHostname value. validDomains customHostname The same customHostname value used in previous steps. webApplicationInfo.id Client Id This value is generated in step Create the AAD Application. webApplicationInfo.resource App IdentifierUri This value is generated in step Create the AAD Application. -
Save the file.
-
Run the script
PublishTeamApp.ps1
to package the Microsoft Teams App in the manifest.zip file. -
Open the Microsoft Team App, click Apps, then click Upload a custom app and finally Upload for {your company}.
-
Upload the manifest.zip file.
-
-
Add the Apps to Microsoft Teams Tabs
In this section you create a Teams Tab and add the web app to the tab.
First, deploy the Meeting Capture web app.
- Open Microsoft Teams Apps and search for Meeting Capture.
- Click Meeting Capture Web.
- Select Add to a team.
- Select a channel and click Set up a tab.
- Configure the Tab according to the following screenshot, then click Save.
Next, deploy the Pending Meetings SPFx web part.
-
Compile and deploy SharePoint SPFx web part
In this section you will configure, compile, package, and deploy the SPFx web part to the App Catalog, add it to a SharePoint Site, approve the API permissions for it, then add it to a Teams Tab and configure it.
- Open the UpcomingMeetings.tsx file in a text editor.
- On line 30, set the targetId variable equal to the Id for the Planner Plan in the Team and Channel where you installed the Teams web app.
- On line 31, set the targetBucketId variable equal to the Id for the Bucket inside the Planner Plan in the Team and Channel where you installed the Teams web app.
Note: You can use the Microsoft Graph Explorer to quickly find Planner Ids and Bucket Ids with this query.
https://graph.microsoft.com/v1.0/planner/plans/{plan-id}/buckets
-
Save the file.
-
In the command line, change to the .\Source\SPFX\PendingMeetings directory.
-
Run following commands to restore, build, and package the solution.
npm install gulp build gulp bundle --ship gulp package-solution --ship
-
Then run the following command to publish web part to Microsoft Teams.
.\PublishWebpart.ps1
Please refer to the below table to enter the parameters:
Name Value Description orgName <orgName> The name of the tenant. If your SharePoint URL is http://contoso.sharepoint.com then your orgName is contoso. adminUPN <user>@<orgName>.onmicrosoft.com The site administrator account. For example: admin@contoso.onmicrosoft.com -
Add the SharePoint App to the SharePoint site associated with the Team where you are adding the Pending Meetings Teams Tab SPFx app.
-
Approve the API requests in the SharePoint Admin Center.
-
In Teams, click the + button to add a new Tab, select PendingMeetings, click Save.
-
Enter the Id for the Team and the Id for the Channel that you would like to query tasks from.
Note: You can use the Microsoft Graph Explorer to quickly find Teams Ids and Channel Ids with these queries.
https://graph.microsoft.com/v1.0/me/joinedTeams https://graph.microsoft.com/v1.0/teams/{team-id}/channels
The deployment is now complete! You are now ready to run the demo.
Follow these steps to demonstrate how to use the sample and all of the features and functionality it provides.
We use the Office365 service to get attendees photos, so please use Office365 to open emails and see the profile pictures.
This demo can be performed with 1 or more user accounts. It's really up to the presenter. In this demo script, user 1 is the person who sets up and attends the meeting and user 2 is a person who attends the meeting.
-
Demo starts in a Channel in Microsoft Teams.
-
User 1 clicks the tab named Meeting Capture.
-
User 1 views the embedded web site.
-
User 1 is prompted with a message: No meetings are set up for this channel, do you want to make one?
-
If User 1 selects No then the popup closes and they see a screen with some text and a cool image in the middle with a create button.
-
User 1 selects Yes.
-
User 1 fills out a custom form in the web application to make the first meeting.
- Enter a title for the meeting.
- The form allows the creator to assign attendees to the meeting.
- By default, the people User 1 most frequently works with, who are members of the Team, are added as attendees.
- Select a date and time for the meeting that works for all attendees.
- Any or all the Team’s members can be removed before the form is submitted.
- User 1 attaches documents that are considered pre-reads. Each time a document is attached a task is created to read it before the meeting starts. This is known as a pre-read task. Pre-read tasks are assigned to all meeting attendees, one task per document that is uploaded.
Note: Do not upload files larger than 4MB. See the Known Limitations section in this document for more information.
- User 1 adds meeting agenda items in a structured list.
-
Upon form submittal a meeting event is created in Outlook, the documents are uploaded to SharePoint, and Tasks are created and assigned in Planner to do the pre-reading.
-
Data is also written to the SQL Server Database. For more information about the data and how to view it please see the SQL Server Database Details section in this document.
-
A Planner is created only once per channel and it contains all the tasks associated with all the meetings in the channel.
-
The content in the body of the meeting invite includes:
- Agenda
- Links to the tasks and who they are assigned to
- Links to any attachments
-
User 1 clicks the Save button and after everything is saved and published (described above) the user is taken to a different page with a list of the meetings created in the channel. When a user selects one of the meetings in the list the web app takes the user to the page where they can capture notes for the meeting.
The system automatically reminds people to complete their pre-read tasks.
- An Azure Web Job continually checks to see if you have any pre-read tasks you have not completed.
- It sends a message to remind you to complete the pre-read tasks.
- The email is sent 1 day in advance of the meeting starting. The number of days is a configurable setting.
- User opens email in Outlook mobile in a web browser and sees an actionable message that includes a link to a pre-read document (PowerPoint presentation).
Users can easily access pre read tasks to prepare for the meeting.
- User 1 opens Teams in a mobile device and loads the Pending Meeting Tasks SPFx app web part on a SharePoint Page in a Teams tab named Pending Meeting Tasks and sees the upcoming tasks associated with their meetings.
- User 1 selects the upcoming meeting and sees all of the tasks associated with the meeting displayed.
- User 1 clicks the button to filter the tasks so only User 1’s tasks are displayed.
- User 1 opens a SharePoint page on their Intranet where the same Pending Meeting Tasks SPFx web part is displayed and the same actions can be performed.
Once the meeting begins, users can capture notes and assign tasks related to the meeting.
- User 1 opens Teams and clicks the Meeting Capture Tab that contains the embedded web site.
- User 1 sees details for the current meeting including:
- Files in the SharePoint site associated with the Team
- Notes in the OneNote associated with the Team
- Tasks from the Planner associated with the Team.
- User 1 creates notes.
- User 1 creates tasks.
- User 1 saves and publishes the information captured in the web app.
- The system sends an email with all of the captured meeting information to all meeting attendees and all people who were assigned tasks.
- The system publishes the notes and tasks to OneNote and Planner.
- User 1 or 2 receives an email summary of the meeting and opens it and reviews all the content.
- User 1 or 2 clicks links in the email that demonstrate the published data has been put into OneNote, and Planner.
- User 1 or 2 can return to the Meeting Capture Tab at any time to review the meeting, notes, tasks, and documents.
- User 1 schedules a follow up meeting by following the same process described above to create a meeting.
To delete demo data open the menu for the Meeting Capture tab and select Settings.
In the dropdown list, select Delete Events.
The page will refresh and show you a list of all the meetings. Click the delete button next to a meeting to delete it. When you delete a meeting EVERYTHING associated with it in the demo is deleted (Outlook, Planner, Files, SQL).
You can also add another tab with the Delete Events option selected if you want one tab for the Meeting Capture app and another tab to delete events.
- Open Teams, you can open online teams: https://teams.microsoft.com or local Teams App.
- Select Teams, click Join or create a team, then select Create Team.
- Select Build a team from scratch.
- Select Public kind.
- Type a team name and click Create button.
- Add members to teams and click Add button, then click Close button.
- A new Team has been created successfully.
- Select the Team and click Add channel.
- Type a Channel name, select the checkbox and click Add button.
- A new Channel has been added successfully.
- Download the team tab manifest.json file.
- Open Teams Apps, search App Studio.
- Find App Studio in the App Marketplace. Install it if you don’t have it. Click App Studio and Click Open button.
- Select Manifest editor and Import an existing app.
- Select manifest.json that you download above.
- Select Meeting Capture Web.
- Click Test and distribute and click Install button.
- Select Add to a team.
- Select a channel and then click Set up a tab.
- Select the Home value in the dropdown list, then click the Save button.
- Done.
In this sample we use the Graph Toolkit Controls wherever possible. You will see them in both the ASP.NET MVC Web Site and in the SPFX web part. By default, Graph Toolkit Controls make client side Graph API calls. In this sample, the Graph Toolkit controls use the Proxy Provider that allows it to use the .NET SDK to make server side calls to Graph. We took this approach because we did not want to have some of the application's calls to Graph client side and some of them server side. This pattern makes the Graph API access pattern consistent. You do not have to use the same approach we used in this sample, it is OK to skip the proxy and instead have both client side and server side calls to the Graph API in your code. It's up to you.
Learn more about the Graph Toolkit Controls and the Proxy.
The SQL Server Database supplements the application and stores information about meeting agenda items, attendees, meetings, files associated with tasks, and channels.
- If you would like to browse the data in the database, follow these steps.
-
Login to the Azure Portal.
-
Find the resource group the script created and open it.
-
In the resource group, locate the SQL database and click it.
-
Click Query editor(preview) and login to the SQL server with the sqlAdministratorLogin and sqlAdministratorLoginPassword parameters you set in the
ARMParameters.json
file. -
Expand the Tables node to see the different tables in the database.
- Agendas
- Attendees
- Meetings
- TaskFiles
- TeamChannels
-
In the Meeting Capture web application, on the new meeting creation page, we implemented some custom business logic to add the default meeting attendees. Essentially, we wanted to invite the people who the meeting organizer works with the most, but only if they are a member of the Team. We also wanted to ensure no duplicates were added. To implement this we used the Microsoft Graph API. Here's what the logic looks like at a high level.
- Get the top 50 people the meeting organizer works with on a regular basis.
- Get all the members of the current Team.
- Select the top 5 people that exist in both the datasets returned in steps 1 and 2.
- If the current user is not in the list, add to the attendees list.
- When adding an attendee from the UI, first determine if the person exists in the current attendees list. If the person does not exist in the list, then add them.
- The SharePoint upload document API used in this sample can only upload files less than 4MB. For files larger than 4MB, a more complicated method is needed, it is explained here. https://docs.microsoft.com/en-us/graph/api/driveitem-put-content?view=graph-rest-1.0&tabs=http