PnP 预配引擎简介

本文介绍了 PnP 预配引擎,它最初于 2015 年 4 月在 OfficeDev PnP 项目内发布,并将每月更新一次,以与 Office Dev PnP 核心库的发布计划保持一致。

注意

PnP 预配框架 & PnP 预配引擎是开放源代码解决方案,其活动社区为其提供支持。 没有用于 Microsoft 开放源代码工具支持的 SLA。

目标

将从使用预配引擎的主要目标开始介绍。 由于 Microsoft Office 365 和 Microsoft SharePoint Online 已推出,开发人员面临一种新选择,即通常情况下使用云外接程序模型创建 Microsoft SharePoint、SharePoint Online 和 Office 365 的自定义软件解决方案。 而在过去,开发人员是使用基于 CAML/XML 的功能框架预配自定义项目(通过完全信任代码 (FTC) 解决方案或沙盒解决方案),但在新的云外接程序模型中,应使用“远程预配”技术完成此操作。 远程预配意味着要做什么呢? 这意味着,要使用其中一个可用的 API(REST 或 CSOM)预配项目,而不是使用功能框架。

若要使用测试和生产环境对项目进行建模和预配,或若要自动预配项目,仅仅是因为要将自定义出售给多个客户,该怎么办? 同样,若要定义自定义网站模板,以便可以跨多个网站实例(面向客户的网站或面向项目的网站)重用它,该怎么办?

使用新的 PnP 预配引擎,你可以通过配置网站栏、内容类型、列表定义和实例、页面等的设计,通过 Web 浏览器对网站进行建模。 设计完成后,你可以将完成的设计导出为预配模板格式(XML、JSON 或称为 PnP 文件的容器格式),并将该模板应用于任何你想应用的目标网站。

但是,如果要预配其他项目(例如 Microsoft Teams),该怎么办? 或 Azure AD 用户? PnP 预配引擎可以为你执行此操作。

两种模板类型

引擎基本上可以识别两种类型的模板:网站模板(也称为预配模板)和扩展版本:租户模板

最初引入引擎时,唯一可用的模板类型是“网站模板”。 几年后,我们引入了租户模板,该模板将其与在 SharePoint 网站范围之外预配项目的功能区分开来。 例如,租户模板允许你预配 Microsoft Teams 团队、Azure AD 用户、网站设计和网站脚本、租户范围内的主题等。与网站模板不同,你可以在租户模板中创建所谓的“序列”并创建网站集。

简而言之,租户模板是一种包含可预配到租户级别的项目的网站模板。

创建网站模板

如上文所述,创建自定义预配模板的最简单方法是,在 SharePoint Online 中创建全新的网站集,配置项目(网站栏、内容类型、列表、页面等),并将结果另存为预配模板。

假设你定义了一个包含自定义主页的示例网站:

模板网站的主页

除了自定义主页外,你还在现成的“活动”列表中创建了一些活动:

活动列表中的自定义活动

若要将该网站导出为预配模板,可以使用 PowerShell 或 CSOM 代码,以及 OfficeDev PnP 核心库提供的一些扩展方法。

使用 PowerShell cmdlet

注意

本文重点介绍如何将 PnP PowerShell 与预配引擎一起使用。 如果你更喜欢使用 C#,请参阅 PnP 预配引擎和核心库

注意

PnP PowerShell 是一种开放源代码解决方案,其中包含为其提供支持的活动社区。 没有用于 Microsoft 开放源代码工具支持的 SLA。

若要将 PowerShell cmdlet 用于 SharePoint Online 或 SharePoint,请转到“PnP PowerShell 概述”,并安装 SharePoint PnP PowerShell 模块。

使用 Connect-PnPOnline cmdlet 将 PowerShell 环境连接到 SharePoint Online 后,可使用以下 PowerShell cmdlet:

Get-PnPSiteTemplate -Out "PnP-Provisioning-File.xml"

–Out 参数指示 cmdlet 将在哪里保存预配模板。

根据你在 cmdlet 中使用的扩展名(目前我们支持 .xml 和 .pnp),提取并保存模板的结果是一个文件。 如果你选择将模板另存为 XML 文件,则其外观将如下所示(请注意,该模板不完整并且只是 XML 结构的一个示例):

注意

有许多配置选项可设置为提取模板。 有关此操作的说明,请参阅配置 PnP 预配引擎

   <?xml version="1.0"?>
