Edit

Share via


Dynamically Create a Microsoft Teams Meeting using Microsoft Graph

In this exercise, you'll automate the process of creating a Microsoft Teams meeting link and passing to the ACS by using Azure Functions and Microsoft Graph.

Create Teams Meeting

  1. You'll need to create a Microsoft Entra ID app for Daemon app authentication. In this step, authentication will be handled in the background with app credentials, and a Microsoft Entra ID app will use Application Permissions to make Microsoft Graph API calls. Microsoft Graph will be used to dynamically create a Microsoft Teams meeting and return the Teams meeting URL.

  2. Perform the following steps to create a Microsoft Entra ID app:

    1. Go to Azure portal and select Microsoft Entra ID.
    2. Select the App registration tab followed by + New registration.
    3. Fill in the new app registration form details as shown below and select Register:
      • Name: ACS Teams Interop App
      • Supported account types: Accounts in any organizational directory (Any Microsoft Entra ID directory - Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox)
      • Redirect URI: leave this blank
    4. After the app is registered, go to API permissions and select + Add a permission.
    5. Select Microsoft Graph followed by Application permissions.
    6. Select the Calendars.ReadWrite permission and then select Add.
    7. After adding the permissions, select Grant admin consent for <YOUR_ORGANIZATION_NAME>.
    8. Go to the Certificates & secrets tab, select + New client secret, and then select Add.
    9. Copy the value of the secret into a local file. You'll use the value later in this exercise.
    10. Go to the Overview tab and copy the Application (client) ID and Directory (tenant) ID values into the same local file that you used in the previous step.

