Ejemplo n.º 1
0
        private async Task <CanProvisionResult> CanProvisionInternal(CanProvisionModel model, Boolean validateUser = true)
        {
            var canProvisionResult = new CanProvisionResult();

            if (!validateUser || IsValidUser())
            {
                String provisioningScope       = ConfigurationManager.AppSettings["SPPA:ProvisioningScope"];
                String provisioningEnvironment = ConfigurationManager.AppSettings["SPPA:ProvisioningEnvironment"];

                var tokenId          = $"{model.TenantId}-{model.UserPrincipalName.ToLower().GetHashCode()}-{provisioningScope}-{provisioningEnvironment}";
                var graphAccessToken = await ProvisioningAppManager.AccessTokenProvider.GetAccessTokenAsync(
                    tokenId, "https://graph.microsoft.com/");

                // Retrieve the provisioning package from the database and from the Blob Storage
                var context = GetDataContext();
                DomainModel.Package package = null;

                // Get the package
                if (ProvisioningAppManager.IsTestingEnvironment)
                {
                    // Process all packages in the test environment
                    package = context.Packages.FirstOrDefault(p => p.Id == new Guid(model.PackageId));
                }
                else
                {
                    // Process not-preview packages in the production environment
                    package = context.Packages.FirstOrDefault(p => p.Id == new Guid(model.PackageId) && p.Preview == false);
                }

                if (package != null)
                {
                    if ((package.PackageType == PackageType.Tenant &&
                         !this.Request.Url.AbsolutePath.Contains("/tenant/")) ||
                        (package.PackageType == PackageType.SiteCollection &&
                         !this.Request.Url.AbsolutePath.Contains("/site/")))
                    {
                        throw new ApplicationException("Invalid request, the requested package/template is not valid for the current request!");
                    }

                    // Retrieve parameters from the package/template definition
                    var packageFileUrl     = new Uri(package.PackageUrl);
                    var packageLocalFolder = packageFileUrl.AbsolutePath.Substring(1,
                                                                                   packageFileUrl.AbsolutePath.LastIndexOf('/') - 1);
                    var packageFileName = packageFileUrl.AbsolutePath.Substring(packageLocalFolder.Length + 2);

                    ProvisioningHierarchy hierarchy = GetHierarchyFromStorage(packageLocalFolder, packageFileName);

                    // If we have the hierarchy
                    if (hierarchy != null)
                    {
                        var accessTokens = new Dictionary <String, String>();

                        AuthenticationManager authManager = new AuthenticationManager();
                        var ptai = new ProvisioningTemplateApplyingInformation();

                        // Retrieve the SPO URL for the Admin Site
                        var rootSiteUrl = model.SPORootSiteUrl;

                        // Retrieve the SPO Access Token for SPO
                        var spoAccessToken = await ProvisioningAppManager.AccessTokenProvider.GetAccessTokenAsync(
                            tokenId, rootSiteUrl,
                            ConfigurationManager.AppSettings["ida:ClientId"],
                            ConfigurationManager.AppSettings["ida:ClientSecret"],
                            ConfigurationManager.AppSettings["ida:AppUrl"]);

                        // Store the SPO Access Token for any further context cloning
                        accessTokens.Add(new Uri(rootSiteUrl).Authority, spoAccessToken);

                        // Define a PnPProvisioningContext scope to share the security context across calls
                        using (var pnpProvisioningContext = new PnPProvisioningContext(async(r, s) =>
                        {
                            if (accessTokens.ContainsKey(r))
                            {
                                // In this scenario we just use the dictionary of access tokens
                                // in fact the overall operation for sure will take less than 1 hour
                                // (in fact, it's a matter of few seconds)
                                return(await Task.FromResult(accessTokens[r]));
                            }
                            else
                            {
                                // Try to get a fresh new Access Token
                                var token = await ProvisioningAppManager.AccessTokenProvider.GetAccessTokenAsync(
                                    tokenId, $"https://{r}",
                                    ConfigurationManager.AppSettings["ida:ClientId"],
                                    ConfigurationManager.AppSettings["ida:ClientSecret"],
                                    ConfigurationManager.AppSettings["ida:AppUrl"]);

                                accessTokens.Add(r, token);

                                return(token);
                            }
                        }))
                        {
                            // If the user is an admin (SPO or Tenant) we run the Tenant level CanProvision rules
                            if (model.UserIsSPOAdmin || model.UserIsTenantAdmin)
                            {
                                // Retrieve the SPO URL for the Admin Site
                                var adminSiteUrl = model.SPORootSiteUrl.Replace(".sharepoint.com", "-admin.sharepoint.com");

                                // Retrieve the SPO Access Token for the Admin Site
                                var spoAdminAccessToken = await ProvisioningAppManager.AccessTokenProvider.GetAccessTokenAsync(
                                    tokenId, adminSiteUrl,
                                    ConfigurationManager.AppSettings["ida:ClientId"],
                                    ConfigurationManager.AppSettings["ida:ClientSecret"],
                                    ConfigurationManager.AppSettings["ida:AppUrl"]);

                                // Store the SPO Admin Access Token for any further context cloning
                                accessTokens.Add(new Uri(adminSiteUrl).Authority, spoAdminAccessToken);

                                // Connect to SPO Admin Site and evaluate the CanProvision rules for the hierarchy
                                using (var tenantContext = authManager.GetAzureADAccessTokenAuthenticatedContext(adminSiteUrl, spoAdminAccessToken))
                                {
                                    using (var pnpTenantContext = PnPClientContext.ConvertFrom(tenantContext))
                                    {
                                        // Creat the Tenant object for the current SPO Admin Site context
                                        TenantAdmin.Tenant tenant = new TenantAdmin.Tenant(pnpTenantContext);

                                        // Run the CanProvision rules against the current tenant
                                        canProvisionResult = CanProvisionRulesManager.CanProvision(tenant, hierarchy, null, ptai);
                                    }
                                }
                            }
                            else
                            {
                                // Otherwise we run the Site level CanProvision rules

                                // Connect to SPO Root Site and evaluate the CanProvision rules for the hierarchy
                                using (var clientContext = authManager.GetAzureADAccessTokenAuthenticatedContext(rootSiteUrl, spoAccessToken))
                                {
                                    using (var pnpContext = PnPClientContext.ConvertFrom(clientContext))
                                    {
                                        // Run the CanProvision rules against the root site
                                        canProvisionResult = CanProvisionRulesManager.CanProvision(pnpContext.Web, hierarchy.Templates[0], ptai);
                                    }
                                }
                            }
                        }
                    }
                }
                else
                {
                    throw new ApplicationException("Invalid request, the requested package/template is not available!");
                }
            }

            return(canProvisionResult);
        }
