PnP PowerShell - PnP Provisioning package (pnp) with folders

Brass Contributor

Hi 

 

Is it possible to create a pnp provisioning file with a folder structure for a library by using Convert-PnPFolderToProvisioningTemplate or doesn't the current provisioning enging support pnp packages with folders? I have tried with several options but with no success. 

 

I would like the whole package to contain the folders from the beginning avoiding to have to handle that separately when the template has been applied to the site. 

 

/Niclas

8 Replies
According to a comment in this post, it seems possible:
http://nickvandenheuvel.eu/2016/03/04/extract-and-apply-templates-with-office-dev-pnp-provisioning-p...

How are you referencing the Files?

You could create a extention in your template and use this code:

Snippet

private void CreateFolders(ClientContext context, Web web, List list, Project.Provisioning.Models.ListConfigurationListFolder[] folders)
      {
          foreach (var folder in folders)
          {
              ListItem newItem;
 
              try
              {
                  // Create folder
                  ListItemCreationInformation itemCreateInfo = new ListItemCreationInformation();
 
                  itemCreateInfo.UnderlyingObjectType = FileSystemObjectType.Folder;
                  itemCreateInfo.LeafName = folder.Title;
 
                  newItem = list.AddItem(itemCreateInfo);
                  newItem["Title"= folder.Title;
                  newItem.Update();
                  context.ExecuteQueryRetry();
              }
              catch (Exception e)
              {
                  TraceHelper.WriteInformationToListener("LogFile"$"Couldn't create folder: {folder.Title}.");
                  continue;
              }
 
              if (folder.ObjectSecurity != null)
              {   // Set item security
                  var securitySettings = folder.ObjectSecurity.BreakRoleInheritance;
 
                  if (securitySettings.RoleAssignment.Length == 0continue;
 
                  try
                  {
 
                      var groups = context.LoadQuery(context.Web.SiteGroups.Include(g => g.LoginName));
                      var webRoleDefinitions = context.LoadQuery(context.Web.RoleDefinitions);
 
                      context.ExecuteQueryRetry();
 
                      newItem.BreakRoleInheritance(securitySettings.CopyRoleAssignments, securitySettings.ClearSubscopes);
 
                      foreach (var roleAssignment in securitySettings.RoleAssignment)
                      {
                          Principal principal = groups.FirstOrDefault(g => g.LoginName == roleAssignment.Principal);
                          if (principal == null)
                          {
                              principal = context.Web.EnsureUser(roleAssignment.Principal);
                          }
 
                          var roleDefinitionBindingCollection = new RoleDefinitionBindingCollection(context);
 
                          var roleDefinition = webRoleDefinitions.FirstOrDefault(r => r.Name == roleAssignment.RoleDefinition);
 
                          if (roleDefinition != null)
                          {
                              roleDefinitionBindingCollection.Add(roleDefinition);
                          }
                          newItem.RoleAssignments.Add(principal, roleDefinitionBindingCollection);
                      }
                      context.ExecuteQueryRetry();
                  }
                  catch (Exception e)
                  {
                      TraceHelper.WriteInformationToListener("LogFile""Error occured when setting itemlevel security on folder.");
 
                      continue;
                  }
              }
          }
      }

Hi Paul

 

What would the provisioning Template entries look like to support the folder hierarchy?

 

Regards

 

Nigel

hi Nigel,

 

in my code i did a big one this is a extract of the template:

   <def:ListConfiguration xmlns:def="http://tempuri.org/ListConfiguration">
              <def:SiteUrl>{parameter:SiteCollectionUrl}</def:SiteUrl>
              <def:Lists>
                <def:List Url="COM">
                  
                  <def:Folders>
                    <def:Folder Title="Estimates">
                      <def:ObjectSecurity>
                        <def:BreakRoleInheritance ClearSubscopes="false" CopyRoleAssignments="true">
                          <def:RoleAssignment Principal="Estimators" RoleDefinition="ContributeWithoutDelete"/>
                        </def:BreakRoleInheritance>
                      </def:ObjectSecurity>
                    </def:Folder>
                  </def:Folders>
                </def:List>
</def:Lists>
</def:ListConfiguration>    

Thanks Paul - very interesting - I will have to give it a go.  I have been looking to be able to this for a while.  Should there not be something in the pnp provisioning template which links your <def:...   to your code ?

Hi

 

I have used the sample ProvisioningSchema-2016-05-DirectorySample.xml provided here and it doesn't seem like the Convert-PnPFolderToProvisioningTemplate take the files in consideration when the package is made. I have referenced the files like this in the xml file.

 

  <pnp:Files>
        <pnp:Directory src="c:\temp\DirectoryBulkLoadSample" Folder="BulkLoaded" Overwrite="true"
            Recursive="true" IncludedExtensions="*.docx,*.pdf" ExcludedExtensions="*.xml,*.txt"
            MetadataMappingFile="c:\BulkLoadedMetadata.json" />
      </pnp:Files>

Yep I guess that I have to look at a custom extension. Too bad as it would have been nice if the Convert-PnPFolderToProvisioningTemplate command had packaged the files and folders in the same pnp package.

 

/Niclas

Hi Paul
 
@ErwinVanHunen's Blog says this is the call you make for the extension ->   


public void ProcessRequest(ClientContext ctx, ProvisioningTemplate template, string configurationData)
 
Your code has this ->
private void CreateFolders(ClientContext context, Web web, List list, Project.Provisioning.Models.ListConfigurationListFolder[] folders)
 
So I presume I have to convert between the two.
 
I can get web from the clientcontext but for the list I need to have the list name.  Where do I get that from ?
 
Thanks
 
Nigel