Creating a local.settings.json File

  1. Open the samples/acs-to-teams-meeting/server/csharp/GraphACSFunctions.sln in Visual Studio or open the GraphACSFunctions folder in Visual Studio Code.

  2. Go to the GraphACSFunctions project and create a local.settings.json file with the following values:

    {
        "IsEncrypted": false,
        "Values": {
            "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
            "TENANT_ID": "",
            "CLIENT_ID": "",
            "CLIENT_SECRET": "",
            "USER_ID": "",
            "ACS_CONNECTION_STRING": ""
        },
        "Host": {
            "LocalHttpPort": 7071,
            "CORS": "*",
            "CORSCredentials": false
        },
        "watchDirectories": [
            "Shared"
        ]
    }
    
    • Use the values you copied into the local file in an earlier exercise to update the TENANT_ID, CLIENT_ID and CLIENT_SECRET values.
    • Define USER_ID with the user id that you'd like to create a Microsoft Teams Meeting.

    You can get the User ID from the Azure portal.

    • Login using your Microsoft 365 developer tenant admin account.
    • Select Microsoft Entra ID
    • Navigate to the Users tab on the side bar.
    • Search for your user name and select it to see the user details.
    • Inside the user details, Object ID represents the User ID. Copy the Object ID value and use it for the USER_ID value in local.settings.json.

    Getting User ID from Microsoft Entra ID

    Note

    ACS_CONNECTION_STRING will be used in the next exercise so you don't need to update it yet.

  3. Open GraphACSFunctions.sln located in the acs-to-teams-meeting/server/csharp folder and note that it includes the following Microsoft Graph and Identity packages:

    <PackageReference Include="Azure.Communication.Identity" Version="1.3.1" />
    <PackageReference Include="Azure.Identity" Version="1.11.2" />
    <PackageReference Include="Microsoft.Graph" Version="5.51.0" />
    
  4. Go to Program.cs and note the following code in the ConfigureServices method:

        var host = new HostBuilder()
            .ConfigureFunctionsWebApplication()
            .ConfigureServices(services => {
                services.AddApplicationInsightsTelemetryWorkerService();
                services.ConfigureFunctionsApplicationInsights();
                services.AddSingleton(static p =>
                {
                    var config = p.GetRequiredService<IConfiguration>();
                    var clientSecretCredential = new ClientSecretCredential(
                        config.GetValue<string>("TENANT_ID"),
                        config.GetValue<string>("CLIENT_ID"),
                        config.GetValue<string>("CLIENT_SECRET")
                    );
    
                    return new GraphServiceClient(
                        clientSecretCredential,
                        ["https://graph.microsoft.com/.default"]
                    );
                });
    
                ...
    
                services.AddSingleton<IGraphService, GraphService>();
            })
            .Build();
    }
    
    • This code creates a GraphServiceClient object that can be used to call Microsoft Graph from Azure Functions. It's a singleton and can be injected into other classes.
    • You can make Microsoft Graph API calls with app-only permissions (such as Calendars.ReadWrite) by passing a ClientSecretCredential value to the GraphServiceClient constructor. The ClientSecretCredential uses the Tenant Id, Client Id and Client Secret values from the Microsoft Entra ID app.
  5. Open Services/GraphService.cs.

  6. Take a moment to explore the CreateMeetingEventAsync method:

    using System;
    using System.Threading.Tasks;
    using Microsoft.Graph;
    using Microsoft.Extensions.Configuration;
    
    namespace GraphACSFunctions.Services;
    
    public class GraphService : IGraphService
    {
        private readonly GraphServiceClient _graphServiceClient;
        private readonly IConfiguration _configuration;
    
        public GraphService(GraphServiceClient graphServiceClient, IConfiguration configuration)
        {
            _graphServiceClient = graphServiceClient;
            _configuration = configuration;
        }
    
        public async Task<string> CreateMeetingAsync()
        {
            var userId = _configuration.GetValue<string>("USER_ID");
            var newMeeting = await _graphServiceClient
                .Users[userId]
                .Calendar
                .Events
                .PostAsync(new()
                {
                    Subject = "Customer Service Meeting",
                    Start = new()
                    {
                        DateTime = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss"),
                        TimeZone = "UTC"
                    },
                    End = new()
                    {
                        DateTime = DateTime.UtcNow.AddHours(1).ToString("yyyy-MM-ddTHH:mm:ss"),
                        TimeZone = "UTC"
                    },
                    IsOnlineMeeting = true
                });
            return newMeeting.OnlineMeeting.JoinUrl;
        }
    }
    
    • GraphServiceClient and IConfiguration objects are injected into the constructor and assigned to fields.
    • The CreateMeetingAsync() function posts data to the Microsoft Graph Calendar Events API, which dynamically creates an event in a user's calendar and returns the join URL.
  7. Open TeamsMeetingFunctions.cs and take a moment to examine it's constructor. The GraphServiceClient you looked at earlier is injected and assigned to the _graphService field.

    private readonly IGraphService _graphService;
    
    public TeamsMeetingFunction(IGraphService graphService) => _graphService = graphService;
    
  8. Locate the Run method:

    [Function("HttpTriggerTeamsUrl")]
    public async Task<HttpResponseData> Run(
        [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequestData req,
        ILogger log)
    {
        var response = req.CreateResponse(HttpStatusCode.OK);
        await response.WriteStringAsync(await _graphService.CreateMeetingAsync());
        return response;
    }
    
    • It defines a function name of HttpTriggerTeamsUrl that can be called with an HTTP GET request.
    • It calls _graphService.CreateMeetingAsync(), which creates a new event and returns the join URL.
  9. Run the program by pressing F5 in Visual Studio or by selecting Debug --> Start Debugging from the menu. This action starts the Azure Functions project and make the ACSTokenFunction available to call.

Note

If you're using VS Code you can open a terminal window in the GraphACSFunctions folder and run func start. This assumes that you have the Azure Functions Core Tools installed on your machine.

Calling the Azure Function from React

  1. Now that the httpTriggerTeamsUrl function is ready to use, let's call it from the React app.

  2. Expand the client/react folder.

  3. Add an .env file into the folder with the following values:

    REACT_APP_TEAMS_MEETING_FUNCTION=http://localhost:7071/api/httpTriggerTeamsUrl
    REACT_APP_ACS_USER_FUNCTION=http://localhost:7071/api/httpTriggerAcsToken
    

    These values will be passed into React as it builds so that you can easily change them as needed during the build process.

  4. Open client/react/App.tsx file in VS Code.

  5. Locate the teamsMeetingLink state variable in the component. Remove the hardcoded teams link and replace it with empty quotes:

    const [teamsMeetingLink, setTeamsMeetingLink] = useState<string>('');
    
  6. Locate the useEffect function and change it to look like the following. This handles calling the Azure Function you looked at earlier which creates a Teams meeting and returns the meeting join link:

    useEffect(() => {
        const init = async () => {
            /* Commenting out for now
            setMessage('Getting ACS user');
            //Call Azure Function to get the ACS user identity and token
            const res = await fetch(process.env.REACT_APP_ACS_USER_FUNCTION as string);
            const user = await res.json();
            setUserId(user.userId);
            setToken(user.token);
            */
    
            setMessage('Getting Teams meeting link...');
            //Call Azure Function to get the meeting link
            const resTeams = await fetch(process.env.REACT_APP_TEAMS_MEETING_FUNCTION as string);
            const link = await resTeams.text();
            setTeamsMeetingLink(link);
            setMessage('');
            console.log('Teams meeting link', link);
    
        }
        init();
    
    }, []);
    
  7. Save the file before continuing.

  8. Open a terminal window, cd into the *react folder, and run npm start to build and run the application.

  9. After the application builds and loads, you should see the ACS calling UI displayed and can then call into the Teams meeting that was dynamically created by Microsoft Graph.

  10. Stop the terminal process by entering Ctrl + C in the terminal window.

  11. Stop the Azure Functions project.

Next Step

Note

Visit the Azure Communication Services documentation to learn more about extending Microsoft Teams meetings in other ways.