<pnp:Provisioning xmlns:pnp="http://schemas.dev.office.com/PnP/2019/09/ProvisioningSchema">
  <pnp:Preferences Generator="OfficeDevPnP.Core, Version=3.14.1910.1, Culture=neutral, PublicKeyToken=null" />
  <pnp:Templates ID="CONTAINER-TEMPLATE-8F4D883BE25B442FB9F889C351D3EA0B">
    <pnp:ProvisioningTemplate ID="TEMPLATE-8F4D883BE25B442FB9F889C351D3EA0B" Version="1" BaseSiteTemplate="SITEPAGEPUBLISHING#0" Scope="RootSite">
      <pnp:WebSettings RequestAccessEmail="" NoCrawl="false" WelcomePage="SitePages/Home.aspx" SiteLogo="{hosturl}{site}/SiteAssets/__sitelogo___sitelogo_theperspective.png"/>
      <pnp:Navigation AddNewPagesToNavigation="true" CreateFriendlyUrlsForNewPages="true">
        <pnp:CurrentNavigation NavigationType="StructuralLocal">
          <pnp:StructuralNavigation RemoveExistingNodes="true">
            <pnp:NavigationNode Title="Who we are" Url="http://linkless.header/" IsExternal="true"/>
            <pnp:NavigationNode Title="What's happening" Url="http://linkless.header/" IsExternal="true"/>
            <pnp:NavigationNode Title="Find it" Url="http://linkless.header/" IsExternal="true"/>
          </pnp:StructuralNavigation>
        </pnp:CurrentNavigation>
      </pnp:Navigation>
      <pnp:Lists>
        <pnp:ListInstance Title="Events" Description="" DocumentTemplate="" TemplateType="106" Url="Lists/Events" MinorVersionLimit="0" MaxVersionLimit="0" DraftVersionVisibility="0" TemplateFeatureID="00bfea71-ec85-4903-972d-ebe475780106" ContentTypesEnabled="true" EnableFolderCreation="false" DefaultDisplayFormUrl="{site}/Lists/Events/DispForm.aspx" DefaultEditFormUrl="{site}/Lists/Events/EditForm.aspx" DefaultNewFormUrl="{site}/Lists/Events/NewForm.aspx" ImageUrl="/_layouts/15/images/itevent.png?rev=44" IrmExpire="false" IrmReject="false" IsApplicationList="false" ValidationFormula="" ValidationMessage="">
        </pnp:ListInstance>
        <pnp:DataRows KeyColumn="Title" UpdateBehavior="Overwrite">
          <pnp:DataRow>
            <pnp:DataValue FieldName="Title">Thanksgiving</pnp:DataValue>
            <pnp:DataValue FieldName="fAllDayEvent">true</pnp:DataValue>
            <pnp:DataValue FieldName="EventDate">2019-11-28 00:00:00</pnp:DataValue>
            <pnp:DataValue FieldName="EndDate">2019-11-28 23:59:00</pnp:DataValue>
          </pnp:DataRow>
          <pnp:DataRow>
            <pnp:DataValue FieldName="Title">In the design lab with Carlos Slattery</pnp:DataValue>
            <pnp:DataValue FieldName="Location">Contoso HQ</pnp:DataValue>
            <pnp:DataValue FieldName="fAllDayEvent">false</pnp:DataValue>
            <pnp:DataValue FieldName="EventDate">2020-01-02 10:00:00</pnp:DataValue>
            <pnp:DataValue FieldName="EndDate">2020-01-02 12:00:00</pnp:DataValue>
          </pnp:DataRow>
        </pnp:DataRows>
      </pnp:Lists>
      <pnp:ClientSidePages>
        <pnp:ClientSidePage PromoteAsNewsArticle="false" PromoteAsTemplate="false" Overwrite="true" Layout="Home" EnableComments="false" Title="Home" ThumbnailUrl="" PageName="Home.aspx" LCID="0">
          <pnp:Header Type="Default" LayoutType="FullWidthImage" TextAlignment="Center" ShowTopicHeader="false" ShowPublishDate="false" TopicHeader="" AlternativeText="" Authors="[]" AuthorByLine="[]" AuthorByLineId="-1" />
          <pnp:Sections>
            <pnp:Section Order="1" Type="OneColumnVerticalSection" VerticalSectionEmphasis="Soft">
              <pnp:Controls>
                <pnp:CanvasControl WebPartType="News" JsonControlData="{&quot;id&quot;: &quot;8c88f208-6c77-4bdb-86a0-0c47b4316588&quot;, &quot;instanceId&quot;: &quot;1ac6db3e-eb95-4d5d-a991-28ee34772313&quot;, ..." ControlId="8c88f208-6c77-4bdb-86a0-0c47b4316588" Order="1" Column="1" />
                <pnp:CanvasControl WebPartType="News" JsonControlData="{&quot;id&quot;: &quot;8c88f208-6c77-4bdb-86a0-0c47b4316588&quot;, &quot;instanceId&quot;: &quot;e5fc83c0-3350-4eea-9606-6627646a0a4b&quot;, &quot;title&quot;: &quot;News&quot;, &quot;description&quot;: &quot;..." ControlId="8c88f208-6c77-4bdb-86a0-0c47b4316588" Order="2" Column="1" />
                <pnp:CanvasControl WebPartType="Custom" JsonControlData="{&quot;id&quot;: &quot;868ac3c3-cad7-4bd6-9a1c-14dc5cc8e823&quot;, &quot;instanceId&quot;: &quot;c1524d29-ab2a-44a3-809e-c01c3762c4ee&quot;, &quot;title&quot;: &quot;Weather&quot;, &quot;description&quot;:..." ControlId="868ac3c3-cad7-4bd6-9a1c-14dc5cc8e823" Order="1" Column="2" />
              </pnp:Controls>
            </pnp:Section>
            <pnp:Section Order="2" Type="OneColumn">
              <pnp:Controls>
                <pnp:CanvasControl WebPartType="CallToAction" JsonControlData="{&quot;id&quot;: &quot;df8e44e7-edd5-46d5-90da-aca1539313b8&quot;, &quot;instanceId&quot;: &quot;b022816d-c9de-4989-aa10-9b44bec4a872&quot;, &quot;title&quot;: &quot;Call to action&quot;, &quot;description&quot;: &quot;Call to action&quot;, &quot;dataVersion&..." Order="1" Column="1" />
              </pnp:Controls>
            </pnp:Section>
            <pnp:Section Order="3" Type="ThreeColumn">
              <pnp:Controls>
                <pnp:CanvasControl WebPartType="Image" JsonControlData="{&quot;id&quot;: &quot;d1d91016-032f-456d-98a4-721247c305e8&quot;, &quot;instanceId&quot;: &quot;984b089c-ca62-4f92-89c0-1ce0e1cb6c03&quot;, &quot;title&quot;: &quot;Image&quot;, &quot;description&quot;: &quot;Image&quot;, &quot;dataVersion&quot;: &quot;1.8&quot;, &quot;..." ControlId="d1d91016-032f-456d-98a4-721247c305e8" Order="1" Column="1" />
                <pnp:CanvasControl WebPartType="Text" ControlId="5755396c-b272-4c0f-8e8d-eb41d218a10e" Order="2" Column="1">
                  <pnp:CanvasControlProperties>
                    <pnp:CanvasControlProperty Key="Text" Value="&lt;p&gt;&lt;span class=&quot;fontColorThemePrimary&quot;&gt;&lt;span class=&quot;fontSizeMediumPlus&quot;&gt;&lt;strong&gt;BREATHTAKING VIDEOS &amp;amp; PHOTOS&lt;/strong&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;&#xA;&#xA;&lt;p&gt;..." />
                  </pnp:CanvasControlProperties>
                </pnp:CanvasControl>
                <pnp:CanvasControl WebPartType="Button" JsonControlData="{&quot;id&quot;: &quot;0f087d7f-520e-42b7-89c0-496aaf979d58&quot;, &quot;instanceId&quot;: &quot;deb39e2b-11a0-4141-8ac1-1078fe7cc392&quot;, &quot;title&quot;: &quot;..." ControlId="0f087d7f-520e-42b7-89c0-496aaf979d58" Order="3" Column="1" />
                <pnp:CanvasControl WebPartType="Image" JsonControlData="{&quot;id&quot;: &quot;d1d91016-032f-456d-98a4-721247c305e8&quot;, &quot;instanceId&quot;: &quot;e0b59b5b-8a5a-406e-9deb-6e6f9de4bd3b&quot;, &quot;title&quot;: &quot;Image&quot;, ..." ControlId="d1d91016-032f-456d-98a4-721247c305e8" Order="1" Column="2" />
               </pnp:Controls>
            </pnp:Section>
          </pnp:Sections>
        </pnp:ClientSidePage>
      </pnp:ClientSidePages>
      <pnp:Header Layout="Compact" MenuStyle="MegaMenu" BackgroundEmphasis="Strong" />
      <pnp:Footer Enabled="true" RemoveExistingNodes="false" />
    </pnp:ProvisioningTemplate>
  </pnp:Templates>