Ejemplo n.º 2
0
        public static async Task <IActionResult> Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, ILogger log, ExecutionContext context)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");
            try
            {
                string siteUrl  = "";
                string userName = "";
                string password = "";

                var securePassword = new SecureString();
                foreach (char c in password)
                {
                    securePassword.AppendChar(c);
                }
                securePassword.MakeReadOnly();

                var authManager = new AuthenticationManager(userName, securePassword);
                using (ClientContext clientContext = authManager.GetContext(siteUrl))
                {
                    var web = clientContext.Web;

                    var templateFileName            = "SitePagesTemplate.pnp";
                    FileConnectorBase fileConnector = new FileSystemConnector(context.FunctionAppDirectory, "");
                    var openXmlConnector            = new OpenXMLConnector(templateFileName, fileConnector);
                    var provider = new XMLOpenXMLTemplateProvider(openXmlConnector);
                    templateFileName = templateFileName.Substring(0, templateFileName.LastIndexOf(".", StringComparison.Ordinal)) + ".xml";
                    ProvisioningTemplate provisioningTemplate = provider.GetTemplate(templateFileName);
                    provisioningTemplate.Connector = provider.Connector;

                    var applyingInformation = new ProvisioningTemplateApplyingInformation()
                    {
                        ProgressDelegate = (message, progress, total) =>
                        {
                            log.LogInformation(string.Format("{0:00}/{1:00} - {2}", progress, total, message));
                        },
                        MessagesDelegate = (message, messageType) =>
                        {
                            log.LogInformation(string.Format("{0} - {1}", messageType, message));
                        },
                        IgnoreDuplicateDataRowErrors = true
                    };

                    web.ApplyProvisioningTemplate(provisioningTemplate, applyingInformation);

                    return(new OkObjectResult("Done"));
                }
            }
            catch (ServerException e)
            {
                log.LogError($"Message: {e.Message}");
                log.LogError($"ServerErrorCode: {e.ServerErrorCode}");
                log.LogError($"ServerErrorDetails: {e.ServerErrorDetails}");
                log.LogError($"ServerErrorTraceCorrelationId: {e.ServerErrorTraceCorrelationId}");
                log.LogError($"ServerErrorTypeName: {e.ServerErrorTypeName}");
                log.LogError($"ServerErrorValue: {e.ServerErrorValue}");
                log.LogError($"ServerStackTrace: {e.ServerStackTrace}");
                log.LogError($"Source: {e.Source}");
                log.LogError($"StackTrace: {e.StackTrace}");

                throw;
            }
            catch (Exception e)
            {
                log.LogError($"Error while processing dossier: {e.Message}\n\n{e.StackTrace}");
                throw;
            }
        }
