Click here to Skip to main content
15,885,366 members
Articles / Hosted Services / Azure
Article

Building Microsoft Teams Apps with Serverless Python Part 2: Building Channel and Group Tabs with Python and Azure Functions

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
19 Jan 2022CPOL8 min read 3.5K   29   3   1
In this article we explore how to build Channel and Group Tabs and Tabs with Adaptive Cards using Python and Azure Functions.
Here we cover integrating Microsoft Teams with Azure serverless functions and SSO authentication to create a Channel Tabs app. Then we discuss how to easily create a minimal project for a working Channel Tab app hosted in the Azure cloud, using Microsoft Teams with Python and Visual Studio Code.

This article is a sponsored article. Articles such as these are intended to provide you with information on products and services that we consider useful and of value to developers

Image 1

In the first article of this series, we learned step-by-step how to build a Personal Tab app for Microsoft Teams in Python and backed by serverless Azure Functions. In this second article of our three-part series, we’ll create a Channel and Group Tabs app with Python and Azure Functions. Fortunately, we can reuse much of our code, and the learning curve lessens from now on.

Microsoft Teams Channel and Group apps present users with a page featuring information regarding that channel or group. This type of Teams app enables people to organize their calendars, project management worksheets, files, and more, and view other information that team or channel members share.

We base this new app on an existing sample in C# that illustrates implementing a Channel and Groups Tab with SSO authentication.

Starting from the code presented in the first article, you can implement the Channel and Group Tab app step-by-step by following this tutorial. Alternatively, you can download the source code from GitHub and jump to the configuration steps to get the app up and running. To follow along, you should know Python and have a free Azure account. We’ll show you how to do the rest.

Examining the Application Architecture

Just like the Personal Tab app we developed in the first article of this series, we’re building a serverless app with Python and Flask to control application flow and render HTML content inside Teams.  

When the serverless functions first load, they read an HTML template from disk and cache it in a variable to quickly render the web page HTML.

The app’s structure is the same as in the first article. However, this time, we’re adding a new az-function-configuration HTTP trigger to provide the Teams Tab with a configuration page for when someone adds the app to the channel or group:

Image 2

This application consists of only five Azure Functions: get-user-access-token, index, auth-start, auth-end, and configuration. We borrow functions from the first project, except for configuration, which we'll build in this tutorial.

Registering the Application on Azure

Before sending an access request to Teams, we must register a new application in the Azure Active Directory (AAD) App Registrations portal and create a secret that the app uses to authenticate to Azure. We already made an app registration for a Personal Tab in the first article, and both applications are similar. However, we want to create a Channel Tab app this time, so we must make a new registration. Follow the steps below to set up your new app registration.

First, follow the instructions for this process in the Teams Tab SSO Authentication README. You can use the same steps to register your Teams Python application with Azure AD. However, we’re creating an app registration with the channel-tab-sso-function-app name this time.

Next, change the App Registration in the Azure Portal so that the Redirect URI link points to your Function App website (for example, *.azurewebsites.net). Also, checkmark the Implicit grant and hybrid flows options.

Image 3

Also, ensure that the Application ID URI points to the /api/az-function-auth-end in the Expose an API tab:

Image 4

Reusing Code from the Existing Personal Tabs App

Now let's reuse the Personal Tabs code we created in the first article to build a large part of our Channel Tab application.

First, create a new folder in the \ChannelTabSSO folder to contain your new Python project. Then, open your computer’s terminal in the new folder and type the following command to open VS Code in the selected folder:

\ChannelTabSSO>code .

Then, copy the static files from the old PersonalTabSSO project to the new ChannelTabSSO project as follows:

Python
ChannelTabSSO 
    +-- static 
          +-- js 
            +-- auth.js 
    +-- templates 
          |-- base.html 
          |-- index.html 
          |-- auth_start.html 
          +-- auth_end.html

Next, copy the ssoAuthHelper.py and cacheHelper.py files from the PersonalTabSSO folder to ChannelTabSSO:

ChannelTabSSO
    |-- ssoAuthHelper.py
    +-- cacheHelper.py

Then, copy the az-functions-* folders and their contained files from the PersonalTabSSO folder to ChannelTabSSO:

ChannelTabSSO
    |-- az-function-auth-end/
    |-- az-function-auth-start/
    |-- az-function-index/
    +-- az-function-get-user-access-token/

This copy-and-paste work is the hardest part of the Channel Tabs application. Once finished, VS Code will display your project like this:

Image 5

Adding Configuration Page Files

Channel Tab apps are slightly different from Personal Tab apps. Microsoft designed the Channel or Group Tab apps to prompt a configuration page that presents choices and collects information from users, then sets the contentUrl of the content page according to the user's response.