</pnp:Provisioning>

可以看到,XML 元素非常直观明了。 此示例中使用的 XML 架构引用 201909 版本的 PnP 预配架构,此架构与 SharePoint PnP 社区一同定义,位于 GitHub 中的 PnP 预配架构。 在同一存储库内,还有 Markdown (MD) 自动生成文档,其中介绍了可用于手动定义 XML 预配模板的主要元素、类型和属性。

将由你手动定义 ProvisioningTemplate(使用模型站点或撰写针对 PnP 预配 XSD 架构进行验证的 XML 文档,或只编写 .NET 代码并构建对象的层次结构)。 你甚至还可以混合使用这些方法:你可以使用模型站点设计预配模板,然后将其另存为一个 XML 文件并进行一些内存自定义,同时处理代码中的 ProvisioningTemplate 实例。

应用预配模板

现在,你已了解什么是预配模板,并且可以将其应用到目标网站。

假设你已经在 SharePoint Online 中创建了另一个新的通信网站集,如下图所示。

创建新网站集的 SharePoint Online 页

默认情况下,该网站可能如下图所示(是通信网站的默认布局)。

全新目标的主页

现在可以使用 PnP PowerShell cmdlet 应用自定义网站模板

Connect-PnPOnline -Url "https://yourtenant.sharepoint.com/sites/targetcommunicationsite"
Invoke-PnPSiteTemplate -Path "PnP-Provisioning-File.xml"