Ejemplo n.º 3
0
        protected override void ExecuteCmdlet()
        {
            SelectedWeb.EnsureProperty(w => w.Url);

            if (!System.IO.Path.IsPathRooted(Path))
            {
                Path = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, Path);
            }
            if (!string.IsNullOrEmpty(ResourceFolder))
            {
                if (System.IO.Path.IsPathRooted(ResourceFolder))
                {
                    ResourceFolder = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, ResourceFolder);
                }
            }

            FileInfo fileInfo = new FileInfo(Path);

            XMLTemplateProvider provider =
                new XMLFileSystemTemplateProvider(fileInfo.DirectoryName, "");

            var provisioningTemplate = provider.GetTemplate(fileInfo.Name);

            if (provisioningTemplate != null)
            {
                FileSystemConnector fileSystemConnector = null;
                if (string.IsNullOrEmpty(ResourceFolder))
                {
                    fileSystemConnector = new FileSystemConnector(fileInfo.DirectoryName, "");
                }
                else
                {
                    fileSystemConnector = new FileSystemConnector(ResourceFolder, "");
                }
                provisioningTemplate.Connector = fileSystemConnector;

                if (Parameters != null)
                {
                    foreach (var parameter in Parameters.Keys)
                    {
                        if (provisioningTemplate.Parameters.ContainsKey(parameter.ToString()))
                        {
                            provisioningTemplate.Parameters[parameter.ToString()] = Parameters[parameter].ToString();
                        }
                        else
                        {
                            provisioningTemplate.Parameters.Add(parameter.ToString(), Parameters[parameter].ToString());
                        }
                    }
                }

                var applyingInformation = new ProvisioningTemplateApplyingInformation();

                if (this.MyInvocation.BoundParameters.ContainsKey("Handlers"))
                {
                    applyingInformation.HandlersToProcess = Handlers;
                }
                if (this.MyInvocation.BoundParameters.ContainsKey("ExcludeHandlers"))
                {
                    foreach (var handler in (OfficeDevPnP.Core.Framework.Provisioning.Model.Handlers[])Enum.GetValues(typeof(Handlers)))
                    {
                        if (!ExcludeHandlers.Has(handler) && handler != Handlers.All)
                        {
                            Handlers = Handlers | handler;
                        }
                    }
                    applyingInformation.HandlersToProcess = Handlers;
                }

                applyingInformation.ProgressDelegate = (message, step, total) =>
                {
                    WriteProgress(new ProgressRecord(0, string.Format("Applying template to {0}", SelectedWeb.Url), message)
                    {
                        PercentComplete = (100 / total) * step
                    });
                };

                applyingInformation.MessagesDelegate = (message, type) =>
                {
                    if (type == ProvisioningMessageType.Warning)
                    {
                        WriteWarning(message);
                    }
                };

                applyingInformation.OverwriteSystemPropertyBagValues = OverwriteSystemPropertyBagValues;
                SelectedWeb.ApplyProvisioningTemplate(provisioningTemplate, applyingInformation);
            }
        }
        private static void applyTemplate(SiteInformation siteInformation, ExecutionContext functionContext, ClientCredentials credentials, TraceWriter log)
        {
            try
            {
                using (var ctx = new AuthenticationManager().GetAppOnlyAuthenticatedContext(siteInformation.SiteUrl, credentials.ClientID, credentials.ClientSecret))
                {
                    Web web = ctx.Web;
                    ctx.Load(web, w => w.Title, w => w.Navigation.QuickLaunch);
                    ctx.ExecuteQueryRetry();

                    string groupID = GetSiteGroupID(ctx);
                    // UpdateSubscriptionItemProperties(credentials, siteInformation, web.Title ,log);
                    var rootSiteUrl = ConfigurationManager.AppSettings["RootSiteUrl"];
                    log.Info($"Successfully connected to site: {web.Title}");

                    var navigationcolls = web.Navigation.QuickLaunch;
                    foreach (var nav in navigationcolls)
                    {
                        if (nav.Title == "Key Dates & Deliverables")
                        {
                            string url = nav.Url.Split('?')[0] + "?groupId=" + groupID + "&planId=" + siteInformation.PlanId;
                            nav.Url = url;
                            nav.Update();
                            ctx.ExecuteQueryRetry();
                        }
                    }

                    string              currentDirectory = functionContext.FunctionDirectory;
                    DirectoryInfo       dInfo            = new DirectoryInfo(currentDirectory);
                    var                 schemaDir        = dInfo.Parent.FullName + "\\Templates";
                    XMLTemplateProvider sitesProvider    = new XMLFileSystemTemplateProvider(schemaDir, "");

                    log.Info($"About to get template with with filename '{PNP_TEMPLATE_FILE}'");

                    ProvisioningTemplate template = sitesProvider.GetTemplate(PNP_TEMPLATE_FILE);

                    var getHomeClientPage = template.ClientSidePages.Find(i => i.Title == "Home");
                    if (getHomeClientPage != null)
                    {
                        UpdateControlsDataDynamic(siteInformation, getHomeClientPage);
                    }

                    log.Info($"Successfully found template with ID '{template.Id}'");

                    ProvisioningTemplateApplyingInformation ptai = new ProvisioningTemplateApplyingInformation
                    {
                        ProgressDelegate = (message, progress, total) =>
                        {
                            log.Info(string.Format("{0:00}/{1:00} - {2}", progress, total, message));
                        }
                    };

                    // Associate file connector for assets..
                    FileSystemConnector connector = new FileSystemConnector(Path.Combine(currentDirectory, "Files"), "");
                    template.Connector = connector;

                    web.ApplyProvisioningTemplate(template, ptai);

                    if (siteInformation.IsTopNavigation)
                    {
                        // Add top navigation bar.
                        web.AddNavigationNode("SharePoint Main Menu", new Uri(rootSiteUrl + "/SitePages/Home.aspx"), "", OfficeDevPnP.Core.Enums.NavigationType.TopNavigationBar);
                        web.AddNavigationNode("Document Centre", new Uri(rootSiteUrl + "/Document%20Centre/SitePages/Home.aspx"), "", OfficeDevPnP.Core.Enums.NavigationType.TopNavigationBar);
                        web.AddNavigationNode("Project Centre", new Uri(rootSiteUrl + "/Project%20Centre/SitePages/Home.aspx"), "", OfficeDevPnP.Core.Enums.NavigationType.TopNavigationBar);
                        web.AddNavigationNode("WHS Centre", new Uri(rootSiteUrl + "/WHS%20Centre/"), "", OfficeDevPnP.Core.Enums.NavigationType.TopNavigationBar);
                        web.AddNavigationNode("Training Centre", new Uri(rootSiteUrl + "/Training%20Centre"), "", OfficeDevPnP.Core.Enums.NavigationType.TopNavigationBar);
                        web.AddNavigationNode("Proposal Hub", new Uri(rootSiteUrl + "/Proposal%20Hub"), "", OfficeDevPnP.Core.Enums.NavigationType.TopNavigationBar);
                    }

                    UpdateListTitle(ctx, web, "01. Project Management");
                }
            }
            catch (Exception e)
            {
                log.Error("Error when applying PnP template!", e);
                throw;
            }
        }
        protected override void ExecuteCmdlet()
        {
            SelectedWeb.EnsureProperty(w => w.Url);
            bool templateFromFileSystem = !Path.ToLower().StartsWith("http");
            FileConnectorBase fileConnector;
            string            templateFileName = System.IO.Path.GetFileName(Path);

            if (templateFromFileSystem)
            {
                if (!System.IO.Path.IsPathRooted(Path))
                {
                    Path = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, Path);
                }
                if (!string.IsNullOrEmpty(ResourceFolder))
                {
                    if (!System.IO.Path.IsPathRooted(ResourceFolder))
                    {
                        ResourceFolder = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path,
                                                                ResourceFolder);
                    }
                }
                var fileInfo = new FileInfo(Path);
                fileConnector = new FileSystemConnector(fileInfo.DirectoryName, "");
            }
            else
            {
                Uri fileUri         = new Uri(Path);
                var webUrl          = Microsoft.SharePoint.Client.Web.WebUrlFromFolderUrlDirect(ClientContext, fileUri);
                var templateContext = ClientContext.Clone(webUrl.ToString());

                var library = Path.ToLower().Replace(templateContext.Url.ToLower(), "").TrimStart('/');
                var idx     = library.IndexOf("/", StringComparison.Ordinal);
                library = library.Substring(0, idx);

                // This syntax creates a SharePoint connector regardless we have the -InputInstance argument or not
                fileConnector = new SharePointConnector(templateContext, templateContext.Url, library);
            }

            ProvisioningTemplate provisioningTemplate;

            // If we don't have the -InputInstance parameter, we load the template from the source connector
            if (InputInstance == null)
            {
                Stream stream           = fileConnector.GetFileStream(templateFileName);
                var    isOpenOfficeFile = IsOpenOfficeFile(stream);
                XMLTemplateProvider provider;
                if (isOpenOfficeFile)
                {
                    provider         = new XMLOpenXMLTemplateProvider(new OpenXMLConnector(templateFileName, fileConnector));
                    templateFileName = templateFileName.Substring(0, templateFileName.LastIndexOf(".", StringComparison.Ordinal)) + ".xml";
                }
                else
                {
                    if (templateFromFileSystem)
                    {
                        provider = new XMLFileSystemTemplateProvider(fileConnector.Parameters[FileConnectorBase.CONNECTIONSTRING] + "", "");
                    }
                    else
                    {
                        throw new NotSupportedException("Only .pnp package files are supported from a SharePoint library");
                    }
                }
                provisioningTemplate = provider.GetTemplate(templateFileName, TemplateProviderExtensions);

                if (provisioningTemplate == null)
                {
                    // If we don't have the template, raise an error and exit
                    WriteError(new ErrorRecord(new Exception("The -Path parameter targets an invalid repository or template object."), "WRONG_PATH", ErrorCategory.SyntaxError, null));
                    return;
                }

                if (isOpenOfficeFile)
                {
                    provisioningTemplate.Connector = provider.Connector;
                }
                else
                {
                    if (ResourceFolder != null)
                    {
                        var fileSystemConnector = new FileSystemConnector(ResourceFolder, "");
                        provisioningTemplate.Connector = fileSystemConnector;
                    }
                    else
                    {
                        provisioningTemplate.Connector = provider.Connector;
                    }
                }
            }
            // Otherwise we use the provisioning template instance provided through the -InputInstance parameter
            else
            {
                provisioningTemplate = InputInstance;

                if (ResourceFolder != null)
                {
                    var fileSystemConnector = new FileSystemConnector(ResourceFolder, "");
                    provisioningTemplate.Connector = fileSystemConnector;
                }
                else
                {
                    provisioningTemplate.Connector = fileConnector;
                }
            }

            if (Parameters != null)
            {
                foreach (var parameter in Parameters.Keys)
                {
                    if (provisioningTemplate.Parameters.ContainsKey(parameter.ToString()))
                    {
                        provisioningTemplate.Parameters[parameter.ToString()] = Parameters[parameter].ToString();
                    }
                    else
                    {
                        provisioningTemplate.Parameters.Add(parameter.ToString(), Parameters[parameter].ToString());
                    }
                }
            }

            var applyingInformation = new ProvisioningTemplateApplyingInformation();

            if (MyInvocation.BoundParameters.ContainsKey("Handlers"))
            {
                applyingInformation.HandlersToProcess = Handlers;
            }
            if (MyInvocation.BoundParameters.ContainsKey("ExcludeHandlers"))
            {
                foreach (var handler in (Handlers[])Enum.GetValues(typeof(Handlers)))
                {
                    if (!ExcludeHandlers.Has(handler) && handler != Handlers.All)
                    {
                        Handlers = Handlers | handler;
                    }
                }
                applyingInformation.HandlersToProcess = Handlers;
            }

            if (ExtensibilityHandlers != null)
            {
                applyingInformation.ExtensibilityHandlers = ExtensibilityHandlers.ToList();
            }

            applyingInformation.ProgressDelegate = (message, step, total) =>
            {
                WriteProgress(new ProgressRecord(0, $"Applying template to {SelectedWeb.Url}", message)
                {
                    PercentComplete = (100 / total) * step
                });
            };

            applyingInformation.MessagesDelegate = (message, type) =>
            {
                if (type == ProvisioningMessageType.Warning)
                {
                    WriteWarning(message);
                }
            };

            applyingInformation.OverwriteSystemPropertyBagValues = OverwriteSystemPropertyBagValues;
            SelectedWeb.ApplyProvisioningTemplate(provisioningTemplate, applyingInformation);

            WriteProgress(new ProgressRecord(0, $"Applying template to {SelectedWeb.Url}", " ")
            {
                RecordType = ProgressRecordType.Completed
            });
        }