As the Microsoft documentation creating configuration pages shows, the configuration page is a special content page that controls what group users can do within the app. Follow the steps below to implement the configuration page.

First, create a new images folder under static, then download the images from the source repository images folder and add them to the images folder:

ChannelTabSSO 
    +-- static 
          +-- images
                |-- IconGray.png
                |-- IconRed.png
                +-- TeamsIcon.png.png

Now create the configuration.html file in the templates folder with the following contents:

HTML
{% extends "base.html" %}
{% block title %}Python Configuration Page{% endblock %}
{% block content %}
  <h1>Python Configuration Channel Tab SSO Authentication</h1>
  <div id="divError" style="display: none"></div>
  <button onclick="requestConsent()" id="consent" style="display:none;">Authenticate</button>
  <div id="divGraphProfile" style="display: none"></div>
 
  <button onclick="(document.getElementById('icon').src = '/static/images/IconGray.png'); colorClickGray()">Select Gray</button>
  <img id="icon" src="https://raw.githubusercontent.com/marcelooliveira/PythonTeamsApps/main/ChannelTabSSO/static/images/TeamsIcon.png" alt="icon" style="width:100px" />
  <button onclick="(document.getElementById('icon').src = '/static/images/IconRed.png'); colorClickRed()">Select Red</button>
{% endblock %}
{% block scripts %}
<script>
  microsoftTeams.initialize();
  let saveGray = () => {
      microsoftTeams.settings.registerOnSaveHandler((saveEvent) => {
          microsoftTeams.settings.setSettings({
              websiteUrl: window.location.origin ,
              contentUrl: window.location.origin + "/api/az-function-index",
              entityId: "Python Channel Configure",
              suggestedDisplayName: "Python Channel Configure - Grey Settings"
          });
          saveEvent.notifySuccess();
      });
  }
  let saveRed = () => {
      microsoftTeams.settings.registerOnSaveHandler((saveEvent) => {
          microsoftTeams.settings.setSettings({
              websiteUrl: window.location.origin ,
              contentUrl: window.location.origin + "/api/az-function-index",
              entityId: "Python Channel Configure",
              suggestedDisplayName: "Python Channel Configure - Red Settings"
          });
          saveEvent.notifySuccess();
      });
  }
 
let icon = document.getElementById("icon");
  const colorClickGray = () => {
      microsoftTeams.settings.setValidityState(true);
      saveGray()
  }
 
  const colorClickRed = () => {
      microsoftTeams.settings.setValidityState(true);
      saveRed();
  }
</script>
 
{% endblock %}

Notice how the configuration page is slightly different from the index content page. Later, we’ll discuss how the configuration page works and how Teams presents it to the users when adding the Channel Tab app to a Channel or Group.

Creating the az-function-configuration Azure Function

We have already copied four Azure functions from the first Personal Tab app. But, we must still create the Azure function to work as an HTTP trigger and render our configuration page.

First, click the Azure tab and click the Create New Project icon:

Image 6

Next, select ChannelTabSSO as your local function project folder:

Image 7

Then, select Python as your function app language:

Image 8

Next, select HTTP trigger as the Azure Function template:

Image 9

Name it az-function-configuration:

Image 10

Then, make the function Anonymous so that anybody can access its endpoint without passing a code parameter:

Image 11

Replace the contents of the __init__.py file in the \az-function-configuration folder with this code:

Python
import azure.functions as func 
from flask import Flask
import sys
from cacheHelper import CacheHelper
 
app = Flask(__name__)
 
this = sys.modules[__name__]
this.cacheHelper = None
 
def main(req: func.HttpRequest, context: func.Context) -> func.HttpResponse:
    if this.cacheHelper is None:
        this.cacheHelper = CacheHelper(context.function_directory)
    return func.WsgiMiddleware(app).handle(req, context)
 
@app.route("/api/az-function-configuration")
def index():
    return this.cacheHelper.render_cached_page(app, "configuration.html")

The code above re-routes calls to the /api/az-function-configuration endpoint to the Python code and renders them with the Flask engine.

Application Setup and Configuration

As in the first article, we change some configurations so that our Channel Tab app can work on our development machine and in the cloud.

First, modify requirements.txt to ensure it contains these lines:

azure-functions
requests
flask==2.0.2
msal

Then, open the terminal at the project root folder and install the requirements:

Python
pip install -r requirements.txt

Next, click the Azure tab, select your Function app, and click Download Remote Settings:

Image 12

Open the local.settings.json file next. Then, add the configuration from the app registration you created on Azure Portal. In this case, we use the settings from channel-tab-sso-function-app.

Include the following configurations in the local.settings.json file: Instance, TenantId, ClientId, AppSecret, AuthUrl, and CacheEnabled:

