public void CanProvisionHierarchy() { XMLTemplateProvider provider = new XMLFileSystemTemplateProvider( String.Format(@"{0}\..\..\Resources", AppDomain.CurrentDomain.BaseDirectory), "Templates"); var hierarchy = provider.GetHierarchy("ProvisioningSchema-2019-03-FullSample-01.xml"); CanProvisionResult result = null; using (var pnpContext = new PnPProvisioningContext()) { using (var tenantContext = TestCommon.CreateTenantClientContext()) { var applyingInformation = new ProvisioningTemplateApplyingInformation(); var tenant = new Tenant(tenantContext); result = CanProvisionRulesManager.CanProvision(tenant, hierarchy, String.Empty, applyingInformation); } } Assert.IsNotNull(result); Assert.IsTrue(result.CanProvision); }
public void CanProvisionSite() { if (TestCommon.AppOnlyTesting()) { Assert.Inconclusive("Template requires term store work, so this will not work in app-only"); } XMLTemplateProvider provider = new XMLFileSystemTemplateProvider( String.Format(@"{0}\..\..\Resources", AppDomain.CurrentDomain.BaseDirectory), "Templates"); var hierarchy = provider.GetHierarchy("ProvisioningSchema-2019-03-FullSample-01.xml"); CanProvisionResult result = null; using (var pnpContext = new PnPProvisioningContext()) { using (var context = TestCommon.CreateClientContext()) { var applyingInformation = new ProvisioningTemplateApplyingInformation(); result = CanProvisionRulesManager.CanProvision(context.Web, hierarchy.Templates[0], applyingInformation); } } Assert.IsNotNull(result); #if ONPREMISES // Because the "apps" rule is verified here Assert.IsFalse(result.CanProvision); #else Assert.IsTrue(result.CanProvision); #endif }
public void CanProvisionHierarchy() { if (TestCommon.AppOnlyTesting()) { Assert.Inconclusive("Template requires term store work, so this will not work in app-only"); } XMLTemplateProvider provider = new XMLFileSystemTemplateProvider( String.Format(@"{0}\..\..\..\Resources", AppDomain.CurrentDomain.BaseDirectory), "Templates"); var hierarchy = provider.GetHierarchy("ProvisioningSchema-2019-03-FullSample-01.xml"); var applyingInformation = new ProvisioningTemplateApplyingInformation(); if (TestCommon.AppOnlyTesting()) { bool templateSupportsAppOnly = true; if (applyingInformation.HandlersToProcess.Has(PnP.Framework.Provisioning.Model.Handlers.TermGroups) || applyingInformation.HandlersToProcess.Has(PnP.Framework.Provisioning.Model.Handlers.SearchSettings)) { if (hierarchy.Templates.Count > 0) { foreach (var template in hierarchy.Templates) { templateSupportsAppOnly = this.IsTemplateSupportedForAppOnly(template); if (!templateSupportsAppOnly) { break; } } } } if (!templateSupportsAppOnly) { Assert.Inconclusive("Taxonomy and SearchSettings tests are not supported when testing using app-only context."); } } CanProvisionResult result = null; using (var pnpContext = new PnPProvisioningContext()) { using (var tenantContext = TestCommon.CreateTenantClientContext()) { var tenant = new Tenant(tenantContext); result = CanProvisionRulesManager.CanProvision(tenant, hierarchy, String.Empty, applyingInformation); } } Assert.IsNotNull(result); Assert.IsTrue(result.CanProvision); Assert.IsTrue(result.CanProvision); }
public void CanProvisionSite() { if (TestCommon.AppOnlyTesting()) { Assert.Inconclusive("Template requires term store work, so this will not work in app-only"); } XMLTemplateProvider provider = new XMLFileSystemTemplateProvider( String.Format(@"{0}\..\..\Resources", AppDomain.CurrentDomain.BaseDirectory), "Templates"); var hierarchy = provider.GetHierarchy("ProvisioningSchema-2019-03-FullSample-01.xml"); var applyingInformation = new ProvisioningTemplateApplyingInformation(); var template = hierarchy.Templates[0]; if (TestCommon.AppOnlyTesting()) { if (applyingInformation.HandlersToProcess.Has(Core.Framework.Provisioning.Model.Handlers.TermGroups) || applyingInformation.HandlersToProcess.Has(Core.Framework.Provisioning.Model.Handlers.SearchSettings)) { bool templateSupportsAppOnly = this.IsTemplateSupportedForAppOnly(template); if (!templateSupportsAppOnly) { Assert.Inconclusive("Taxonomy and SearchSettings tests are not supported when testing using app-only context."); } } } CanProvisionResult result = null; using (var pnpContext = new PnPProvisioningContext()) { using (var context = TestCommon.CreateClientContext()) { result = CanProvisionRulesManager.CanProvision(context.Web, hierarchy.Templates[0], applyingInformation); } } Assert.IsNotNull(result); #if SP2013 || SP2016 // Because the "apps" rule is verified here Assert.IsFalse(result.CanProvision); #else Assert.IsTrue(result.CanProvision); #endif }
private void ExtractTemplate(XMLPnPSchemaVersion schema, string path, string packageName, ExtractConfiguration configuration) { CurrentWeb.EnsureProperty(w => w.Url); ProvisioningTemplateCreationInformation creationInformation = null; if (configuration != null) { creationInformation = configuration.ToCreationInformation(CurrentWeb); } else { creationInformation = new ProvisioningTemplateCreationInformation(CurrentWeb); } if (ParameterSpecified(nameof(Handlers))) { creationInformation.HandlersToProcess = Handlers; } if (ParameterSpecified(nameof(ExcludeHandlers))) { foreach (var handler in (Handlers[])Enum.GetValues(typeof(Handlers))) { if (!ExcludeHandlers.Has(handler) && handler != Handlers.All) { Handlers = Handlers | handler; } } creationInformation.HandlersToProcess = Handlers; } var extension = ""; if (packageName != null) { if (packageName.IndexOf(".", StringComparison.Ordinal) > -1) { extension = packageName.Substring(packageName.LastIndexOf(".", StringComparison.Ordinal)).ToLower(); } else { packageName += ".pnp"; extension = ".pnp"; } } var fileSystemConnector = new FileSystemConnector(path, ""); if (extension == ".pnp") { creationInformation.FileConnector = new OpenXMLConnector(packageName, fileSystemConnector); } else if (extension == ".md") { creationInformation.FileConnector = fileSystemConnector; } else { creationInformation.FileConnector = fileSystemConnector; } #pragma warning disable 618 if (ParameterSpecified(nameof(PersistBrandingFiles))) { creationInformation.PersistBrandingFiles = PersistBrandingFiles; } #pragma warning restore 618 creationInformation.PersistPublishingFiles = PersistPublishingFiles; creationInformation.IncludeNativePublishingFiles = IncludeNativePublishingFiles; if (ParameterSpecified(nameof(IncludeSiteGroups))) { creationInformation.IncludeSiteGroups = IncludeSiteGroups; } creationInformation.IncludeTermGroupsSecurity = IncludeTermGroupsSecurity; creationInformation.IncludeSearchConfiguration = IncludeSearchConfiguration; if (ParameterSpecified(nameof(IncludeHiddenLists))) { creationInformation.IncludeHiddenLists = IncludeHiddenLists; } if (ParameterSpecified(nameof(IncludeAllPages))) { creationInformation.IncludeAllClientSidePages = IncludeAllPages; } creationInformation.SkipVersionCheck = SkipVersionCheck; if (ParameterSpecified(nameof(ContentTypeGroups)) && ContentTypeGroups != null) { creationInformation.ContentTypeGroupsToInclude = ContentTypeGroups.ToList(); } creationInformation.PersistMultiLanguageResources = PersistMultiLanguageResources; if (extension == ".pnp") { // if file is of pnp format, persist all files creationInformation.PersistBrandingFiles = true; creationInformation.PersistPublishingFiles = true; creationInformation.PersistMultiLanguageResources = true; } if (!string.IsNullOrEmpty(ResourceFilePrefix)) { creationInformation.ResourceFilePrefix = ResourceFilePrefix; } else { if (Out != null) { FileInfo fileInfo = new FileInfo(Out); var prefix = fileInfo.Name; // strip extension, if there is any var indexOfLastDot = prefix.LastIndexOf(".", StringComparison.Ordinal); if (indexOfLastDot > -1) { prefix = prefix.Substring(0, indexOfLastDot); } creationInformation.ResourceFilePrefix = prefix; } } if (ExtensibilityHandlers != null) { creationInformation.ExtensibilityHandlers = ExtensibilityHandlers.ToList(); } #pragma warning disable CS0618 // Type or member is obsolete if (NoBaseTemplate) { creationInformation.BaseTemplate = null; } else { creationInformation.BaseTemplate = CurrentWeb.GetBaseTemplate(); } #pragma warning restore CS0618 // Type or member is obsolete creationInformation.ProgressDelegate = (message, step, total) => { var percentage = Convert.ToInt32((100 / Convert.ToDouble(total)) * Convert.ToDouble(step)); WriteProgress(new ProgressRecord(0, $"Extracting Template from {CurrentWeb.Url}", message) { PercentComplete = percentage }); WriteProgress(new ProgressRecord(1, " ", " ") { RecordType = ProgressRecordType.Completed }); }; creationInformation.MessagesDelegate = (message, type) => { switch (type) { case ProvisioningMessageType.Warning: { WriteWarning(message); break; } case ProvisioningMessageType.Progress: { var activity = message; if (message.IndexOf("|") > -1) { var messageSplitted = message.Split('|'); if (messageSplitted.Length == 4) { var current = double.Parse(messageSplitted[2]); var total = double.Parse(messageSplitted[3]); subProgressRecord.RecordType = ProgressRecordType.Processing; subProgressRecord.Activity = messageSplitted[0]; subProgressRecord.StatusDescription = messageSplitted[1]; subProgressRecord.PercentComplete = Convert.ToInt32((100 / total) * current); WriteProgress(subProgressRecord); } else { subProgressRecord.Activity = "Processing"; subProgressRecord.RecordType = ProgressRecordType.Processing; subProgressRecord.StatusDescription = activity; subProgressRecord.PercentComplete = 0; WriteProgress(subProgressRecord); } } else { subProgressRecord.Activity = "Processing"; subProgressRecord.RecordType = ProgressRecordType.Processing; subProgressRecord.StatusDescription = activity; subProgressRecord.PercentComplete = 0; WriteProgress(subProgressRecord); } break; } case ProvisioningMessageType.Completed: { WriteProgress(new ProgressRecord(1, message, " ") { RecordType = ProgressRecordType.Completed }); break; } } }; if (IncludeAllTermGroups) { creationInformation.IncludeAllTermGroups = true; } else { if (IncludeSiteCollectionTermGroup) { creationInformation.IncludeSiteCollectionTermGroup = true; } } if (ParameterSpecified(nameof(ExcludeContentTypesFromSyndication))) { creationInformation.IncludeContentTypesFromSyndication = !ExcludeContentTypesFromSyndication.ToBool(); } if (ListsToExtract != null && ListsToExtract.Count > 0) { creationInformation.ListsToExtract.AddRange(ListsToExtract); } ProvisioningTemplate template = null; using (var provisioningContext = new PnPProvisioningContext(async(resource, scope) => { return(await TokenRetrieval.GetAccessTokenAsync(resource, scope)); // if (resource.ToLower().StartsWith("https://")) // { // var uri = new Uri(resource); // resource = uri.Authority; // } // if (resource.ToLower().Contains(".sharepoint.")) // { // // SharePoint // var authManager = PnPConnection.CurrentConnection.Context.GetContextSettings().AuthenticationManager; // if (authManager != null) // { // var token = await authManager.GetAccessTokenAsync($"https://{resource}"); // if (token != null) // { // return token; // } // } // } // // Get Azure AD Token // if (PnPConnection.CurrentConnection != null) // { // var graphAccessToken = await PnPConnection.CurrentConnection.TryGetAccessTokenAsync(Enums.TokenAudience.MicrosoftGraph); // if (graphAccessToken != null) // { // // Authenticated using -Graph or using another way to retrieve the accesstoken with Connect-PnPOnline // return graphAccessToken; // } // } // if (PnPConnection.CurrentConnection.PSCredential != null) // { // // Using normal credentials // return await TokenHandler.AcquireTokenAsync(resource, null); // } // else // { // // No token... // if (resource.ToLower().Contains(".sharepoint.")) // { // return null; // } // else // { // throw new PSInvalidOperationException($"Your template contains artifacts that require an access token for {resource}. Either connect with a clientid which the appropriate permissions, or use credentials with Connect-PnPOnline after providing consent to the PnP Management Shell application first by executing: Register-PnPManagementShellAccess. See https://pnp.github.io/powershell/articles/authentication.html"); // } // } })) { template = CurrentWeb.GetProvisioningTemplate(creationInformation); } // Set metadata for template, if any SetTemplateMetadata(template, TemplateDisplayName, TemplateImagePreviewUrl, TemplateProperties); if (!OutputInstance) { var formatter = ProvisioningHelper.GetFormatter(schema); if (extension == ".pnp") { XMLTemplateProvider provider = new XMLOpenXMLTemplateProvider( creationInformation.FileConnector as OpenXMLConnector); var templateFileName = packageName.Substring(0, packageName.LastIndexOf(".", StringComparison.Ordinal)) + ".xml"; provider.SaveAs(template, templateFileName, formatter, TemplateProviderExtensions); } else if (extension == ".md") { WriteWarning("The generation of a markdown report is work in progress, it will improve/grow with later releases."); ITemplateFormatter mdFormatter = new MarkdownPnPFormatter(); using (var outputStream = mdFormatter.ToFormattedTemplate(template)) { using (var fileStream = File.Create(Path.Combine(path, packageName))) { outputStream.Seek(0, SeekOrigin.Begin); outputStream.CopyTo(fileStream); fileStream.Close(); } } } else { if (Out != null) { XMLTemplateProvider provider = new XMLFileSystemTemplateProvider(path, ""); provider.SaveAs(template, Path.Combine(path, packageName), formatter, TemplateProviderExtensions); } else { var outputStream = formatter.ToFormattedTemplate(template); var reader = new StreamReader(outputStream); WriteObject(reader.ReadToEnd()); } } } else { WriteObject(template); } }
protected override void ExecuteCmdlet() { var sitesProvisioned = new List <ProvisionedSite>(); var configuration = new ApplyConfiguration(); if (ParameterSpecified(nameof(Configuration))) { configuration = Configuration.GetConfiguration(SessionState.Path.CurrentFileSystemLocation.Path); } configuration.SiteProvisionedDelegate = (title, url) => { if (sitesProvisioned.FirstOrDefault(s => s.Url == url) == null) { sitesProvisioned.Add(new ProvisionedSite() { Title = title, Url = url }); } }; if (ParameterSpecified(nameof(Handlers))) { if (!Handlers.Has(Handlers.All)) { foreach (var enumValue in (Handlers[])Enum.GetValues(typeof(Handlers))) { if (Handlers.Has(enumValue)) { if (enumValue == Handlers.TermGroups) { configuration.Handlers.Add(ConfigurationHandler.Taxonomy); } else if (enumValue == Handlers.PageContents) { configuration.Handlers.Add(ConfigurationHandler.Pages); } else if (Enum.TryParse <ConfigurationHandler>(enumValue.ToString(), out ConfigurationHandler configHandler)) { configuration.Handlers.Add(configHandler); } } } } } if (ParameterSpecified(nameof(ExcludeHandlers))) { foreach (var handler in (Handlers[])Enum.GetValues(typeof(Handlers))) { if (!ExcludeHandlers.Has(handler) && handler != Handlers.All) { if (handler == Handlers.TermGroups) { if (configuration.Handlers.Contains(ConfigurationHandler.Taxonomy)) { configuration.Handlers.Remove(ConfigurationHandler.Taxonomy); } else if (Enum.TryParse <ConfigurationHandler>(handler.ToString(), out ConfigurationHandler configHandler)) { if (configuration.Handlers.Contains(configHandler)) { configuration.Handlers.Remove(configHandler); } } } } } } if (ExtensibilityHandlers != null) { configuration.Extensibility.Handlers = ExtensibilityHandlers.ToList(); } configuration.ProgressDelegate = (message, step, total) => { if (message != null) { var percentage = Convert.ToInt32((100 / Convert.ToDouble(total)) * Convert.ToDouble(step)); progressRecord.Activity = $"Applying template to tenant"; progressRecord.StatusDescription = message; progressRecord.PercentComplete = percentage; progressRecord.RecordType = ProgressRecordType.Processing; WriteProgress(progressRecord); } }; var warningsShown = new List <string>(); configuration.MessagesDelegate = (message, type) => { switch (type) { case ProvisioningMessageType.Warning: { if (!warningsShown.Contains(message)) { WriteWarning(message); warningsShown.Add(message); } break; } case ProvisioningMessageType.Progress: { if (message != null) { var activity = message; if (message.IndexOf("|") > -1) { var messageSplitted = message.Split('|'); if (messageSplitted.Length == 4) { var current = double.Parse(messageSplitted[2]); var total = double.Parse(messageSplitted[3]); subProgressRecord.RecordType = ProgressRecordType.Processing; subProgressRecord.Activity = string.IsNullOrEmpty(messageSplitted[0]) ? "-" : messageSplitted[0]; subProgressRecord.StatusDescription = string.IsNullOrEmpty(messageSplitted[1]) ? "-" : messageSplitted[1]; subProgressRecord.PercentComplete = Convert.ToInt32((100 / total) * current); WriteProgress(subProgressRecord); } else { subProgressRecord.Activity = "Processing"; subProgressRecord.RecordType = ProgressRecordType.Processing; subProgressRecord.StatusDescription = activity; subProgressRecord.PercentComplete = 0; WriteProgress(subProgressRecord); } } else { subProgressRecord.Activity = "Processing"; subProgressRecord.RecordType = ProgressRecordType.Processing; subProgressRecord.StatusDescription = activity; subProgressRecord.PercentComplete = 0; WriteProgress(subProgressRecord); } } break; } case ProvisioningMessageType.Completed: { WriteProgress(new ProgressRecord(1, message, " ") { RecordType = ProgressRecordType.Completed }); break; } } }; configuration.PropertyBag.OverwriteSystemValues = OverwriteSystemPropertyBagValues; configuration.Lists.IgnoreDuplicateDataRowErrors = IgnoreDuplicateDataRowErrors; configuration.Navigation.ClearNavigation = ClearNavigation; configuration.ContentTypes.ProvisionContentTypesToSubWebs = ProvisionContentTypesToSubWebs; configuration.Fields.ProvisionFieldsToSubWebs = ProvisionFieldsToSubWebs; ProvisioningHierarchy hierarchyToApply = null; switch (ParameterSetName) { case ParameterSet_PATH: { hierarchyToApply = GetHierarchy(); break; } case ParameterSet_OBJECT: { hierarchyToApply = Template; if (ResourceFolder != null) { var fileSystemConnector = new FileSystemConnector(ResourceFolder, ""); hierarchyToApply.Connector = fileSystemConnector; } else { if (Path != null) { if (!System.IO.Path.IsPathRooted(Path)) { Path = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, Path); } } else { Path = SessionState.Path.CurrentFileSystemLocation.Path; } var fileInfo = new FileInfo(Path); var fileConnector = new FileSystemConnector(fileInfo.DirectoryName, ""); hierarchyToApply.Connector = fileConnector; } break; } } if (Parameters != null) { foreach (var parameter in Parameters.Keys) { if (hierarchyToApply.Parameters.ContainsKey(parameter.ToString())) { hierarchyToApply.Parameters[parameter.ToString()] = Parameters[parameter].ToString(); } else { hierarchyToApply.Parameters.Add(parameter.ToString(), Parameters[parameter].ToString()); } } } #if !ONPREMISES // check if consent is needed and in place var consentRequired = false; if (hierarchyToApply.Teams != null) { consentRequired = true; } if (hierarchyToApply.AzureActiveDirectory != null) { consentRequired = true; } if (consentRequired) { // try to retrieve an access token for the Microsoft Graph: var accessToken = PnPConnection.CurrentConnection.TryGetAccessToken(Enums.TokenAudience.MicrosoftGraph); if (accessToken == null) { if (PnPConnection.CurrentConnection.PSCredential != null) { // Using normal credentials accessToken = TokenHandler.AcquireToken("graph.microsoft.com", null); } if (accessToken == null) { throw new PSInvalidOperationException("Your template contains artifacts that require an access token. Please provide consent to the PnP Management Shell application first by executing: Connect-PnPOnline -Graph -LaunchBrowser"); } } } using (var provisioningContext = new PnPProvisioningContext((resource, scope) => { if (resource.ToLower().StartsWith("https://")) { var uri = new Uri(resource); resource = uri.Authority; } // Get Azure AD Token if (PnPConnection.CurrentConnection != null) { if (resource.Equals("graph.microsoft.com", StringComparison.OrdinalIgnoreCase)) { var graphAccessToken = PnPConnection.CurrentConnection.TryGetAccessToken(Enums.TokenAudience.MicrosoftGraph); if (graphAccessToken != null) { // Authenticated using -Graph or using another way to retrieve the accesstoken with Connect-PnPOnline return(Task.FromResult(graphAccessToken)); } } } if (PnPConnection.CurrentConnection.PSCredential != null) { // Using normal credentials return(Task.FromResult(TokenHandler.AcquireToken(resource, null))); } else { // No token... throw new PSInvalidOperationException("Your template contains artifacts that require an access token. Please provide consent to the PnP Management Shell application first by executing: Connect-PnPOnline -Graph -LaunchBrowser"); } })) { #endif if (!string.IsNullOrEmpty(SequenceId)) { Tenant.ApplyTenantTemplate(hierarchyToApply, SequenceId, configuration); } else { if (hierarchyToApply.Sequences.Count > 0) { foreach (var sequence in hierarchyToApply.Sequences) { Tenant.ApplyTenantTemplate(hierarchyToApply, sequence.ID, configuration); } } else { Tenant.ApplyTenantTemplate(hierarchyToApply, null, configuration); } } #if !ONPREMISES } #endif WriteObject(sitesProvisioned, true); }
private async Task <CanProvisionResult> CanProvisionInternal(CanProvisionModel model) { var canProvisionResult = new CanProvisionResult(); 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 = dbContext; 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) { // 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); }
private ProvisioningHierarchy ExtractTemplate(ExtractConfiguration configuration) { configuration.ProgressDelegate = (message, step, total) => { var percentage = Convert.ToInt32((100 / Convert.ToDouble(total)) * Convert.ToDouble(step)); WriteProgress(new ProgressRecord(0, $"Extracting Tenant Template", message) { PercentComplete = percentage }); WriteProgress(new ProgressRecord(1, " ", " ") { RecordType = ProgressRecordType.Completed }); }; configuration.MessagesDelegate = (message, type) => { switch (type) { case ProvisioningMessageType.Warning: { WriteWarning(message); break; } case ProvisioningMessageType.Progress: { var activity = message; if (message.IndexOf("|") > -1) { var messageSplitted = message.Split('|'); if (messageSplitted.Length == 4) { var current = double.Parse(messageSplitted[2]); var total = double.Parse(messageSplitted[3]); subProgressRecord.RecordType = ProgressRecordType.Processing; subProgressRecord.Activity = messageSplitted[0]; subProgressRecord.StatusDescription = messageSplitted[1]; subProgressRecord.PercentComplete = Convert.ToInt32((100 / total) * current); WriteProgress(subProgressRecord); } else { subProgressRecord.Activity = "Processing"; subProgressRecord.RecordType = ProgressRecordType.Processing; subProgressRecord.StatusDescription = activity; subProgressRecord.PercentComplete = 0; WriteProgress(subProgressRecord); } } else { subProgressRecord.Activity = "Processing"; subProgressRecord.RecordType = ProgressRecordType.Processing; subProgressRecord.StatusDescription = activity; subProgressRecord.PercentComplete = 0; WriteProgress(subProgressRecord); } break; } case ProvisioningMessageType.Completed: { WriteProgress(new ProgressRecord(1, message, " ") { RecordType = ProgressRecordType.Completed }); break; } } }; using (var provisioningContext = new PnPProvisioningContext((resource, scope) => { // Get Azure AD Token if (PnPConnection.CurrentConnection != null) { var graphAccessToken = PnPConnection.CurrentConnection.TryGetAccessToken(Enums.TokenAudience.MicrosoftGraph); if (graphAccessToken != null) { // Authenticated using -Graph or using another way to retrieve the accesstoken with Connect-PnPOnline return(Task.FromResult(graphAccessToken)); } } if (PnPConnection.CurrentConnection.PSCredential != null) { // Using normal credentials return(Task.FromResult(TokenHandler.AcquireToken(resource, null))); } else { // No token... return(null); } })) { return(Tenant.GetTenantTemplate(configuration)); } }
protected override void ExecuteCmdlet() { SelectedWeb.EnsureProperty(w => w.Url); ProvisioningTemplate provisioningTemplate; FileConnectorBase fileConnector; if (ParameterSpecified(nameof(Path))) { bool templateFromFileSystem = !Path.ToLower().StartsWith("http"); 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 (!System.IO.File.Exists(Path)) { throw new FileNotFoundException($"File not found"); } 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); } // If we don't have the -InputInstance parameter, we load the template from the source connector Stream stream = fileConnector.GetFileStream(templateFileName); var isOpenOfficeFile = FileUtilities.IsOpenOfficeFile(stream); XMLTemplateProvider provider; if (isOpenOfficeFile) { var openXmlConnector = new OpenXMLConnector(templateFileName, fileConnector); provider = new XMLOpenXMLTemplateProvider(openXmlConnector); if (!String.IsNullOrEmpty(openXmlConnector.Info?.Properties?.TemplateFileName)) { templateFileName = openXmlConnector.Info.Properties.TemplateFileName; } else { 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"); } } if (ParameterSpecified(nameof(TemplateId))) { provisioningTemplate = provider.GetTemplate(templateFileName, TemplateId, null, TemplateProviderExtensions); } else { 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; } } } else { provisioningTemplate = InputInstance; if (ResourceFolder != null) { var fileSystemConnector = new FileSystemConnector(ResourceFolder, ""); provisioningTemplate.Connector = fileSystemConnector; } else { if (Path != null) { if (!System.IO.Path.IsPathRooted(Path)) { Path = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, Path); } } else { Path = SessionState.Path.CurrentFileSystemLocation.Path; } var fileInfo = new FileInfo(Path); fileConnector = new FileSystemConnector(System.IO.Path.IsPathRooted(fileInfo.FullName) ? fileInfo.FullName : fileInfo.DirectoryName, ""); 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 (ParameterSpecified(nameof(Handlers))) { applyingInformation.HandlersToProcess = Handlers; } if (ParameterSpecified(nameof(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) => { if (message != null) { var percentage = Convert.ToInt32((100 / Convert.ToDouble(total)) * Convert.ToDouble(step)); progressRecord.Activity = $"Applying template to {SelectedWeb.Url}"; progressRecord.StatusDescription = message; progressRecord.PercentComplete = percentage; progressRecord.RecordType = ProgressRecordType.Processing; WriteProgress(progressRecord); } }; var warningsShown = new List <string>(); applyingInformation.MessagesDelegate = (message, type) => { switch (type) { case ProvisioningMessageType.Warning: { if (!warningsShown.Contains(message)) { WriteWarning(message); warningsShown.Add(message); } break; } case ProvisioningMessageType.Progress: { if (message != null) { var activity = message; if (message.IndexOf("|") > -1) { var messageSplitted = message.Split('|'); if (messageSplitted.Length == 4) { var current = double.Parse(messageSplitted[2]); var total = double.Parse(messageSplitted[3]); subProgressRecord.RecordType = ProgressRecordType.Processing; subProgressRecord.Activity = string.IsNullOrEmpty(messageSplitted[0]) ? "-" : messageSplitted[0]; subProgressRecord.StatusDescription = string.IsNullOrEmpty(messageSplitted[1]) ? "-" : messageSplitted[1]; subProgressRecord.PercentComplete = Convert.ToInt32((100 / total) * current); WriteProgress(subProgressRecord); } else { subProgressRecord.Activity = "Processing"; subProgressRecord.RecordType = ProgressRecordType.Processing; subProgressRecord.StatusDescription = activity; subProgressRecord.PercentComplete = 0; WriteProgress(subProgressRecord); } } else { subProgressRecord.Activity = "Processing"; subProgressRecord.RecordType = ProgressRecordType.Processing; subProgressRecord.StatusDescription = activity; subProgressRecord.PercentComplete = 0; WriteProgress(subProgressRecord); } } break; } case ProvisioningMessageType.Completed: { WriteProgress(new ProgressRecord(1, message, " ") { RecordType = ProgressRecordType.Completed }); break; } } }; applyingInformation.OverwriteSystemPropertyBagValues = OverwriteSystemPropertyBagValues; applyingInformation.IgnoreDuplicateDataRowErrors = IgnoreDuplicateDataRowErrors; applyingInformation.ClearNavigation = ClearNavigation; applyingInformation.ProvisionContentTypesToSubWebs = ProvisionContentTypesToSubWebs; applyingInformation.ProvisionFieldsToSubWebs = ProvisionFieldsToSubWebs; #if !ONPREMISES using (var provisioningContext = new PnPProvisioningContext((resource, scope) => { // Get Azure AD Token if (PnPConnection.CurrentConnection != null) { var graphAccessToken = PnPConnection.CurrentConnection.TryGetAccessToken(Enums.TokenAudience.MicrosoftGraph); if (graphAccessToken != null) { // Authenticated using -Graph or using another way to retrieve the accesstoken with Connect-PnPOnline return(Task.FromResult(graphAccessToken)); } } if (PnPConnection.CurrentConnection.PSCredential != null) { // Using normal credentials return(Task.FromResult(TokenHandler.AcquireToken(resource, null))); } else { // No token... return(null); } })) { #endif SelectedWeb.ApplyProvisioningTemplate(provisioningTemplate, applyingInformation); #if !ONPREMISES } #endif WriteProgress(new ProgressRecord(0, $"Applying template to {SelectedWeb.Url}", " ") { RecordType = ProgressRecordType.Completed }); }
private void ExtractTemplate(XMLPnPSchemaVersion schema, string path, string packageName, ExtractConfiguration configuration) { SelectedWeb.EnsureProperty(w => w.Url); ProvisioningTemplateCreationInformation creationInformation = null; if (configuration != null) { creationInformation = configuration.ToCreationInformation(SelectedWeb); } else { creationInformation = new ProvisioningTemplateCreationInformation(SelectedWeb); } if (ParameterSpecified(nameof(Handlers))) { creationInformation.HandlersToProcess = Handlers; } if (ParameterSpecified(nameof(ExcludeHandlers))) { foreach (var handler in (Handlers[])Enum.GetValues(typeof(Handlers))) { if (!ExcludeHandlers.Has(handler) && handler != Handlers.All) { Handlers = Handlers | handler; } } creationInformation.HandlersToProcess = Handlers; } var extension = ""; if (packageName != null) { if (packageName.IndexOf(".", StringComparison.Ordinal) > -1) { extension = packageName.Substring(packageName.LastIndexOf(".", StringComparison.Ordinal)).ToLower(); } else { packageName += ".pnp"; extension = ".pnp"; } } var fileSystemConnector = new FileSystemConnector(path, ""); if (extension == ".pnp") { creationInformation.FileConnector = new OpenXMLConnector(packageName, fileSystemConnector); } else { creationInformation.FileConnector = fileSystemConnector; } #pragma warning disable 618 if (ParameterSpecified(nameof(PersistBrandingFiles))) { creationInformation.PersistBrandingFiles = PersistBrandingFiles; } #pragma warning restore 618 creationInformation.PersistPublishingFiles = PersistPublishingFiles; creationInformation.IncludeNativePublishingFiles = IncludeNativePublishingFiles; if (ParameterSpecified(nameof(IncludeSiteGroups))) { creationInformation.IncludeSiteGroups = IncludeSiteGroups; } creationInformation.IncludeTermGroupsSecurity = IncludeTermGroupsSecurity; creationInformation.IncludeSearchConfiguration = IncludeSearchConfiguration; if (ParameterSpecified(nameof(IncludeHiddenLists))) { creationInformation.IncludeHiddenLists = IncludeHiddenLists; } if (ParameterSpecified(nameof(IncludeAllClientSidePages))) { creationInformation.IncludeAllClientSidePages = IncludeAllClientSidePages; } creationInformation.SkipVersionCheck = SkipVersionCheck; if (ParameterSpecified(nameof(ContentTypeGroups)) && ContentTypeGroups != null) { creationInformation.ContentTypeGroupsToInclude = ContentTypeGroups.ToList(); } creationInformation.PersistMultiLanguageResources = PersistMultiLanguageResources; if (extension == ".pnp") { // if file is of pnp format, persist all files creationInformation.PersistBrandingFiles = true; creationInformation.PersistPublishingFiles = true; creationInformation.PersistMultiLanguageResources = true; } if (!string.IsNullOrEmpty(ResourceFilePrefix)) { creationInformation.ResourceFilePrefix = ResourceFilePrefix; } else { if (Out != null) { FileInfo fileInfo = new FileInfo(Out); var prefix = fileInfo.Name; // strip extension, if there is any var indexOfLastDot = prefix.LastIndexOf(".", StringComparison.Ordinal); if (indexOfLastDot > -1) { prefix = prefix.Substring(0, indexOfLastDot); } creationInformation.ResourceFilePrefix = prefix; } } if (ExtensibilityHandlers != null) { creationInformation.ExtensibilityHandlers = ExtensibilityHandlers.ToList(); } #pragma warning disable CS0618 // Type or member is obsolete if (NoBaseTemplate) { creationInformation.BaseTemplate = null; } else { creationInformation.BaseTemplate = SelectedWeb.GetBaseTemplate(); } #pragma warning restore CS0618 // Type or member is obsolete creationInformation.ProgressDelegate = (message, step, total) => { var percentage = Convert.ToInt32((100 / Convert.ToDouble(total)) * Convert.ToDouble(step)); WriteProgress(new ProgressRecord(0, $"Extracting Template from {SelectedWeb.Url}", message) { PercentComplete = percentage }); WriteProgress(new ProgressRecord(1, " ", " ") { RecordType = ProgressRecordType.Completed }); }; creationInformation.MessagesDelegate = (message, type) => { switch (type) { case ProvisioningMessageType.Warning: { WriteWarning(message); break; } case ProvisioningMessageType.Progress: { var activity = message; if (message.IndexOf("|") > -1) { var messageSplitted = message.Split('|'); if (messageSplitted.Length == 4) { var current = double.Parse(messageSplitted[2]); var total = double.Parse(messageSplitted[3]); subProgressRecord.RecordType = ProgressRecordType.Processing; subProgressRecord.Activity = messageSplitted[0]; subProgressRecord.StatusDescription = messageSplitted[1]; subProgressRecord.PercentComplete = Convert.ToInt32((100 / total) * current); WriteProgress(subProgressRecord); } else { subProgressRecord.Activity = "Processing"; subProgressRecord.RecordType = ProgressRecordType.Processing; subProgressRecord.StatusDescription = activity; subProgressRecord.PercentComplete = 0; WriteProgress(subProgressRecord); } } else { subProgressRecord.Activity = "Processing"; subProgressRecord.RecordType = ProgressRecordType.Processing; subProgressRecord.StatusDescription = activity; subProgressRecord.PercentComplete = 0; WriteProgress(subProgressRecord); } break; } case ProvisioningMessageType.Completed: { WriteProgress(new ProgressRecord(1, message, " ") { RecordType = ProgressRecordType.Completed }); break; } } }; if (IncludeAllTermGroups) { creationInformation.IncludeAllTermGroups = true; } else { if (IncludeSiteCollectionTermGroup) { creationInformation.IncludeSiteCollectionTermGroup = true; } } if (ParameterSpecified(nameof(ExcludeContentTypesFromSyndication))) { creationInformation.IncludeContentTypesFromSyndication = !ExcludeContentTypesFromSyndication.ToBool(); } if (ListsToExtract != null && ListsToExtract.Count > 0) { creationInformation.ListsToExtract.AddRange(ListsToExtract); } ProvisioningTemplate template = null; using (var provisioningContext = new PnPProvisioningContext((resource, scope) => { // Get Azure AD Token if (PnPConnection.CurrentConnection != null) { var graphAccessToken = PnPConnection.CurrentConnection.TryGetAccessToken(Enums.TokenAudience.MicrosoftGraph); if (graphAccessToken != null) { // Authenticated using -Graph or using another way to retrieve the accesstoken with Connect-PnPOnline return(Task.FromResult(graphAccessToken)); } } if (PnPConnection.CurrentConnection.PSCredential != null) { // Using normal credentials return(Task.FromResult(TokenHandler.AcquireToken(resource, null))); } else { // No token... return(null); } })) { template = SelectedWeb.GetProvisioningTemplate(creationInformation); } // Set metadata for template, if any SetTemplateMetadata(template, TemplateDisplayName, TemplateImagePreviewUrl, TemplateProperties); if (!OutputInstance) { var formatter = ProvisioningHelper.GetFormatter(schema); if (extension == ".pnp") { XMLTemplateProvider provider = new XMLOpenXMLTemplateProvider( creationInformation.FileConnector as OpenXMLConnector); var templateFileName = packageName.Substring(0, packageName.LastIndexOf(".", StringComparison.Ordinal)) + ".xml"; provider.SaveAs(template, templateFileName, formatter, TemplateProviderExtensions); } else { if (Out != null) { XMLTemplateProvider provider = new XMLFileSystemTemplateProvider(path, ""); provider.SaveAs(template, Path.Combine(path, packageName), formatter, TemplateProviderExtensions); } else { var outputStream = formatter.ToFormattedTemplate(template); var reader = new StreamReader(outputStream); WriteObject(reader.ReadToEnd()); } } } else { WriteObject(template); } }
protected override void ExecuteCmdlet() { if (MyInvocation.InvocationName.ToLower() == "apply-pnpprovisioninghierarchy") { WriteWarning("Apply-PnPProvisioningHierarchy has been deprecated. Use Apply-PnPTenantTemplate instead."); } var sitesProvisioned = new List <ProvisionedSite>(); var applyingInformation = new ProvisioningTemplateApplyingInformation(); applyingInformation.SiteProvisionedDelegate = (title, url) => { if (sitesProvisioned.FirstOrDefault(s => s.Url == url) == null) { sitesProvisioned.Add(new ProvisionedSite() { Title = title, Url = url }); } }; 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) => { if (message != null) { var percentage = Convert.ToInt32((100 / Convert.ToDouble(total)) * Convert.ToDouble(step)); progressRecord.Activity = $"Applying template to tenant"; progressRecord.StatusDescription = message; progressRecord.PercentComplete = percentage; progressRecord.RecordType = ProgressRecordType.Processing; WriteProgress(progressRecord); } }; var warningsShown = new List <string>(); applyingInformation.MessagesDelegate = (message, type) => { switch (type) { case ProvisioningMessageType.Warning: { if (!warningsShown.Contains(message)) { WriteWarning(message); warningsShown.Add(message); } break; } case ProvisioningMessageType.Progress: { if (message != null) { var activity = message; if (message.IndexOf("|") > -1) { var messageSplitted = message.Split('|'); if (messageSplitted.Length == 4) { var current = double.Parse(messageSplitted[2]); var total = double.Parse(messageSplitted[3]); subProgressRecord.RecordType = ProgressRecordType.Processing; subProgressRecord.Activity = string.IsNullOrEmpty(messageSplitted[0]) ? "-" : messageSplitted[0]; subProgressRecord.StatusDescription = string.IsNullOrEmpty(messageSplitted[1]) ? "-" : messageSplitted[1]; subProgressRecord.PercentComplete = Convert.ToInt32((100 / total) * current); WriteProgress(subProgressRecord); } else { subProgressRecord.Activity = "Processing"; subProgressRecord.RecordType = ProgressRecordType.Processing; subProgressRecord.StatusDescription = activity; subProgressRecord.PercentComplete = 0; WriteProgress(subProgressRecord); } } else { subProgressRecord.Activity = "Processing"; subProgressRecord.RecordType = ProgressRecordType.Processing; subProgressRecord.StatusDescription = activity; subProgressRecord.PercentComplete = 0; WriteProgress(subProgressRecord); } } break; } case ProvisioningMessageType.Completed: { WriteProgress(new ProgressRecord(1, message, " ") { RecordType = ProgressRecordType.Completed }); break; } } }; applyingInformation.OverwriteSystemPropertyBagValues = OverwriteSystemPropertyBagValues; applyingInformation.IgnoreDuplicateDataRowErrors = IgnoreDuplicateDataRowErrors; applyingInformation.ClearNavigation = ClearNavigation; applyingInformation.ProvisionContentTypesToSubWebs = ProvisionContentTypesToSubWebs; applyingInformation.ProvisionFieldsToSubWebs = ProvisionFieldsToSubWebs; ProvisioningHierarchy hierarchyToApply = null; switch (ParameterSetName) { case ParameterSet_PATH: { hierarchyToApply = GetHierarchy(); break; } case ParameterSet_OBJECT: { hierarchyToApply = Template; if (ResourceFolder != null) { var fileSystemConnector = new FileSystemConnector(ResourceFolder, ""); hierarchyToApply.Connector = fileSystemConnector; } else { if (Path != null) { if (!System.IO.Path.IsPathRooted(Path)) { Path = System.IO.Path.Combine(SessionState.Path.CurrentFileSystemLocation.Path, Path); } } else { Path = SessionState.Path.CurrentFileSystemLocation.Path; } var fileInfo = new FileInfo(Path); var fileConnector = new FileSystemConnector(fileInfo.DirectoryName, ""); hierarchyToApply.Connector = fileConnector; } break; } } if (Parameters != null) { foreach (var parameter in Parameters.Keys) { if (hierarchyToApply.Parameters.ContainsKey(parameter.ToString())) { hierarchyToApply.Parameters[parameter.ToString()] = Parameters[parameter].ToString(); } else { hierarchyToApply.Parameters.Add(parameter.ToString(), Parameters[parameter].ToString()); } } } #if !ONPREMISES using (var provisioningContext = new PnPProvisioningContext((resource, scope) => { // Get Azure AD Token if (AccessToken != null) { // Authenticated using -Graph or using another way to retrieve the accesstoken with Connect-PnPOnline return(Task.FromResult(AccessToken)); } else if (SPOnlineConnection.CurrentConnection.PSCredential != null) { // Using normal credentials return(Task.FromResult(TokenHandler.AcquireToken(resource, null))); } else { // No token... return(null); } })) { #endif if (!string.IsNullOrEmpty(SequenceId)) { Tenant.ApplyProvisionHierarchy(hierarchyToApply, SequenceId, applyingInformation); } else { if (hierarchyToApply.Sequences.Count > 0) { foreach (var sequence in hierarchyToApply.Sequences) { Tenant.ApplyProvisionHierarchy(hierarchyToApply, sequence.ID, applyingInformation); } } else { Tenant.ApplyProvisionHierarchy(hierarchyToApply, null, applyingInformation); } } #if !ONPREMISES } #endif WriteObject(sitesProvisioned, true); }
public static async Task RunAsync([QueueTrigger("actions")] ProvisioningActionModel action, TextWriter log) { var startProvisioning = DateTime.Now; String provisioningEnvironment = ConfigurationManager.AppSettings["SPPA:ProvisioningEnvironment"]; log.WriteLine($"Processing queue trigger function for tenant {action.TenantId}"); log.WriteLine($"PnP Correlation ID: {action.CorrelationId.ToString()}"); // Instantiate and use the telemetry model TelemetryUtility telemetry = new TelemetryUtility((s) => { log.WriteLine(s); }); 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"]); log.WriteLine($"Retrieved target Microsoft Graph Access Token."); 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; log.WriteLine($"Target SharePoint Online Tenant: {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"]); log.WriteLine($"Retrieved target SharePoint Online Access Token."); #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); log.WriteLine($"SharePoint Online Root Site Collection title: {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"]); log.WriteLine($"Retrieved target SharePoint Online Admin Center Access Token."); 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) { log.WriteLine($"{messageType} - {message}"); }; ptai.ProgressDelegate += delegate(string message, int step, int total) { log.WriteLine($"{step:00}/{total:00} - {message}"); }; ptai.SiteProvisionedDelegate += delegate(string title, string url) { log.WriteLine($"Fully provisioned site '{title}' with URL: {url}"); var provisionedSite = new Tuple <string, string>(title, url); if (!provisionedSites.Contains(provisionedSite)) { provisionedSites.Add(provisionedSite); } }; //#if !DEBUG // // Set the default delay for sites creations to 5 mins // ptai.DelayAfterModernSiteCreation = 60 * 5; //#endif // 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); } } // Apply the hierarchy log.WriteLine($"Hierarchy Provisioning Started: {DateTime.Now:hh.mm.ss}"); tenant.ApplyProvisionHierarchy(hierarchy, (hierarchy.Sequences != null && hierarchy.Sequences.Count > 0) ? hierarchy.Sequences[0].ID : null, ptai); log.WriteLine($"Hierarchy Provisioning Completed: {DateTime.Now:hh.mm.ss}"); } if (action.ApplyTheme && action.ApplyCustomTheme) { if (!String.IsNullOrEmpty(action.ThemePrimaryColor) && !String.IsNullOrEmpty(action.ThemeBodyTextColor) && !String.IsNullOrEmpty(action.ThemeBodyBackgroundColor)) { log.WriteLine($"Applying custom Theme to provisioned sites"); #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)) { log.WriteLine($"Custom Theme applied on site '{ps.Item1}' with URL: {ps.Item2}"); } else { log.WriteLine($"Failed to apply custom Theme on site '{ps.Item1}' with URL: {ps.Item2}"); } } } } } // 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); } } } 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 log.WriteLine($"Function successfully executed!"); // Log telemetry event telemetry?.LogEvent("ProvisioningFunction.End", telemetryProperties); } } else { var noTokensErrorMessage = $"Cannot retrieve Refresh Token or Access Token for {action.CorrelationId} in tenant {action.TenantId}!"; log.WriteLine(noTokensErrorMessage); throw new ApplicationException(noTokensErrorMessage); } } catch (Exception ex) { // Skip logging exception for Recycled Site if (ex is RecycledSiteException) { // rather log an event telemetry?.LogEvent("ProvisioningFunction.RecycledSite", telemetryProperties); // Log reporting event (3 = RecycledSite) LogReporting(action, provisioningEnvironment, startProvisioning, null, 3, ex.ToDetailedString()); } // Skip logging exception for Concurrent Provisioning else if (ex is ConcurrentProvisioningException) { // rather log an event telemetry?.LogEvent("ProvisioningFunction.ConcurrentProvisioning", telemetryProperties); // Log reporting event (4 = ConcurrentProvisioningException) LogReporting(action, provisioningEnvironment, startProvisioning, null, 4, ex.ToDetailedString()); } else { // Log telemetry event telemetry?.LogException(ex, "ProvisioningFunction.RunAsync", telemetryProperties); // Log reporting event (2 = Failed) LogReporting(action, provisioningEnvironment, startProvisioning, null, 2, ex.ToDetailedString()); } 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(); } }
private ProvisioningHierarchy ExtractTemplate(ExtractConfiguration configuration) { configuration.ProgressDelegate = (message, step, total) => { var percentage = Convert.ToInt32((100 / Convert.ToDouble(total)) * Convert.ToDouble(step)); WriteProgress(new ProgressRecord(0, $"Extracting Tenant Template", message) { PercentComplete = percentage }); WriteProgress(new ProgressRecord(1, " ", " ") { RecordType = ProgressRecordType.Completed }); }; configuration.MessagesDelegate = (message, type) => { switch (type) { case ProvisioningMessageType.Warning: { WriteWarning(message); break; } case ProvisioningMessageType.Progress: { var activity = message; if (message.IndexOf("|") > -1) { var messageSplitted = message.Split('|'); if (messageSplitted.Length == 4) { var current = double.Parse(messageSplitted[2]); var total = double.Parse(messageSplitted[3]); subProgressRecord.RecordType = ProgressRecordType.Processing; subProgressRecord.Activity = messageSplitted[0]; subProgressRecord.StatusDescription = messageSplitted[1]; subProgressRecord.PercentComplete = Convert.ToInt32((100 / total) * current); WriteProgress(subProgressRecord); } else { subProgressRecord.Activity = "Processing"; subProgressRecord.RecordType = ProgressRecordType.Processing; subProgressRecord.StatusDescription = activity; subProgressRecord.PercentComplete = 0; WriteProgress(subProgressRecord); } } else { subProgressRecord.Activity = "Processing"; subProgressRecord.RecordType = ProgressRecordType.Processing; subProgressRecord.StatusDescription = activity; subProgressRecord.PercentComplete = 0; WriteProgress(subProgressRecord); } break; } case ProvisioningMessageType.Completed: { WriteProgress(new ProgressRecord(1, message, " ") { RecordType = ProgressRecordType.Completed }); break; } } }; using (var provisioningContext = new PnPProvisioningContext(async(resource, scope) => { return(await TokenRetrieval.GetAccessTokenAsync(resource, scope)); // if (resource.ToLower().StartsWith("https://")) // { // var uri = new Uri(resource); // resource = uri.Authority; // } // // Get Azure AD Token // if (PnPConnection.CurrentConnection != null) // { // if (resource.Equals("graph.microsoft.com", StringComparison.OrdinalIgnoreCase)) // { // var graphAccessToken = await PnPConnection.CurrentConnection.TryGetAccessTokenAsync(Enums.TokenAudience.MicrosoftGraph); // if (graphAccessToken != null) // { // // Authenticated using -Graph or using another way to retrieve the accesstoken with Connect-PnPOnline\ // return graphAccessToken; // } // } // #if NETFRAMEWORK // else if (resource.ToLower().Contains(".sharepoint.")) // #else // else if (resource.Contains(".sharepoint.", StringComparison.OrdinalIgnoreCase)) // #endif // { // using (var clientContext = PnPConnection.CurrentConnection.CloneContext($"https://{resource}")) // { // string accessToken = null; // EventHandler<WebRequestEventArgs> handler = (s, e) => // { // string authorization = e.WebRequestExecutor.RequestHeaders["Authorization"]; // if (!string.IsNullOrEmpty(authorization)) // { // accessToken = authorization.Replace("Bearer ", string.Empty); // } // }; // // Issue a dummy request to get it from the Authorization header // clientContext.ExecutingWebRequest += handler; // clientContext.ExecuteQueryRetry(); // clientContext.ExecutingWebRequest -= handler; // return accessToken; // } // } // } // if (PnPConnection.CurrentConnection.PSCredential != null || PnPConnection.CurrentConnection.ClientId != null) // { // // Using normal credentials // return await TokenHandler.AcquireTokenAsync(resource, null); // } // else // { // // No token... // throw new PSInvalidOperationException("Your template contains artifacts that require an access token. Please provide consent to the PnP Management Shell application first by executing: Register-PnPManagementShellAccess"); // } })) { return(Tenant.GetTenantTemplate(configuration)); } }
private ProvisioningHierarchy ExtractTemplate(ExtractConfiguration configuration) { configuration.ProgressDelegate = (message, step, total) => { var percentage = Convert.ToInt32((100 / Convert.ToDouble(total)) * Convert.ToDouble(step)); WriteProgress(new ProgressRecord(0, $"Extracting Tenant Template", message) { PercentComplete = percentage }); WriteProgress(new ProgressRecord(1, " ", " ") { RecordType = ProgressRecordType.Completed }); }; configuration.MessagesDelegate = (message, type) => { switch (type) { case ProvisioningMessageType.Warning: { WriteWarning(message); break; } case ProvisioningMessageType.Progress: { var activity = message; if (message.IndexOf("|") > -1) { var messageSplitted = message.Split('|'); if (messageSplitted.Length == 4) { var current = double.Parse(messageSplitted[2]); var total = double.Parse(messageSplitted[3]); subProgressRecord.RecordType = ProgressRecordType.Processing; subProgressRecord.Activity = messageSplitted[0]; subProgressRecord.StatusDescription = messageSplitted[1]; subProgressRecord.PercentComplete = Convert.ToInt32((100 / total) * current); WriteProgress(subProgressRecord); } else { subProgressRecord.Activity = "Processing"; subProgressRecord.RecordType = ProgressRecordType.Processing; subProgressRecord.StatusDescription = activity; subProgressRecord.PercentComplete = 0; WriteProgress(subProgressRecord); } } else { subProgressRecord.Activity = "Processing"; subProgressRecord.RecordType = ProgressRecordType.Processing; subProgressRecord.StatusDescription = activity; subProgressRecord.PercentComplete = 0; WriteProgress(subProgressRecord); } break; } case ProvisioningMessageType.Completed: { WriteProgress(new ProgressRecord(1, message, " ") { RecordType = ProgressRecordType.Completed }); break; } } }; using (var provisioningContext = new PnPProvisioningContext(async(resource, scope) => { return(await TokenRetrieval.GetAccessTokenAsync(resource, scope)); }, azureEnvironment: PnPConnection.Current.AzureEnvironment)) { return(Tenant.GetTenantTemplate(configuration)); } }
/// <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}"); } } } }