public static async Task <HttpResponseMessage> Run( [HttpTrigger(AuthorizationLevel.Function, "get", Route = null)] HttpRequestMessage req, TraceWriter log) { log.Info($"C# HTTP trigger function '{FunctionName}' processed a request."); // parse query parameter string relativeUrl = req.GetQueryNameValuePairs() .FirstOrDefault(q => string.Compare(q.Key, "relativeUrl", true) == 0) .Value; if (string.IsNullOrEmpty(relativeUrl) == true) { return(req.CreateResponse(HttpStatusCode.BadRequest, "Please pass a relative url on the query string.")); } var tenantUrl = new Uri(CloudConfigurationManager.GetSetting("TenantUrl")); Uri.TryCreate(tenantUrl, relativeUrl, out Uri fullSiteUrl); var siteCollectionExistsMessage = new SiteCollectionExistsMessage() { Exists = false }; try { var clientContextManager = new ClientContextManager(new BaseConfiguration(), new CertificateManager()); using (var ctx = clientContextManager.GetAzureADAppOnlyAuthenticatedContext(fullSiteUrl.AbsoluteUri)) { ctx.Load(ctx.Web); ctx.ExecuteQuery(); siteCollectionExistsMessage.Title = ctx.Web.Title; var type = fullSiteUrl.PathAndQuery.Substring("/sites/".Length, 4).ToUpperInvariant(); if (string.IsNullOrEmpty(type) == true) { type = fullSiteUrl.PathAndQuery.Substring("/teams/".Length, 4).ToUpperInvariant(); } siteCollectionExistsMessage.AbsoluteUri = fullSiteUrl.AbsoluteUri; siteCollectionExistsMessage.RelativeUrl = fullSiteUrl.PathAndQuery; siteCollectionExistsMessage.Type = type; siteCollectionExistsMessage.Exists = true; } } catch { // site does not exist } return(req.CreateResponse(HttpStatusCode.OK, siteCollectionExistsMessage)); }
private static void GetTemplateFromSharePointOnline(string templateUrl, CloudBlockBlob blob) { var clientContextManager = new ClientContextManager(new BaseConfiguration(), new CertificateManager()); var provisioningSiteUrl = CloudConfigurationManager.GetSetting("ProvisioningSite"); using (var ctx = clientContextManager.GetAzureADAppOnlyAuthenticatedContext(provisioningSiteUrl)) { var templateListItem = ctx.Web.GetListItem(templateUrl); var file = templateListItem.File; // File in SPO is newer, so grab that var binaryStream = file.OpenBinaryStream(); ctx.Load(file); ctx.ExecuteQuery(); if (binaryStream != null && binaryStream.Value != null) { // Save to blob binaryStream.Value.Position = 0; blob.UploadFromStream(binaryStream.Value); } } }
public static async void Run( [ServiceBusTrigger("new-sites-topic", "create-site-subscription", AccessRights.Manage, Connection = "ManageTopicConnection")] BrokeredMessage newSiteMsg, [OrchestrationClient] DurableOrchestrationClient orchestrationClient, TraceWriter log) { log.Info($"C# Service Bus trigger function '{FunctionName}' processed message: {newSiteMsg.MessageId}"); /* * The following line should work, but doesn't, so small workaround here... */ //var createSiteCollectionJob = newSiteMsg.GetBody<CreateSiteCollectionJob>(); var stream = newSiteMsg.GetBody <Stream>(); StreamReader streamReader = new StreamReader(stream); string createSiteCollectionJobAsJson = streamReader.ReadToEnd(); var createSiteCollectionJob = JsonConvert.DeserializeObject <CreateSiteCollectionJob>(createSiteCollectionJobAsJson); CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("AzureWebJobsStorage")); CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient(); CloudBlobContainer container = blobClient.GetContainerReference(CloudConfigurationManager.GetSetting("JobFilesContainer")); var blob = container.GetBlobReference(createSiteCollectionJob.FileNameWithExtension); var blobStream = new MemoryStream(); blob.DownloadToStream(blobStream); streamReader = new StreamReader(blobStream); blobStream.Position = 0; string blobContent = streamReader.ReadToEnd(); JObject provisioningJobFile = JObject.Parse(blobContent); var provisioningTemplateUrl = provisioningJobFile["ProvisioningTemplateUrl"].Value <string>(); var tenantUrl = new Uri(CloudConfigurationManager.GetSetting("TenantUrl")); Uri.TryCreate(tenantUrl, provisioningJobFile["RelativeUrl"].Value <string>(), out Uri fullSiteUrl); //Properties of the New SiteCollection var siteCreationProperties = new SiteCreationProperties { //New SiteCollection Url Url = fullSiteUrl.AbsoluteUri, //Title of the Root Site Title = provisioningJobFile["SiteTitle"].Value <string>(), //Template of the Root Site. Using Team Site for now. Template = "STS#0", //Owner of thge Site Owner = provisioningJobFile["Owner"].Value <string>(), //Storage Limit in MB StorageMaximumLevel = provisioningJobFile["StorageMaximumLevel"].Value <int>(), StorageWarningLevel = provisioningJobFile["StorageWarningLevel"].Value <int>(), //UserCode Resource Points Allowed UserCodeMaximumLevel = provisioningJobFile["UserCodeMaximumLevel"].Value <int>(), UserCodeWarningLevel = provisioningJobFile["UserCodeWarningLevel"].Value <int>(), //TimeZone TimeZoneId = provisioningJobFile["TimeZone"].Value <int>(), }; var clientContextManager = new ClientContextManager(new BaseConfiguration(), new CertificateManager()); using (var adminCtx = clientContextManager.GetAzureADAppOnlyAuthenticatedContext(CloudConfigurationManager.GetSetting("TenantAdminUrl"))) { var tenant = new Tenant(adminCtx); //Create the SiteCollection tenant.CreateSite(siteCreationProperties); try { adminCtx.Load(tenant); adminCtx.ExecuteQuery(); log.Info($"Initiated creation of site collection: {siteCreationProperties.Url}"); string instanceId = await orchestrationClient .StartNewAsync("monitor-site-collection-creation", new MonitorSiteCollectionCreationData { FullSiteUrl = siteCreationProperties.Url, ListItemID = createSiteCollectionJob.ListItemID, ProvisioningTemplateUrl = provisioningTemplateUrl, TimeStamp = DateTime.Now, CreateSiteCollectionJob = createSiteCollectionJob }); // ToDo: Update value of field provisioning status in Azure Storage Table "CustomerDocumentCenterSites" log.Info($"Durable Function Ochestration for site collection creation started: {instanceId}"); } catch (Exception ex) { log.Error($"Something went wrong while creating site collection: {siteCreationProperties.Url}.", ex); } } }
public static async void Run( [OrchestrationTrigger] DurableOrchestrationContext orchestrationContext, [ServiceBus("site-updates-topic", Connection = "ManageTopicConnection")] ICollector <BrokeredMessage> updateSitesTopic, TraceWriter log) { log.Info($"C# Orchestration trigger function '{FunctionName}' started."); bool siteHasBeenCreated = false; var siteCollectionCreationData = orchestrationContext.GetInput <MonitorSiteCollectionCreationData>(); log.Info($"Monitor creation of site '{siteCollectionCreationData.FullSiteUrl}'."); if (siteCollectionCreationData.TimeStamp == null || DateTime.Now.Subtract(siteCollectionCreationData.TimeStamp) > new TimeSpan(24, 10, 00)) { log.Warning($"SiteCollection {siteCollectionCreationData.FullSiteUrl} was not created within 24 hours or timestamp was empty."); return; } var clientContextManager = new ClientContextManager(new BaseConfiguration(), new CertificateManager()); using (var adminCtx = clientContextManager.GetAzureADAppOnlyAuthenticatedContext(CloudConfigurationManager.GetSetting("TenantAdminUrl"))) { var tenant = new Tenant(adminCtx); try { //Get the site name var properties = tenant.GetSitePropertiesByUrl(siteCollectionCreationData.FullSiteUrl, false); tenant.Context.Load(properties); // Will cause an exception if site URL is not there. Not optimal, but the way it works. tenant.Context.ExecuteQueryRetry(); log.Info($"Site creation status: '{properties.Status}'."); siteHasBeenCreated = properties.Status.Equals("Active", StringComparison.OrdinalIgnoreCase); } catch { try { // Check if a site collection with this URL has been recycled (exists in garbage bin) var deletedProperties = tenant.GetDeletedSitePropertiesByUrl(siteCollectionCreationData.FullSiteUrl); tenant.Context.Load(deletedProperties); tenant.Context.ExecuteQueryRetry(); if (deletedProperties.Status.Equals("Recycled", StringComparison.OrdinalIgnoreCase)) { log.Info($"SiteCollection with URL{siteCollectionCreationData.FullSiteUrl} already exists in recycle bin."); var provisioningSiteUrl = CloudConfigurationManager.GetSetting("ProvisioningSite"); using (var ctx = clientContextManager.GetAzureADAppOnlyAuthenticatedContext(provisioningSiteUrl)) { // Todo: get list title from configuration. // Assume that the web has a list named "PnPProvisioningJobs". List provisioningJobsList = ctx.Web.Lists.GetByTitle("PnPProvisioningJobs"); ListItem listItem = provisioningJobsList.GetItemById(siteCollectionCreationData.ListItemID); // Write a new value to the PnPProvisioningJobStatus field of // the PnPProvisioningJobs item. listItem["PnPProvisioningJobStatus"] = "Failed (site with same URL exists in recycle bin)"; //ToDo: Should we have different statusses for actual site creation and the applying of the template? listItem.Update(); ctx.ExecuteQuery(); } return; } } catch { siteHasBeenCreated = false; } } //Check if provisioning of the SiteCollection is complete. while (!siteHasBeenCreated) { //Wait for 1 minute and then try again DateTime deadline = orchestrationContext.CurrentUtcDateTime.Add(TimeSpan.FromMinutes(1)); await orchestrationContext.CreateTimer(deadline, CancellationToken.None); } if (siteHasBeenCreated) { log.Info($"SiteCollection {siteCollectionCreationData.FullSiteUrl} created."); var applyProvisioningTemplateJob = new ApplyProvisioningTemplateJob() { ListItemID = siteCollectionCreationData.ListItemID, FileNameWithExtension = siteCollectionCreationData.CreateSiteCollectionJob.FileNameWithExtension, ProvisioningTemplateUrl = siteCollectionCreationData.ProvisioningTemplateUrl }; var applyProvisioningTemplateMsg = new BrokeredMessage(applyProvisioningTemplateJob, new DataContractJsonSerializer(typeof(ApplyProvisioningTemplateJob))) { ContentType = "application/json", Label = "UpdateSiteTemplate" }; updateSitesTopic.Add(applyProvisioningTemplateMsg); } } }
public static void Run( [ServiceBusTrigger("site-updates-topic", "update-metadata-subscription", AccessRights.Manage, Connection = "ManageTopicConnection")] BrokeredMessage updateMsg, TraceWriter log) { log.Info($"C# ServiceBus trigger function '{FunctionName}' processed message: {updateMsg.MessageId} (Label': {updateMsg.Label}')"); var somethingWentWrong = false; var clientContextManager = new ClientContextManager(new BaseConfiguration(), new CertificateManager()); var updateMetadataJob = updateMsg.GetBody <UpdateSiteJob>(); using (var ctx = clientContextManager.GetAzureADAppOnlyAuthenticatedContext(updateMetadataJob.Url)) { // ToDo; currently we only support updating the Title (incl. Maritime Installations) // Maybe use switch statement here. Come up with some better way of resolving // what kind of site we're dealing with (info from property bag or Azure storage table) // Specific stuff happens here, like for instance updating the default column // value 'site' for an installation (metadata value for site is always the same // as the Title of an Installation site collection) if (updateMetadataJob.Url.ToLowerInvariant().Contains("/inst-") == true) { try { const string propertyBagDefaultColumnValues = "_marlink_defaultcolumnvalues"; var definitionJson = ctx.Web.GetPropertyBagValueString(propertyBagDefaultColumnValues, String.Empty); if (string.IsNullOrEmpty(definitionJson)) { log.Info($"Definition in site {updateMetadataJob.Url} was empty."); return; } var definition = JsonConvert.DeserializeObject <DefaultColumnValuesDefinition>(definitionJson); const string siteColumnName = "dc_Site"; definition.Libraries.ForEach(l => { l.Folders.ForEach(f => { var siteColumnIndex = f.DefaultColumnValues.FindIndex(c => String.CompareOrdinal(c.Name, siteColumnName) == 0); if (siteColumnIndex != -1) { f.DefaultColumnValues[siteColumnIndex].Value = updateMetadataJob.Title; } else { f.DefaultColumnValues.Add(new DefaultColumnValue() { Name = siteColumnName, Value = updateMetadataJob.Title }); } }); }); // Save the template information in the target site var updatedDefinition = definition; updatedDefinition.AppliedOn = null; string updatedJson = JsonConvert.SerializeObject(updatedDefinition); // Validate if the defintion confirms to the schema /* * if (DefaultColumnValuesHelper.IsValidDefinition(updatedJson, out IEnumerable<string> errors) == false) * { * log.Error($"Invalid JSON Definition was provided for {updateMetadataJob.Url}."); * foreach (var errMsg in errors) * { * log.Info(errMsg); * } * return; * } */ ctx.Web.SetPropertyBagValue("_marlink_defaultcolumnvalues", updatedJson); log.Info($"Updated Default Column Values Definition in site {updateMetadataJob.Url}."); } catch (Exception ex) { log.Error($"Error occured while setting default column values to {updateMetadataJob.Url}.", ex); somethingWentWrong = true; } } try { CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("AzureWebJobsStorage")); CloudTableClient tableClient = storageAccount.CreateCloudTableClient(); CloudTable customerDocumentCenterSitesTable = tableClient.GetTableReference(CloudConfigurationManager.GetSetting("SitesTable")); // Update columns in Azure Storage table to reflect updates var item = new CustomerDocumentCenterSitesTableEntry { PartitionKey = updateMetadataJob.Type, RowKey = updateMetadataJob.ID, ETag = "*", Title = updateMetadataJob.Title, }; if (updateMetadataJob.Type == "INST") { item.Site = updateMetadataJob.Title; } var operation = TableOperation.Merge(item); customerDocumentCenterSitesTable.ExecuteAsync(operation); } catch (Exception ex) { log.Error($"Error occured while updating table entry of {updateMetadataJob.Url}.", ex); somethingWentWrong = true; } if (somethingWentWrong == false) { ctx.Web.Title = updateMetadataJob.Title; ctx.Web.Update(); ctx.ExecuteQuery(); log.Info($"Updated title of site collection '{updateMetadataJob.Url}' to '{updateMetadataJob.Title}'"); } } }
public static void Run( [ServiceBusTrigger("site-updates-topic", "apply-template-subscription", AccessRights.Manage, Connection = "ManageTopicConnection")] BrokeredMessage updateMsg, [ServiceBus("new-sites-topic", Connection = "ManageTopicConnection")] ICollector <BrokeredMessage> newSitesTopic, ExecutionContext executionContext, TraceWriter log) { log.Info($"C# Service Bus trigger function '{FunctionName}' processed message: {updateMsg.MessageId} (Label': {updateMsg.Label}')"); /* * The following line should work, but doesn't, so small workaround here... */ //var applyProvisioningTemplateJobAsJson = updateMsg.GetBody<ApplyProvisioningTemplateJob>(); var stream = updateMsg.GetBody <Stream>(); StreamReader streamReader = new StreamReader(stream); string applyProvisioningTemplateJobAsJson = streamReader.ReadToEnd(); var applyProvisioningTemplateJob = JsonConvert.DeserializeObject <ApplyProvisioningTemplateJob>(applyProvisioningTemplateJobAsJson); CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("AzureWebJobsStorage")); CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient(); CloudBlobContainer container = blobClient.GetContainerReference(CloudConfigurationManager.GetSetting("JobFilesContainer")); var blob = container.GetBlobReference(applyProvisioningTemplateJob.FileNameWithExtension); var blobStream = new MemoryStream(); blob.DownloadToStream(blobStream); streamReader = new StreamReader(blobStream); blobStream.Position = 0; string blobContent = streamReader.ReadToEnd(); JObject provisioningJobFile = JObject.Parse(blobContent); var provisioningTemplateUrl = provisioningJobFile["ProvisioningTemplateUrl"].Value <string>(); var relativeUrl = provisioningJobFile["RelativeUrl"].Value <string>(); // get JSON result objects into a list IList <JToken> parameters = provisioningJobFile["TemplateParameters"].Children().ToList(); // serialize JSON results into .NET objects IDictionary <string, string> templateParameters = new Dictionary <string, string>(); foreach (JProperty parameter in parameters) { templateParameters.Add(parameter.Name, parameter.Value.ToObject <string>()); } var clientContextManager = new ClientContextManager(new BaseConfiguration(), new CertificateManager()); var provisioningSiteUrl = CloudConfigurationManager.GetSetting("ProvisioningSite"); using (var ctx = clientContextManager.GetAzureADAppOnlyAuthenticatedContext(provisioningSiteUrl)) { // Todo: get list title from configuration. // Assume that the web has a list named "PnPProvisioningJobs". List provisioningJobsList = ctx.Web.Lists.GetByTitle("PnPProvisioningJobs"); ListItem listItem = provisioningJobsList.GetItemById(applyProvisioningTemplateJob.ListItemID); // Write a new value to the PnPProvisioningJobStatus field of // the PnPProvisioningJobs item. listItem["PnPProvisioningJobStatus"] = "Running (applying template)"; listItem.Update(); ctx.ExecuteQuery(); var templateContainer = blobClient.GetContainerReference(CloudConfigurationManager.GetSetting("TemplateFilesContainer")); var templateFileName = Path.GetFileName(provisioningTemplateUrl); var templateBlob = templateContainer.GetBlobReference(templateFileName); var templateBlobStream = new MemoryStream(); templateBlob.DownloadToStream(templateBlobStream); var provisioningTemplate = new SiteTemplate(templateBlobStream).ProvisioningTemplate; log.Info($"(id {executionContext.InvocationId}) Retrieved template {templateFileName} from blob storage."); foreach (var parameter in templateParameters) { provisioningTemplate.Parameters[parameter.Key] = parameter.Value; } var ptai = new ProvisioningTemplateApplyingInformation { ProgressDelegate = (string message, int progress, int total) => { log.Info($"(id {executionContext.InvocationId})[Progress]: {progress:00}/{total:00} - {message}"); }, MessagesDelegate = (string message, ProvisioningMessageType messageType) => { log.Info($"(id {executionContext.InvocationId})[{messageType.ToString()}]: {message}"); }, }; var tenantUrl = new Uri(CloudConfigurationManager.GetSetting("TenantUrl")); Uri.TryCreate(tenantUrl, relativeUrl, out Uri fullSiteUrl); var templateAppliedWithOutAnyErrors = false; log.Info($"Opening ctx to {fullSiteUrl.AbsoluteUri}"); using (var newSiteContext = clientContextManager.GetAzureADAppOnlyAuthenticatedContext(fullSiteUrl.AbsoluteUri)) { int tryCount = 0; const int maxTries = 3; do { tryCount++; try { log.Info($"Applying the provisioning template {provisioningTemplateUrl} to {fullSiteUrl.AbsoluteUri}."); newSiteContext.Web.ApplyProvisioningTemplate(provisioningTemplate, ptai); log.Info($"Provisioning template has been applied to {fullSiteUrl.AbsoluteUri}."); templateAppliedWithOutAnyErrors = true; } catch (Exception ex) { log.Error($"Error occured while applying the provisioning template to {fullSiteUrl.AbsoluteUri}.", ex); templateAppliedWithOutAnyErrors = false; if (tryCount <= maxTries) { log.Warning($"An error occured while applying the provisioning template, but will try to apply the provisioning template to {fullSiteUrl.AbsoluteUri} once more. (max {maxTries} times, this was attempt number {tryCount}.)"); } else { log.Warning($"Tried {maxTries} times to apply the provisioning template without succes."); } } } while (templateAppliedWithOutAnyErrors == false && tryCount <= maxTries); } if (templateAppliedWithOutAnyErrors == true) { var setDefaultColumnValuesMsg = new BrokeredMessage(applyProvisioningTemplateJob, new DataContractJsonSerializer(typeof(ApplyProvisioningTemplateJob))) { ContentType = "application/json", Label = "SetDefaultColumnValues" }; newSitesTopic.Add(setDefaultColumnValuesMsg); listItem["PnPProvisioningJobStatus"] = "Provisioned"; } else { listItem["PnPProvisioningJobStatus"] = "Failed (error while applying template)"; } listItem.Update(); ctx.ExecuteQuery(); } }
public static void Run( [ServiceBusTrigger("site-operations-topic", "new-sites-subscription", AccessRights.Manage, Connection = "ManageTopicConnection")] BrokeredMessage newSiteMsg, [Blob("provisioning-job-files", Connection = "AzureWebJobsStorage")] CloudBlobDirectory blobDirectory, [ServiceBus("new-sites-topic", Connection = "ManageTopicConnection")] ICollector <BrokeredMessage> newSitesTopic, TraceWriter log) { log.Info($"C# ServiceBus trigger function '{FunctionName}' processed message: {newSiteMsg.MessageId} (Label': {newSiteMsg.Label}')"); /* * The following line should work, but doesn't, so small workaround here... */ //var createSiteCollectionJob = newSiteMsg.GetBody<CreateSiteCollectionJob>(); var stream = newSiteMsg.GetBody <Stream>(); StreamReader streamReader = new StreamReader(stream); string createSiteCollectionJobAsJson = streamReader.ReadToEnd(); var createSiteCollectionJob = JsonConvert.DeserializeObject <CreateSiteCollectionJob>(createSiteCollectionJobAsJson); var clientContextManager = new ClientContextManager(new BaseConfiguration(), new CertificateManager()); var provisioningSiteUrl = CloudConfigurationManager.GetSetting("ProvisioningSite"); using (var ctx = clientContextManager.GetAzureADAppOnlyAuthenticatedContext(provisioningSiteUrl)) { // Todo: construct URL in a more resililant way ;-) var relativeSiteUrl = new Uri(provisioningSiteUrl).PathAndQuery; string serverRelativeFileUrl = $"{relativeSiteUrl}/{createSiteCollectionJob.FolderPath}{createSiteCollectionJob.FileNameWithExtension}"; var jobFile = ctx.Web.GetFileByServerRelativeUrl(serverRelativeFileUrl); ctx.Load(jobFile, jf => jf.ServerRelativeUrl); // ToDo: what if file does not exist? var jobFileStream = jobFile.OpenBinaryStream(); ctx.ExecuteQueryRetry(); MemoryStream mem = new MemoryStream(); jobFileStream.Value.CopyTo(mem); mem.Position = 0; StreamReader reader = new StreamReader(mem, Encoding.Unicode); var jobFileAsString = reader.ReadToEnd(); // Store job file in blob storage CloudBlockBlob blob = blobDirectory.GetBlockBlobReference(createSiteCollectionJob.FileNameWithExtension); blob.Properties.ContentType = "application/json"; //blob.Metadata.Add("abcd", "12345"); blob.UploadText(jobFileAsString); log.Info($"JobFile '{createSiteCollectionJob.FileNameWithExtension}' stored in Azure blob storage"); var createSiteCollectionMsg = new BrokeredMessage(createSiteCollectionJob, new DataContractJsonSerializer(typeof(CreateSiteCollectionJob))) { ContentType = "application/json", Label = "CreateSiteCollection" }; newSitesTopic.Add(createSiteCollectionMsg); // Todo: get list title from configuration. // Assume that the web has a list named "PnPProvisioningJobs". List provisioningJobsList = ctx.Web.Lists.GetByTitle("PnPProvisioningJobs"); ListItem listItem = provisioningJobsList.GetItemById(createSiteCollectionJob.ListItemID); listItem["PnPProvisioningJobStatus"] = "Running (creating site collection)"; //ToDo: Should we have different statusses for actual site creation and the applying of the template? listItem.Update(); ctx.ExecuteQuery(); } }
public static void Run( [ServiceBusTrigger("new-sites-topic", "set-default-column-values-subscription", AccessRights.Manage, Connection = "ManageTopicConnection")] BrokeredMessage setDefaultColumnValuesMsg, TraceWriter log) { log.Info($"C# Service Bus trigger function '{FunctionName}' processed message: {setDefaultColumnValuesMsg.MessageId} (Label': {setDefaultColumnValuesMsg.Label}')"); /* * The following line should work, but doesn't, so small workaround here... */ //var applyProvisioningTemplateJobAsJson = setDefaultColumnValuesMsg.GetBody<ApplyProvisioningTemplateJob>(); var stream = setDefaultColumnValuesMsg.GetBody <Stream>(); StreamReader streamReader = new StreamReader(stream); string applyProvisioningTemplateJobAsJson = streamReader.ReadToEnd(); var applyProvisioningTemplateJob = JsonConvert.DeserializeObject <ApplyProvisioningTemplateJob>(applyProvisioningTemplateJobAsJson); // ToDo: instead of retrieving this info from blob, get it from table storage CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("AzureWebJobsStorage")); CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient(); CloudBlobContainer container = blobClient.GetContainerReference(CloudConfigurationManager.GetSetting("JobFilesContainer")); var blob = container.GetBlobReference(applyProvisioningTemplateJob.FileNameWithExtension); var blobStream = new MemoryStream(); blob.DownloadToStream(blobStream); streamReader = new StreamReader(blobStream); blobStream.Position = 0; string blobContent = streamReader.ReadToEnd(); JObject provisioningJobFile = JObject.Parse(blobContent); var provisioningTemplateUrl = provisioningJobFile["ProvisioningTemplateUrl"].Value <string>(); var relativeUrl = provisioningJobFile["RelativeUrl"].Value <string>(); var tenantUrl = new Uri(CloudConfigurationManager.GetSetting("TenantUrl")); Uri.TryCreate(tenantUrl, relativeUrl, out Uri fullSiteUrl); // Currently only do this for Maritime Installation sites if (relativeUrl.ToLowerInvariant().Contains("/inst-") == false) { log.Info($"Site collection {fullSiteUrl.AbsoluteUri} is not a maritime Installation site. Skip setting default column values."); return; } // get JSON result objects into a list IList <JToken> parameters = provisioningJobFile["TemplateParameters"].Children().ToList(); // serialize JSON results into .NET objects var defaultColumnValues = new List <DefaultColumnValue>(); foreach (JProperty parameter in parameters) { defaultColumnValues.Add(new DefaultColumnValue { Name = $"dc_{parameter.Name}", Value = parameter.Value.ToObject <string>() }); } var folders = new List <DefaultColumnValuesFolder> { new DefaultColumnValuesFolder { Path = "/", DefaultColumnValues = defaultColumnValues } }; var definition = new DefaultColumnValuesDefinition(); definition.Libraries.AddRange(new List <DefaultColumnValuesLibrary> { new DefaultColumnValuesLibrary { Name = "Installation", Folders = folders } , new DefaultColumnValuesLibrary { Name = "Logistics", Folders = folders } , new DefaultColumnValuesLibrary { Name = "Network", Folders = folders } , new DefaultColumnValuesLibrary { Name = "Pictures", Folders = folders } , new DefaultColumnValuesLibrary { Name = "Project", Folders = folders } , new DefaultColumnValuesLibrary { Name = "Solutions", Folders = folders } , new DefaultColumnValuesLibrary { Name = "Videos", Folders = folders } }); var clientContextManager = new ClientContextManager(new BaseConfiguration(), new CertificateManager()); using (var ctx = clientContextManager.GetAzureADAppOnlyAuthenticatedContext(fullSiteUrl.AbsoluteUri)) { try { var jsonDefinition = JsonConvert.SerializeObject(definition, Formatting.None); ctx.Web.SetPropertyBagValue("_marlink_defaultcolumnvalues", jsonDefinition); log.Info($"Added Default Column Values Definition object to property bag of site {fullSiteUrl.AbsoluteUri}."); } catch (Exception ex) { log.Error($"Error occured while setting default column values to {fullSiteUrl.AbsoluteUri}.", ex); } } }