public async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            log.LogInformation("ProvisionTeam function invoked.");

            try
            {
                // Get request body
                string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
                var    request     = JsonConvert.DeserializeObject <TeamProvisionRequest>(requestBody);

                if (!String.IsNullOrEmpty(request.TeamTemplateUri))
                {
                    // Get the URL of the root site collection
                    var rootSiteUrl = request.RequestSiteUrl.Substring(0, request.RequestSiteUrl.IndexOf("/", 10));

                    // Get the provisioning template based on the provided URI
                    var templateUri    = new Uri(new Uri(request.RequestSiteUrl), request.TeamTemplateUri).AbsoluteUri;
                    var tenantTemplate = _templatesProvider.GetTenantTemplate(templateUri);

                    if (tenantTemplate.Teams != null &&
                        tenantTemplate.Teams.Teams != null &&
                        tenantTemplate.Teams.Teams.Count > 0)
                    {
                        // Get the first team template
                        var team = tenantTemplate.Teams.Teams[0];

                        // Configure Team properties
                        team.DisplayName  = request.TeamTitle;
                        team.MailNickname = request.TeamAlias.ToLower();

                        // Configure Team security
                        team.Security = new OfficeDevPnP.Core.Framework.Provisioning.Model.Teams.TeamSecurity();
                        foreach (var owner in request.TeamOwners)
                        {
                            team.Security.Owners.Add(new OfficeDevPnP.Core.Framework.Provisioning.Model.Teams.TeamSecurityUser {
                                UserPrincipalName = owner.UPN
                            });
                        }
                        foreach (var member in request.TeamMembers)
                        {
                            team.Security.Members.Add(new OfficeDevPnP.Core.Framework.Provisioning.Model.Teams.TeamSecurityUser {
                                UserPrincipalName = member.UPN
                            });
                        }

                        // Provision the template onto the target tenant
                        SPOUtilities.ApplyTenantTemplate(rootSiteUrl, tenantTemplate, log);
                    }
                }
                else
                {
                    throw new ApplicationException("Invalid input request! Missing Team Template URI!");
                }
            }
            catch (Exception ex)
            {
                return(new ExceptionResult(ex, true));
            }

            return(new OkObjectResult("All good!"));
        }
        /// <summary>
        /// Applies a Provisioning Template Hierarchy to a target tenant
        /// </summary>
        /// <param name="targetSiteUrl">The URL of the target Site Collection</param>
        /// <param name="tenantTemplate">The Provisioning Template Hierarchy to apply</param>
        /// <param name="log">The TraceWriter to log activities</param>
        public static void ApplyTenantTemplate(String targetSiteUrl, ProvisioningHierarchy tenantTemplate, ILogger log)
        {
            ProvisioningTemplateApplyingInformation ptai =
                new ProvisioningTemplateApplyingInformation();

            // We exclude Term Groups because they are not supported in AppOnly
            ptai.HandlersToProcess  = Handlers.All;
            ptai.HandlersToProcess ^= Handlers.TermGroups;

            ptai.MessagesDelegate += delegate(string message, ProvisioningMessageType messageType)
            {
                log.LogDebug($"{messageType} - {message}");
            };
            ptai.ProgressDelegate += delegate(string message, int step, int total)
            {
                log.LogInformation($"{step:00}/{total:00} - {message}");
            };

            var tenantUrl = SPOUtilities.GetTenantAdministrationUrl(targetSiteUrl);

            using (var tenantContext = SPOContextProvider.BuildAppOnlyClientContext(tenantUrl))
            {
                using (var pnpTenantContext = PnPClientContext.ConvertFrom(tenantContext))
                {
                    var tenant = new Microsoft.Online.SharePoint.TenantAdministration.Tenant(pnpTenantContext);

                    // Prepare a dictionary to hold the access tokens
                    var accessTokens = new Dictionary <String, String>();

                    // Get the Microsoft Graph Access Token
                    var clientApplication = ConfidentialClientApplicationBuilder
                                            .Create(Environment.GetEnvironmentVariable("ClientId"))
                                            .WithTenantId(Environment.GetEnvironmentVariable("Tenant"))
                                            .WithClientSecret(Environment.GetEnvironmentVariable("ClientSecret")).Build();

                    try
                    {
                        string[] graphScopes          = new string[] { "https://graph.microsoft.com/.default" };
                        var      authenticationResult = clientApplication.AcquireTokenForClient(graphScopes).ExecuteAsync().GetAwaiter().GetResult();
                        var      graphAccessToken     = authenticationResult.AccessToken;
                        accessTokens.Add(new Uri("https://graph.microsoft.com/").Authority, graphAccessToken);
                    }
                    catch (Exception ex)
                    {
                        log.LogError(ex.Message);
                        throw;
                    }

                    // Configure the OAuth Access Tokens for the PnPClientContext, too
                    pnpTenantContext.PropertyBag["AccessTokens"] = accessTokens;
                    ptai.AccessTokens = accessTokens;

                    // Define a PnPProvisioningContext scope to share the security context across calls
                    using (var pnpProvisioningContext = new PnPProvisioningContext(async(r, s) =>
                    {
                        if (accessTokens.Any(i => i.Key.Equals(r, StringComparison.InvariantCultureIgnoreCase) ||
                                             r.ToLower().Contains(i.Key)))
                        {
                            // In this scenario we just use the dictionary of access tokens
                            // in fact the overall operation for sure will take less than 1 hour
                            var item = accessTokens.FirstOrDefault(i =>
                                                                   i.Key.Equals(r, StringComparison.InvariantCultureIgnoreCase) ||
                                                                   r.ToLower().Contains(i.Key));

                            return(await Task.FromResult(item.Value));
                        }
                        else
                        {
                            return(null);
                        }
                    }))
                    {
                        log.LogInformation($"Hierarchy Provisioning Started: {DateTime.Now:hh.mm.ss}");
                        tenant.ApplyProvisionHierarchy(tenantTemplate, null, ptai);
                        log.LogInformation($"Hierarchy Provisioning Completed: {DateTime.Now:hh.mm.ss}");
                    }
                }
            }
        }