–Path 参数是指 cmdlet 自动应用于当前已连接网站(由 Connect-PnPOnline cmdlet 表示)的源模板文件。

注意

经验法则是,当你应用网站模板时,需要创建目标网站并使其正常工作。 如果想要通过模板随时创建网站,则必须创建租户模板。 有关租户模板的详细信息,请参阅下文。

应用租户模板

租户模板与站点模板非常相似,只是增加了几个元素,其中一个重要元素是序列。

序列是指要创建的一个或多个网站集的配置。 查看以下模板摘录

<pnp:Provisioning xmlns:pnp="http://schemas.dev.office.com/PnP/2019/09/ProvisioningSchema" Author="John White" Generator="Human being" Version="1.0" Description="Home Site" DisplayName="The Perspective">
  <pnp:Sequence ID="sequence">
    <pnp:SiteCollections>
      <pnp:SiteCollection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="pnp:CommunicationSite" ProvisioningId="MAIN" SiteDesign="Topic" Title="My New Site" Description="" Url="/sites/mynewsite" IsHubSite="false" Owner="user@domain.com">
        <pnp:Templates>
          <pnp:ProvisioningTemplateReference ID="MAIN-TEMPLATE"/>
        </pnp:Templates>
      </pnp:SiteCollection>
    </pnp:SiteCollections>
  </pnp:Sequence>
  <pnp:Templates ID="SITE-TEMPLATES">
    <pnp:ProvisioningTemplate ID="MAIN-TEMPLATE" Version="1" BaseSiteTemplate="SITEPAGEPUBLISHING#0" Scope="RootSite">
      <pnp:Header Layout="Compact" MenuStyle="MegaMenu" BackgroundEmphasis="Strong" />

如你所见,序列与 <pnp:Templates /> 元素在相同的级别上定义。 序列可以包含一个或多个网站,也可以定义子网站。 对于每个网站,你可以引用一个或多个模板,以便在创建网站后进行应用。 通过模板 ID 引用该模板,并且此示例中的模板位于同一 XML 文件中。

若要将租户模板应用于你输入的租户:

Connect-PnPOnline https://yourtenant.sharepoint.com
Invoke-PnPTenantTemplate -Path "yourtenanttemplate.xml"

有关租户模板的详细信息,请参阅 PnP 预配租户模板

高级主题

这只是一篇介绍性文章;需要强调的重要一点是,使用 PnP 预配引擎时,你也可以预配分类,以及使用变量和令牌进行,它们可以基于你预配的内容(列表 ID、参数、术语 ID 等)在运行时替换。 你可以从计时器作业服务、提供程序托管的外接程序、外部站点等调用预配引擎。 最后,你可以使用 PnP 预配引擎将项目从测试/暂存环境移到生产环境。

确保查看其他文章以获取更多高级主题。

要求和总结

为了能够在本地使用 PnP 预配引擎,至少需要安装 2015 年 3 月发布的 SharePoint 2013 累积更新,因为预配引擎利用了客户端对象模型的一些功能(旧版产品中没有的功能)。 如果以 SharePoint Online 为目标,那么将自动满足要求(归功于服务型软件模型)。

请使用 PnP 预配引擎,向我们提供反馈,并在今后使用 SharePoint 外接程序模型和远程预配!

另请参阅