Ejemplo n.º 6
0
        public ProvisioningTemplateApplyingInformation ToApplyingInformation()
        {
            var ai = new ProvisioningTemplateApplyingInformation
            {
                ApplyConfiguration = this
            };

            if (this.AccessTokens != null && this.AccessTokens.Any())
            {
                ai.AccessTokens = this.AccessTokens;
            }

            ai.ProvisionContentTypesToSubWebs   = this.ContentTypes.ProvisionContentTypesToSubWebs;
            ai.OverwriteSystemPropertyBagValues = this.PropertyBag.OverwriteSystemValues;
            ai.IgnoreDuplicateDataRowErrors     = this.Lists.IgnoreDuplicateDataRowErrors;
            ai.ClearNavigation          = this.Navigation.ClearNavigation;
            ai.ProvisionFieldsToSubWebs = this.Fields.ProvisionFieldsToSubWebs;

            if (Handlers.Any())
            {
                ai.HandlersToProcess = Model.Handlers.None;
                foreach (var handler in Handlers)
                {
                    Model.Handlers handlerEnumValue = Model.Handlers.None;
                    switch (handler)
                    {
                    case ConfigurationHandler.Pages:
                        handlerEnumValue = Model.Handlers.Pages
                                           | Model.Handlers.PageContents;
                        break;

                    case ConfigurationHandler.Taxonomy:
                        handlerEnumValue = Model.Handlers.TermGroups;
                        break;

                    default:
                        handlerEnumValue = (Model.Handlers)Enum.Parse(typeof(Model.Handlers), handler.ToString());
                        break;
                    }
                    ai.HandlersToProcess |= handlerEnumValue;
                }
            }
            else
            {
                ai.HandlersToProcess = Model.Handlers.All;
            }

            if (this.ProgressDelegate != null)
            {
                ai.ProgressDelegate = (message, step, total) =>
                {
                    ProgressDelegate(message, step, total);
                };
            }
            if (this.MessagesDelegate != null)
            {
                ai.MessagesDelegate = (message, type) =>
                {
                    MessagesDelegate(message, type);
                };
            }
            if (this.SiteProvisionedDelegate != null)
            {
                ai.SiteProvisionedDelegate = (title, siteUrl) =>
                {
                    SiteProvisionedDelegate(title, siteUrl);
                };
            }

            return(ai);
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Method to Invoke Custom Provisioning Handlers.
        /// </summary>
        /// <remarks>
        /// Ensure the ClientContext is not disposed in the custom provider.
        /// </remarks>
        /// <param name="ctx">Authenticated ClientContext that is passed to the custom provider.</param>
        /// <param name="handler">A custom Extensibility Provisioning Provider</param>
        /// <param name="template">ProvisioningTemplate that is passed to the custom provider</param>
        /// <param name="applyingInformation">The Provisioning Template application information object</param>
        /// <param name="tokenParser">The Token Parser used by the engine during template provisioning</param>
        /// <param name="scope">The PnPMonitoredScope of the current step in the pipeline</param>
        /// <exception cref="ExtensiblityPipelineException"></exception>
        /// <exception cref="ArgumentException">Provider.Assembly or Provider.Type is NullOrWhiteSpace></exception>
        /// <exception cref="ArgumentNullException">ClientContext is Null></exception>
        public void ExecuteExtensibilityProvisionCallOut(ClientContext ctx, ExtensibilityHandler handler,
                                                         ProvisioningTemplate template, ProvisioningTemplateApplyingInformation applyingInformation,
                                                         TokenParser tokenParser, PnPMonitoredScope scope)
        {
            var _loggingSource = "OfficeDevPnP.Core.Framework.Provisioning.Extensibility.ExtensibilityManager.ExecuteCallout";

            if (ctx == null)
            {
                throw new ArgumentNullException(nameof(ctx), CoreResources.Provisioning_Extensibility_Pipeline_ClientCtxNull);
            }

            if (string.IsNullOrWhiteSpace(handler.Assembly))
            {
                throw new ArgumentException(String.Format("{0}.{1}", nameof(handler), nameof(handler.Type)), CoreResources.Provisioning_Extensibility_Pipeline_Missing_AssemblyName);
            }

            if (string.IsNullOrWhiteSpace(handler.Type))
            {
                throw new ArgumentException(String.Format("{0}.{1}", nameof(handler), nameof(handler.Type)), CoreResources.Provisioning_Extensibility_Pipeline_Missing_TypeName);
            }

            try
            {
                var _instance = GetProviderInstance(handler);
#pragma warning disable 618
                if (_instance is IProvisioningExtensibilityProvider)
#pragma warning restore 618
                {
                    Log.Info(_loggingSource,
                             CoreResources.Provisioning_Extensibility_Pipeline_BeforeInvocation,
                             handler.Assembly,
                             handler.Type);

#pragma warning disable 618
                    (_instance as IProvisioningExtensibilityProvider).ProcessRequest(ctx, template, handler.Configuration);
#pragma warning restore 618

                    Log.Info(_loggingSource,
                             CoreResources.Provisioning_Extensibility_Pipeline_Success,
                             handler.Assembly,
                             handler.Type);
                }
                else if (_instance is IProvisioningExtensibilityHandler)
                {
                    Log.Info(_loggingSource,
                             CoreResources.Provisioning_Extensibility_Pipeline_BeforeInvocation,
                             handler.Assembly,
                             handler.Type);

                    (_instance as IProvisioningExtensibilityHandler).Provision(ctx, template, applyingInformation, tokenParser, scope, handler.Configuration);

                    Log.Info(_loggingSource,
                             CoreResources.Provisioning_Extensibility_Pipeline_Success,
                             handler.Assembly,
                             handler.Type);
                }
                else if (_instance != null && !(_instance is IProvisioningExtensibilityTokenProvider))
                {
                    throw new ArgumentOutOfRangeException(nameof(handler), string.Format(CoreResources.Provisioning_Extensibility_Invalid_Handler_Implementation, this.GetType().Assembly.GetName().Version.ToString(), handler.Assembly, handler.Type));
                }
            }
            catch (Exception ex)
            {
                Log.Error(_loggingSource, CoreResources.Provisioning_Extensibility_Pipeline_Exception,
                          handler.Assembly,
                          handler.Type,
                          ex);

                string _message = string.Format(
                    CoreResources.Provisioning_Extensibility_Pipeline_Exception,
                    handler.Assembly,
                    handler.Type,
                    ex);

                throw new ExtensiblityPipelineException(_message, ex);
            }
        }
Ejemplo n.º 8
0
        public static async Task RunAsync([ServiceBusTrigger("actions", IsSessionsEnabled = false)] ProvisioningActionModel action, ILogger logger)
        {
            var startProvisioning = DateTime.Now;

            String provisioningEnvironment = ConfigurationManager.AppSettings["SPPA:ProvisioningEnvironment"];

            logger.LogInformationWithPnPCorrelation("Processing queue trigger function for tenant {TenantId}", action.CorrelationId, action.TenantId);

            // Instantiate and use the telemetry model
            TelemetryUtility telemetry = new TelemetryUtility((s) => {
                logger.LogInformationWithPnPCorrelation(s, action.CorrelationId);
            });
            Dictionary <string, string> telemetryProperties = new Dictionary <string, string>();

            // Configure telemetry properties
            // telemetryProperties.Add("UserPrincipalName", action.UserPrincipalName);
            telemetryProperties.Add("TenantId", action.TenantId);
            telemetryProperties.Add("PnPCorrelationId", action.CorrelationId.ToString());
            telemetryProperties.Add("TargetSiteAlreadyExists", action.TargetSiteAlreadyExists.ToString());
            telemetryProperties.Add("TargetSiteBaseTemplateId", action.TargetSiteBaseTemplateId);

            // Get a reference to the data context
            ProvisioningAppDBContext dbContext = new ProvisioningAppDBContext();

            try
            {
                // Log telemetry event
                telemetry?.LogEvent("ProvisioningFunction.Start");

                if (CheckIfActionIsAlreadyRunning(action, dbContext))
                {
                    throw new ConcurrentProvisioningException("The requested package is currently provisioning in the selected target tenant and cannot be applied in parallel. Please wait for the previous provisioning action to complete.");
                }

                var tokenId = $"{action.TenantId}-{action.UserPrincipalName.ToLower().GetHashCode()}-{action.ActionType.ToString().ToLower()}-{provisioningEnvironment}";

                // Retrieve the SPO target tenant via Microsoft Graph
                var graphAccessToken = await ProvisioningAppManager.AccessTokenProvider.GetAccessTokenAsync(
                    tokenId, "https://graph.microsoft.com/",
                    ConfigurationManager.AppSettings[$"{action.ActionType}:ClientId"],
                    ConfigurationManager.AppSettings[$"{action.ActionType}:ClientSecret"],
                    ConfigurationManager.AppSettings[$"{action.ActionType}:AppUrl"]);

                logger.LogInformationWithPnPCorrelation("Retrieved target Microsoft Graph Access Token.", action.CorrelationId);

                if (!String.IsNullOrEmpty(graphAccessToken))
                {
                    #region Get current context data (User, SPO Tenant, SPO Access Token)

                    // Get the currently connected user name and email (UPN)
                    var jwtAccessToken = new System.IdentityModel.Tokens.Jwt.JwtSecurityToken(graphAccessToken);

                    String delegatedUPN = String.Empty;
                    var    upnClaim     = jwtAccessToken.Claims.FirstOrDefault(c => c.Type == "upn");
                    if (upnClaim != null && !String.IsNullOrEmpty(upnClaim.Value))
                    {
                        delegatedUPN = upnClaim.Value;
                    }

                    String delegatedUserName = String.Empty;
                    var    nameClaim         = jwtAccessToken.Claims.FirstOrDefault(c => c.Type == "name");
                    if (nameClaim != null && !String.IsNullOrEmpty(nameClaim.Value))
                    {
                        delegatedUserName = nameClaim.Value;
                    }

                    // Determine the URL of the root SPO site for the current tenant
                    var            rootSiteJson = HttpHelper.MakeGetRequestForString("https://graph.microsoft.com/v1.0/sites/root", graphAccessToken);
                    SharePointSite rootSite     = JsonConvert.DeserializeObject <SharePointSite>(rootSiteJson);

                    String spoTenant = rootSite.WebUrl;

                    logger.LogInformationWithPnPCorrelation("Target SharePoint Online Tenant: {SPOTenant}", action.CorrelationId, spoTenant);

                    // Configure telemetry properties
                    telemetryProperties.Add("SPOTenant", spoTenant);

                    // Retrieve the SPO Access Token
                    var spoAccessToken = await ProvisioningAppManager.AccessTokenProvider.GetAccessTokenAsync(
                        tokenId, rootSite.WebUrl,
                        ConfigurationManager.AppSettings[$"{action.ActionType}:ClientId"],
                        ConfigurationManager.AppSettings[$"{action.ActionType}:ClientSecret"],
                        ConfigurationManager.AppSettings[$"{action.ActionType}:AppUrl"]);

                    logger.LogInformationWithPnPCorrelation("Retrieved target SharePoint Online Access Token.", action.CorrelationId);

                    #endregion

                    // Connect to SPO, create and provision site
                    AuthenticationManager authManager = new AuthenticationManager();
                    using (ClientContext context = authManager.GetAzureADAccessTokenAuthenticatedContext(spoTenant, spoAccessToken))
                    {
                        // Telemetry and startup
                        var web = context.Web;
                        context.ClientTag = $"SPDev:ProvisioningPortal-{provisioningEnvironment}";
                        context.Load(web, w => w.Title, w => w.Id);
                        await context.ExecuteQueryAsync();

                        // Save the current SPO Correlation ID
                        telemetryProperties.Add("SPOCorrelationId", context.TraceCorrelationId);

                        logger.LogInformationWithPnPCorrelation("SharePoint Online Root Site Collection title: {WebTitle}", action.CorrelationId, web.Title);

                        #region Store the main site URL in KeyVault

                        // Store the main site URL in the Vault
                        var vault = ProvisioningAppManager.SecurityTokensServiceProvider;

                        // Read any existing properties for the current tenantId
                        var properties = await vault.GetAsync(tokenId);

                        if (properties == null)
                        {
                            // If there are no properties, create a new dictionary
                            properties = new Dictionary <String, String>();
                        }

                        // Set/Update the RefreshToken value
                        properties["SPORootSite"] = spoTenant;

                        // Add or Update the Key Vault accordingly
                        await vault.AddOrUpdateAsync(tokenId, properties);

                        #endregion

                        #region Provision the package

                        var package = dbContext.Packages.FirstOrDefault(p => p.Id == new Guid(action.PackageId));

                        if (package != null)
                        {
                            // Update the Popularity of the package
                            package.TimesApplied++;
                            dbContext.SaveChanges();

                            #region Get the Provisioning Hierarchy file

                            // Determine reference path variables
                            var blobConnectionString = ConfigurationManager.AppSettings["BlobTemplatesProvider:ConnectionString"];
                            var blobContainerName    = ConfigurationManager.AppSettings["BlobTemplatesProvider:ContainerName"];

                            var packageFileName           = package.PackageUrl.Substring(package.PackageUrl.LastIndexOf('/') + 1);
                            var packageFileUri            = new Uri(package.PackageUrl);
                            var packageFileRelativePath   = packageFileUri.AbsolutePath.Substring(2 + blobContainerName.Length);
                            var packageFileRelativeFolder = packageFileRelativePath.Substring(0, packageFileRelativePath.LastIndexOf('/'));

                            // Configure telemetry properties
                            telemetryProperties.Add("PackageFileName", packageFileName);
                            telemetryProperties.Add("PackageFileUri", packageFileUri.ToString());

                            // Read the main provisioning file from the Blob Storage
                            CloudStorageAccount csa;
                            if (!CloudStorageAccount.TryParse(blobConnectionString, out csa))
                            {
                                throw new ArgumentException("Cannot create cloud storage account from given connection string.");
                            }

                            CloudBlobClient    blobClient    = csa.CreateCloudBlobClient();
                            CloudBlobContainer blobContainer = blobClient.GetContainerReference(blobContainerName);

                            var blockBlob = blobContainer.GetBlockBlobReference(packageFileRelativePath);

                            // Crate an in-memory copy of the source stream
                            MemoryStream mem = new MemoryStream();
                            await blockBlob.DownloadToStreamAsync(mem);

                            mem.Position = 0;

                            // Prepare the output hierarchy
                            ProvisioningHierarchy hierarchy = null;

                            if (packageFileName.EndsWith(".xml", StringComparison.InvariantCultureIgnoreCase))
                            {
                                // That's an XML Provisioning Template file

                                XDocument xml = XDocument.Load(mem);
                                mem.Position = 0;

                                // Deserialize the stream into a provisioning hierarchy reading any
                                // dependecy with the Azure Blob Storage connector
                                var formatter           = XMLPnPSchemaFormatter.GetSpecificFormatter(xml.Root.Name.NamespaceName);
                                var templateLocalFolder = $"{blobContainerName}/{packageFileRelativeFolder}";

                                var provider = new XMLAzureStorageTemplateProvider(
                                    blobConnectionString,
                                    templateLocalFolder);
                                formatter.Initialize(provider);

                                // Get the full hierarchy
                                hierarchy           = ((IProvisioningHierarchyFormatter)formatter).ToProvisioningHierarchy(mem);
                                hierarchy.Connector = provider.Connector;
                            }
                            else if (packageFileName.EndsWith(".pnp", StringComparison.InvariantCultureIgnoreCase))
                            {
                                // That's a PnP Package file

                                // Get a provider based on the in-memory .PNP Open XML file
                                OpenXMLConnector    openXmlConnector = new OpenXMLConnector(mem);
                                XMLTemplateProvider provider         = new XMLOpenXMLTemplateProvider(
                                    openXmlConnector);

                                // Get the .xml provisioning template file name
                                var xmlTemplateFileName = openXmlConnector.Info?.Properties?.TemplateFileName ??
                                                          packageFileName.Substring(packageFileName.LastIndexOf('/') + 1)
                                                          .ToLower().Replace(".pnp", ".xml");

                                // Get the full hierarchy
                                hierarchy           = provider.GetHierarchy(xmlTemplateFileName);
                                hierarchy.Connector = provider.Connector;
                            }

                            #endregion

                            #region Apply the template

                            // Prepare variable to collect provisioned sites
                            var provisionedSites = new List <Tuple <String, String> >();

                            // If we have a hierarchy with at least one Sequence
                            if (hierarchy != null) // && hierarchy.Sequences != null && hierarchy.Sequences.Count > 0)
                            {
                                Console.WriteLine($"Provisioning hierarchy \"{hierarchy.DisplayName}\"");

                                var tenantUrl = UrlUtilities.GetTenantAdministrationUrl(context.Url);

                                // Retrieve the SPO Access Token
                                var spoAdminAccessToken = await ProvisioningAppManager.AccessTokenProvider.GetAccessTokenAsync(
                                    tokenId, tenantUrl,
                                    ConfigurationManager.AppSettings[$"{action.ActionType}:ClientId"],
                                    ConfigurationManager.AppSettings[$"{action.ActionType}:ClientSecret"],
                                    ConfigurationManager.AppSettings[$"{action.ActionType}:AppUrl"]);

                                logger.LogInformationWithPnPCorrelation("Retrieved target SharePoint Online Admin Center Access Token.", action.CorrelationId);

                                using (var tenantContext = authManager.GetAzureADAccessTokenAuthenticatedContext(tenantUrl, spoAdminAccessToken))
                                {
                                    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>();

                                        // Prepare logging for hierarchy application
                                        var ptai = new ProvisioningTemplateApplyingInformation();
                                        ptai.MessagesDelegate += delegate(string message, ProvisioningMessageType messageType)
                                        {
                                            logger.LogInformationWithPnPCorrelation($"{messageType} - {message.Replace("{", "{{").Replace("}", "}}")}", action.CorrelationId);
                                        };
                                        ptai.ProgressDelegate += delegate(string message, int step, int total)
                                        {
                                            logger.LogInformationWithPnPCorrelation($"{step:00}/{total:00} - {message.Replace("{", "{{").Replace("}", "}}")}", action.CorrelationId);
                                        };
                                        ptai.SiteProvisionedDelegate += delegate(string title, string url)
                                        {
                                            logger.LogInformationWithPnPCorrelation("Fully provisioned site '{SiteTitle}' with URL: {SiteUrl}", action.CorrelationId, title, url);
                                            var provisionedSite = new Tuple <string, string>(title, url);
                                            if (!provisionedSites.Contains(provisionedSite))
                                            {
                                                provisionedSites.Add(provisionedSite);
                                            }
                                        };

                                        // Configure the OAuth Access Tokens for the client context
                                        accessTokens.Add(new Uri(tenantUrl).Authority, spoAdminAccessToken);
                                        accessTokens.Add(new Uri(spoTenant).Authority, spoAccessToken);

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

                                        #region Theme handling

                                        // Process the graphical Theme
                                        if (action.ApplyTheme)
                                        {
                                            // If we don't have any custom Theme
                                            if (!action.ApplyCustomTheme)
                                            {
                                                // Associate the selected already existing Theme to all the sites of the hierarchy
                                                foreach (var sc in hierarchy.Sequences[0].SiteCollections)
                                                {
                                                    sc.Theme = action.SelectedTheme;
                                                    foreach (var s in sc.Sites)
                                                    {
                                                        UpdateChildrenSitesTheme(s, action.SelectedTheme);
                                                    }
                                                }
                                            }
                                        }

                                        #endregion

                                        // Configure provisioning parameters
                                        if (action.PackageProperties != null)
                                        {
                                            foreach (var key in action.PackageProperties.Keys)
                                            {
                                                if (hierarchy.Parameters.ContainsKey(key.ToString()))
                                                {
                                                    hierarchy.Parameters[key.ToString()] = action.PackageProperties[key].ToString();
                                                }
                                                else
                                                {
                                                    hierarchy.Parameters.Add(key.ToString(), action.PackageProperties[key].ToString());
                                                }

                                                // Configure telemetry properties
                                                telemetryProperties.Add($"PackageProperty.{key}", action.PackageProperties[key].ToString());
                                            }
                                        }

                                        // Log telemetry event
                                        telemetry?.LogEvent("ProvisioningFunction.BeginProvisioning", telemetryProperties);

                                        // Define a PnPProvisioningContext scope to share the security context across calls
                                        using (var pnpProvisioningContext = new PnPProvisioningContext(async(r, s) =>
                                        {
                                            if (accessTokens.ContainsKey(r))
                                            {
                                                // In this scenario we just use the dictionary of access tokens
                                                // in fact the overall operation for sure will take less than 1 hour
                                                return(await Task.FromResult(accessTokens[r]));
                                            }
                                            else
                                            {
                                                // Try to get a fresh new Access Token
                                                var token = await ProvisioningAppManager.AccessTokenProvider.GetAccessTokenAsync(
                                                    tokenId, $"https://{r}",
                                                    ConfigurationManager.AppSettings[$"{action.ActionType}:ClientId"],
                                                    ConfigurationManager.AppSettings[$"{action.ActionType}:ClientSecret"],
                                                    ConfigurationManager.AppSettings[$"{action.ActionType}:AppUrl"]);

                                                accessTokens.Add(r, token);

                                                return(token);
                                            }
                                        }))
                                        {
                                            // Configure the webhooks, if any
                                            if (action.Webhooks != null && action.Webhooks.Count > 0)
                                            {
                                                foreach (var t in hierarchy.Templates)
                                                {
                                                    foreach (var wh in action.Webhooks)
                                                    {
                                                        AddProvisioningTemplateWebhook(t, wh, ProvisioningTemplateWebhookKind.ProvisioningTemplateStarted);
                                                        AddProvisioningTemplateWebhook(t, wh, ProvisioningTemplateWebhookKind.ObjectHandlerProvisioningStarted);
                                                        AddProvisioningTemplateWebhook(t, wh, ProvisioningTemplateWebhookKind.ObjectHandlerProvisioningCompleted);
                                                        AddProvisioningTemplateWebhook(t, wh, ProvisioningTemplateWebhookKind.ProvisioningTemplateCompleted);
                                                        AddProvisioningTemplateWebhook(t, wh, ProvisioningTemplateWebhookKind.ExceptionOccurred);
                                                    }
                                                }

                                                foreach (var wh in action.Webhooks)
                                                {
                                                    AddProvisioningWebhook(hierarchy, wh, ProvisioningTemplateWebhookKind.ProvisioningStarted);
                                                    AddProvisioningWebhook(hierarchy, wh, ProvisioningTemplateWebhookKind.ProvisioningCompleted);
                                                    AddProvisioningWebhook(hierarchy, wh, ProvisioningTemplateWebhookKind.ProvisioningExceptionOccurred);
                                                }
                                            }

                                            // Disable the WebSettings handler for non-admin users
                                            if (!TenantExtensions.IsCurrentUserTenantAdmin(tenantContext))
                                            {
                                                ptai.HandlersToProcess &= ~Handlers.WebSettings;
                                            }

                                            // Apply the hierarchy
                                            logger.LogInformationWithPnPCorrelation("Hierarchy Provisioning Started: {ProvisioningStartDateTime}", action.CorrelationId, DateTime.Now.ToString("hh.mm.ss"));
                                            tenant.ApplyProvisionHierarchy(hierarchy,
                                                                           (hierarchy.Sequences != null && hierarchy.Sequences.Count > 0) ?
                                                                           hierarchy.Sequences[0].ID : null,
                                                                           ptai);
                                            logger.LogInformationWithPnPCorrelation("Hierarchy Provisioning Completed: {ProvisioningEndDateTime}", action.CorrelationId, DateTime.Now.ToString("hh.mm.ss"));
                                        }

                                        if (action.ApplyTheme && action.ApplyCustomTheme)
                                        {
                                            if (!String.IsNullOrEmpty(action.ThemePrimaryColor) &&
                                                !String.IsNullOrEmpty(action.ThemeBodyTextColor) &&
                                                !String.IsNullOrEmpty(action.ThemeBodyBackgroundColor))
                                            {
                                                logger.LogInformationWithPnPCorrelation("Applying custom Theme to provisioned sites", action.CorrelationId);

                                                #region Palette generation for Theme

                                                var jsonPalette = ThemeUtility.GetThemeAsJSON(
                                                    action.ThemePrimaryColor,
                                                    action.ThemeBodyTextColor,
                                                    action.ThemeBodyBackgroundColor);

                                                #endregion

                                                // Apply the custom theme to all of the provisioned sites
                                                foreach (var ps in provisionedSites)
                                                {
                                                    using (var provisionedSiteContext = authManager.GetAzureADAccessTokenAuthenticatedContext(ps.Item2, spoAccessToken))
                                                    {
                                                        if (provisionedSiteContext.Web.ApplyTheme(jsonPalette))
                                                        {
                                                            logger.LogInformationWithPnPCorrelation($"Custom Theme applied on site '{ps.Item1}' with URL: {ps.Item2}", action.CorrelationId);
                                                        }
                                                        else
                                                        {
                                                            logger.LogInformationWithPnPCorrelation($"Failed to apply custom Theme on site '{ps.Item1}' with URL: {ps.Item2}", action.CorrelationId);
                                                        }
                                                    }
                                                }
                                            }
                                        }

                                        // Log telemetry event
                                        telemetry?.LogEvent("ProvisioningFunction.EndProvisioning", telemetryProperties);

                                        // Notify user about the provisioning outcome
                                        if (!String.IsNullOrEmpty(action.NotificationEmail))
                                        {
                                            var appOnlyAccessToken = await ProvisioningAppManager.AccessTokenProvider.GetAppOnlyAccessTokenAsync(
                                                "https://graph.microsoft.com/",
                                                ConfigurationManager.AppSettings["OfficeDevPnP:TenantId"],
                                                ConfigurationManager.AppSettings["OfficeDevPnP:ClientId"],
                                                ConfigurationManager.AppSettings["OfficeDevPnP:ClientSecret"],
                                                ConfigurationManager.AppSettings["OfficeDevPnP:AppUrl"]);

                                            MailHandler.SendMailNotification(
                                                "ProvisioningCompleted",
                                                action.NotificationEmail,
                                                null,
                                                new
                                            {
                                                TemplateName     = action.DisplayName,
                                                ProvisionedSites = provisionedSites,
                                            },
                                                appOnlyAccessToken);
                                        }

                                        // Log reporting event (1 = Success)
                                        LogReporting(action, provisioningEnvironment, startProvisioning, package, 1);

                                        // Log source tracking for provisioned sites
                                        LogSourceTrackingProvisionedSites(action, spoAccessToken, authManager, provisionedSites);
                                    }
                                }
                            }
                            else
                            {
                                throw new ApplicationException($"The requested package does not contain a valid PnP Hierarchy!");
                            }

                            #endregion
                        }
                        else
                        {
                            throw new ApplicationException($"Cannot find the package with ID: {action.PackageId}");
                        }

                        #endregion

                        #region Process any children items

                        // If there are children items
                        if (action.ChildrenItems != null && action.ChildrenItems.Count > 0)
                        {
                            // Prepare any further child provisioning request
                            action.PackageId         = action.ChildrenItems[0].PackageId;
                            action.PackageProperties = action.ChildrenItems[0].Parameters;
                            action.ChildrenItems.RemoveAt(0);

                            // Enqueue any further child provisioning request
                            await ProvisioningAppManager.EnqueueProvisioningRequest(action);
                        }

                        #endregion

                        logger.LogInformationWithPnPCorrelation("Function successfully executed!", action.CorrelationId);
                        // Log telemetry event
                        telemetry?.LogEvent("ProvisioningFunction.End", telemetryProperties);
                    }
                }
                else
                {
                    var noTokensErrorMessage = $"Cannot retrieve Refresh Token or Access Token for action {action.CorrelationId} in tenant {action.TenantId}!";
                    logger.LogInformationWithPnPCorrelation(noTokensErrorMessage, action.CorrelationId);
                    throw new ApplicationException(noTokensErrorMessage);
                }
            }
            catch (Exception ex)
            {
                // Skip logging exception for Recycled Site
                if (ex is RecycledSiteException)
                {
                    // Log reporting event (3 = RecycledSite)
                    LogReporting(action, provisioningEnvironment, startProvisioning, null, 3, ex.ToDetailedString());

                    // rather log an event
                    telemetry?.LogEvent("ProvisioningFunction.RecycledSite", telemetryProperties);
                }
                // Skip logging exception for Concurrent Provisioning
                else if (ex is ConcurrentProvisioningException)
                {
                    // Log reporting event (4 = ConcurrentProvisioningException)
                    LogReporting(action, provisioningEnvironment, startProvisioning, null, 4, ex.ToDetailedString());

                    // rather log an event
                    telemetry?.LogEvent("ProvisioningFunction.ConcurrentProvisioning", telemetryProperties);
                }
                else
                {
                    // Log reporting event (2 = Failed)
                    LogReporting(action, provisioningEnvironment, startProvisioning, null, 2, ex.ToDetailedString());

                    // Log telemetry event
                    telemetry?.LogException(ex, "ProvisioningFunction.RunAsync", telemetryProperties);
                }

                if (!String.IsNullOrEmpty(action.NotificationEmail))
                {
                    var appOnlyAccessToken = await ProvisioningAppManager.AccessTokenProvider.GetAppOnlyAccessTokenAsync(
                        "https://graph.microsoft.com/",
                        ConfigurationManager.AppSettings["OfficeDevPnP:TenantId"],
                        ConfigurationManager.AppSettings["OfficeDevPnP:ClientId"],
                        ConfigurationManager.AppSettings["OfficeDevPnP:ClientSecret"],
                        ConfigurationManager.AppSettings["OfficeDevPnP:AppUrl"]);

                    // Notify user about the provisioning outcome
                    MailHandler.SendMailNotification(
                        "ProvisioningFailed",
                        action.NotificationEmail,
                        null,
                        new
                    {
                        TemplateName     = action.DisplayName,
                        ExceptionDetails = SimplifyException(ex),
                        PnPCorrelationId = action.CorrelationId.ToString(),
                    },
                        appOnlyAccessToken);
                }

                ProcessWebhooksExceptionNotification(action, ex);

                // Track the failure in the local action log
                MarkCurrentActionItemAsFailed(action, dbContext);

                throw ex;
            }
            finally
            {
                // Try to cleanup the pending action item, if any
                CleanupCurrentActionItem(action, dbContext);

                telemetry?.Flush();
            }
        }