JSON
{ 
  "IsEncrypted": false, 
  "Values": { 
    "AzureWebJobsStorage": "DefaultEndpointsProtocol=https;AccountName=personaltabssofunctionap;AccountKey=*************************************;EndpointSuffix=core.windows.net", 
    "FUNCTIONS_WORKER_RUNTIME": "python", 
    "FUNCTIONS_EXTENSION_VERSION": "~4", 
    "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING": "DefaultEndpointsProtocol=https;AccountName=personaltabssofunctionap;AccountKey=********************************************;EndpointSuffix=core.windows.net", 
    "WEBSITE_CONTENTSHARE": "personal-tab-sso-function-app45e3c0", 
    "APPINSIGHTS_INSTRUMENTATIONKEY": "********-****-****-****-************", 
    "Instance": "https://login.microsoftonline.com/", 
    "TenantId": "********-****-****-****-************", 
    "ClientId": "********-****-****-****-************", 
    "AppSecret": "***************************************************************", 
    "AuthUrl": "/oauth2/v2.0/token", 
    "CacheEnabled": "false" 
  } 
}

On the Azure tab, click Upload Local Settings. This command sends your local configurations to your Function App in the cloud:

Image 13

Finally, press F5 to run the project in debug mode:

Image 14

Observe how we now have five working Azure functions:

  • az-function-auth-end: [GET,POST] http://localhost:7071/api/az-function-auth-end
  • az-function-auth-start: [GET,POST] http://localhost:7071/api/az-function-auth-start
  • az-function-configuration: [GET,POST] http://localhost:7071/api/az-function-configuration
  • az-function-get-user-access-token: [GET,POST] http://localhost:7071/api/az-function-get-user-access-token
  • az-function-index: [GET,POST] http://localhost:7071/api/az-function-index

Now click the Functions folder within the Local Project folder and click the Deploy to the Function App icon. This action uploads your Python-based Functions to Azure:

Image 15

Finally, select your Function App in Azure for deployment:

 Image 16

Adding the Channel Tab App to a Team

We’ll install the Channel Tab app by uploading a .zip file containing the manifest like the Personal Tab app. First, click the Apps tab and click the Upload a Custom App link:

Image 17

Then, select the manifest.zip file:

Image 18

At this point, you’ve installed your new app. But, unlike Personal Tab apps, users can’t open Channel Tab apps immediately. Instead, the user can only open Channel and Group Tabs after someone adds the app to the team. Let’s explore this process.

First, click the app on the Apps tab and click the Add to a team button:

Image 19

Then, select the team where you want to add the app:

Image 20

After you add your app to the team, the app displays the Configuration page we created before:

Image 21

This page is a great place to define parameters, behaviors, and appearances to customize how your team or channel will use your new Python app. Although our sample Configuration page just has some placeholder buttons to select colors, it hints at the real-world possibilities:

Image 22

Using the Channel Tab App in a Team

After we add the Channel Tab app to a team, users can view a new Configure tab in the team’s interface:

Image 23

Now Teams redirects our user to the Index page. But as with Personal Tab apps, Channel Tab apps must first request and receive authorization from the users before use. Here is the same Index content page we copied from the original Personal Tab project featuring the Authenticate button:

Image 24

Teams requests the user to provide consent for your app before use:

Image 25

Now your team users can enjoy their Channel Tab with SSO authentication. What your application displays here depends on your application and the options you defined on the Configuration page.

Image 26

Next Steps

In this second installment of the series, we’ve covered integrating Microsoft Teams with Azure serverless functions and SSO authentication to create a Channel Tabs app. We’ve discussed how to easily create a minimal project for a working Channel Tab app hosted in the Azure cloud, using Microsoft Teams with Python and Visual Studio Code.

We created a new Channel Tab app with minimal effort from the first Personal Tab app. Flask and Jinja use HTML and JavaScript code to generate the new Configuration web page. And once again, Python code shines as the driving force behind our Azure serverless functions.

You now have a working channel tab application that you can use as a scaffolding for developing professional Microsoft Teams apps using your Python programming skills.

Sign up for a free trial of Azure to create and run your own Python-based Teams apps, or continue to the third and final part of this series to learn how to build tabs with adaptive cards.

To learn more about how to build apps for an individual, your team, your organization, or for all Microsoft Teams users everywhere, check out Develop apps for Microsoft Teams.

This article is part of the series 'Building Microsoft Teams Apps with Serverless Python View All

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Instructor / Trainer Alura Cursos Online
Brazil Brazil

Comments and Discussions

 
GeneralMy vote of 5 Pin
Ștefan-Mihai MOGA23-Jan-22 20:27
professionalȘtefan-Mihai MOGA23-Jan-22 20:27 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.