Ejemplo n.º 9
0
        public override CanProvisionResult CanProvision(Web web, ProvisioningTemplate template, ProvisioningTemplateApplyingInformation applyingInformation)
        {
            // Prepare the default output
            var result = new CanProvisionResult();

            Model.ProvisioningTemplate targetTemplate = null;

            if (template.ParentHierarchy != null)
            {
                // If we have a hierarchy, search for a template with Taxonomy settings, if any
                targetTemplate = template.ParentHierarchy.Templates.FirstOrDefault(t => t.TermGroups.Count > 0);

                if (targetTemplate == null)
                {
                    // or use the first in the hierarchy
                    targetTemplate = template.ParentHierarchy.Templates[0];
                }
            }
            else
            {
                // Otherwise, use the provided template
                targetTemplate = template;
            }


            // Verify if we need the Term Store permissions (i.e. the template contains term groups to provision, or sequences with TermStore settings)
            if ((targetTemplate.TermGroups != null && targetTemplate.TermGroups?.Count > 0) ||
                targetTemplate.ParentHierarchy.Sequences.Any(
                    s => s.TermStore?.TermGroups != null && s.TermStore?.TermGroups?.Count > 0))
            {
                using (var scope = new PnPMonitoredScope(this.Name))
                {
                    try
                    {
                        // Try to access the Term Store
                        TaxonomySession taxSession = TaxonomySession.GetTaxonomySession(web.Context);
                        TermStore       termStore  = taxSession.GetDefaultKeywordsTermStore();
                        web.Context.Load(termStore,
                                         ts => ts.Languages,
                                         ts => ts.DefaultLanguage,
                                         ts => ts.Groups.Include(
                                             tg => tg.Name,
                                             tg => tg.Id,
                                             tg => tg.TermSets.Include(
                                                 tset => tset.Name,
                                                 tset => tset.Id)));
                        var siteCollectionTermGroup = termStore.GetSiteCollectionGroup((web.Context as ClientContext).Site, false);
                        web.Context.Load(siteCollectionTermGroup);
                        web.Context.ExecuteQueryRetry();

                        var termGroupId = Guid.NewGuid();
                        var group       = termStore.CreateGroup($"Temp-{termGroupId.ToString()}", termGroupId);
                        termStore.CommitAll();
                        web.Context.Load(group);
                        web.Context.ExecuteQueryRetry();

                        // Now delete the just created termGroup, to cleanup the Term Store
                        group.DeleteObject();
                        web.Context.ExecuteQueryRetry();
                    }
                    catch (Exception ex)
                    {
                        // And if we fail, raise a CanProvisionIssue
                        result.CanProvision = false;
                        result.Issues.Add(new CanProvisionIssue()
                        {
                            Source              = this.Name,
                            Tag                 = CanProvisionIssueTags.MISSING_TERMSTORE_PERMISSIONS,
                            Message             = CanProvisionIssuesMessages.Term_Store_Not_Admin,
                            ExceptionMessage    = ex.Message,    // Here we have a specific exception
                            ExceptionStackTrace = ex.StackTrace, // Here we have a specific exception
                        });
                    }
                }
            }
            return(result);
        }
Ejemplo n.º 10
0
 /// <summary>
 /// Can be used to apply custom remote provisioning template on top of existing site.
 /// </summary>
 /// <param name="web"></param>
 /// <param name="template">ProvisioningTemplate with the settings to be applied</param>
 /// <param name="applyingInformation">Specified additional settings and or properties</param>
 public static void ApplyProvisioningTemplate(this Web web, ProvisioningTemplate template, ProvisioningTemplateApplyingInformation applyingInformation = null)
 {
     // Call actual handler
     new SiteToTemplateConversion().ApplyRemoteTemplate(web, template, applyingInformation);
 }