使用 PnP PowerShell 转换所选经典页面

Microsoft 365 评估工具会清点经典页面,但不会修改它们。 使用评估输出批准页面波次,并使用 PnP PowerShell ConvertTo-PnPPage 创建新式页面。

注意

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

页面转换工作流

  1. 从已完成的评估覆盖范围中选择具有代表性的页面波次。
  2. 解决阻止 Web 部件问题,然后选择就地或跨网站目标。
  3. 准备租户拥有的 PnP PowerShell 应用程序和所需的权限。
  4. 使用源保留默认值和日志记录转换代表波。
  5. 在展开波形之前验证每个生成的页面。

开始之前

完成以下先决条件:

  1. 运行并解释 经典页面评估
  2. 从成功的扫描覆盖范围中选择具有代表性的波形。
  3. 安装 PowerShell 7.4.0 或更高版本以及当前稳定的 PnP PowerShell 版本。
  4. 注册用于交互式 PnP PowerShell 的租户拥有的 Microsoft Entra 应用程序。
  5. 确认登录用户可以编辑源 Web 和目标 Web 中的页面。

自 2024 年 9 月 9 日起,交互式 PnP PowerShell 身份验证需要你自己的应用程序注册和客户端 ID。

必须允许注册应用程序的帐户创建应用注册。 租户同意策略决定管理员是否必须授予同意。

权限

使用与只读评估应用程序不同的委派应用程序。

转换要求 委派的 SharePoint 范围
读取源并创建、保存和发布新式页面 AllSites.Manage
同时复制项目级唯一权限 AllSites.FullControl

已登录用户的站点权限也适用。 应用程序的委派范围不会授予用户访问其无法访问的网站的权限。

使用 ,添加 AllSites.Manage以使 -SkipItemLevelPermissionCopyToClientSidePage 生成的页面从其库继承权限。 仅当必须保留页面的唯一权限时才使用 AllSites.FullControl

安装或更新 PnP PowerShell

PnP PowerShell 需要 PowerShell 7.4.0 或更高版本。

$PSVersionTable.PSVersion
Install-Module PnP.PowerShell -Scope CurrentUser

如果已安装 PnP PowerShell, Update-Module PnP.PowerShell 请从 PowerShell 7.4.0 或更高版本运行。

请参阅 安装 PnP PowerShell

注册交互式转换应用程序

以下命令创建用于交互式登录的公共客户端应用程序:

Register-PnPEntraIDAppForInteractiveLogin `
  -ApplicationName "Classic Page Transformation" `
  -Tenant "<tenant>.onmicrosoft.com" `
  -SharePointDelegatePermissions AllSites.Manage `
  -SignInAudience AzureADMyOrg

复制返回的应用程序 ID。 根据租户同意策略,管理员可能需要在第一次连接之前授予许可。

此公共客户端注册适用于委派交互式或设备登录。 它不提供无人参与身份验证所需的应用程序权限或证书。

有关手动注册和其他身份验证方法的信息,请参阅为 PnP PowerShell 注册 Entra ID 应用程序

对于由世纪互联运营的 Microsoft 365 运营的 GCC High、DoD 或 由世纪互联运营的 Microsoft 365,请在注册应用程序和连接时指定匹配-AzureEnvironment值。 请参阅 Register-PnPEntraIDAppForInteractiveLoginConnect-PnPOnline cmdlet 参考。

将评估行映射到 PnP PowerShell

评估字段 PnP PowerShell 使用
SiteUrl + WebUrl Connect-PnPOnlineURL 的源 URL。
ListUrl, ListId, PageUrl 确切 SitePages 的库和源文件标识。
PageType 必须为 WikiPageWebPartPage 对于此工作流。
Layout 代表性模式分组和验证。

在转换第一波页面之前,需要:

  • 成功的网站和网络覆盖。
  • PageType 等于 WikiPage OR WebPartPage
  • 无未解析的未映射 Web 部件。
  • WebPartCount 大于 0。 手动审核后处理此自动示例之外的零部分页面。
  • 从其库继承权限的源页。
  • 用于验证的录制内容和布局基线。

生成有代表性的页面组

以下脚本将创建一个候选清单。 它不会转换页面。

它按页面类型、布局和有序的 Web 部件签名对符合条件的 Wiki 和 Web 部件页面进行分组。 签名包括 Web 部件类型、映射结果、隐藏状态和关闭状态。

$pages = Import-Csv .\classicpages.csv
$webParts = Import-Csv .\classicpagewebparts.csv
$partsByPage = @{}

function Get-PageKey {
  param($Row)

  '{0}|{1}|{2}|{3}' -f $Row.ScanId, $Row.SiteUrl, $Row.WebUrl, $Row.PageUrl
}

foreach ($part in $webParts) {
  $key = Get-PageKey $part
  if (-not $partsByPage.ContainsKey($key)) {
    $partsByPage[$key] = [Collections.Generic.List[object]]::new()
  }

  $partsByPage[$key].Add($part)
}

$candidates = foreach ($page in $pages) {
  $fileName = [IO.Path]::GetFileName($page.PageUrl)

  if ($page.PageType -notin @('WikiPage', 'WebPartPage') -or
      $page.HomePage -eq 'True' -or
      -not $page.ListUrl.EndsWith('/SitePages', [StringComparison]::OrdinalIgnoreCase) -or
      $page.WebPartCount -eq '0' -or
      $page.MappingPercentage -ne '100' -or
      -not [string]::IsNullOrWhiteSpace($page.UnmappedWebParts) -or
      $fileName.StartsWith('Migrated_', [StringComparison]::OrdinalIgnoreCase) -or
      $fileName.StartsWith('Previous_', [StringComparison]::OrdinalIgnoreCase)) {
    continue
  }

  $key = Get-PageKey $page
  if (-not $partsByPage.ContainsKey($key)) {
    throw "Web Part rows are missing for $($page.PageUrl)."
  }

  $signature = (
    $partsByPage[$key] |
      Sort-Object { [int]$_.WebPartIndex } |
      ForEach-Object {
        '{0}|Mappable={1}|Hidden={2}|Closed={3}' -f
          $_.WebPartTypeShort, $_.IsMappable, $_.Hidden, $_.IsClosed
      }
  ) -join ';'

  [pscustomobject]@{
    PatternKey = '{0}|{1}|{2}' -f $page.PageType, $page.Layout, $signature
    ScanId = $page.ScanId
    SiteUrl = $page.SiteUrl
    WebUrl = $page.WebUrl
    PageUrl = $page.PageUrl
    PageType = $page.PageType
    ListUrl = $page.ListUrl
    ListId = $page.ListId
    AssessmentTimeZoneId = [TimeZoneInfo]::Local.Id
    Layout = $page.Layout
    HomePage = $page.HomePage
    WebPartCount = [int]$page.WebPartCount
    MappingPercentage = $page.MappingPercentage
    UnmappedWebParts = $page.UnmappedWebParts
    ModifiedAt = $page.ModifiedAt
    WebPartSignature = $signature
    IncludePattern = 'True'
    Selected = 'False'
    ExpectedVisibleContent = ''
    ValidationOwner = ''
  }
}

$groups = $candidates | Group-Object PatternKey -AsHashTable -AsString

$candidateInventory = foreach ($candidate in $candidates) {
  $candidate | Select-Object *,
    @{ Name = 'PatternPageCount'; Expression = { $groups[$candidate.PatternKey].Count } }
}

$candidateInventory |
  Sort-Object PatternKey, PageUrl |
  Export-Csv .\representative-page-groups.csv -NoTypeInformation

查看并 representative-page-groups.csv 从迁移波次将包含的每个 PatternKey 页面中至少选择一个页面。 当 Web 部件属性、链接的内容或业务行为在模式内存在重大差异时,请选择其他页面。

针对计划迁移之外的模式进行设置 IncludePattern=False 。 对于每个选定页面,设置 Selected=True 并填充 ExpectedVisibleContentValidationOwner

在生成评估 CSV 的同一台计算机上运行分组步骤。 AssessmentTimeZoneId 记录用于解释无 ModifiedAt 偏移值的时区。

将零部分页面、主页、发布页面和具有未解析映射的页面保留在单独的审阅队列中。 默认 SitePages 库之外的页面也保留在单独审阅的单页路径上。

了解源代码保留默认值

警告

-TakeSourcePageName 重命名经典源页面,并 -Overwrite 替换现有目标页面。 在批准生成的页面并存在回退计划之前,请勿使用这两个选项。

选项 第一波指南
默认命名 保留源名称并创建 Migrated_<source-page>.aspx.
-TakeSourcePageName 初次请勿使用。 它使用 Previous_ 前缀重命名经典源。
-Overwrite 初次请勿使用。 查看现有目标,而不是自动替换它。
-ReplaceHomePageWithDefault 不要在代表波中使用。
-DontPublish 用于第一波,这样生成的页面在验证期间仍为草稿。
-SkipItemLevelPermissionCopyToClientSidePage 与应用程序一起使用 AllSites.Manage ,除非必须复制唯一权限。

回退第一波草稿

批处理工作流不会重命名或覆盖经典源页面。 如果生成的草稿未通过验证:

  1. 保持经典源页处于服务状态。
  2. 保留 TransformationStatus=Created、 设置 ValidationStatus=Failed、 填充 ValidationNotesValidatedByValidatedAt
  3. 保留 TargetPageUrl 以及 LogPath 失败的验证证据。
  4. 当不再需要调查生成的草稿,并且组织的保留策略允许删除时,从网站页面库中回收生成Migrated_的草稿。
  5. 更正候选规则或修正,并在恢复波次之前重试一个代表页面。

-Overwrite 并且 -TakeSourcePageName 不在批处理工作流之外。 在单独的过程中使用任一选项之前,请保留页面版本和 URL,记录当前主页设置,在非生产站点中测试反向重命名或还原过程,并获得明确批准。

转换一个选定的 Wiki 或 Web 部件页面

使用具有代表性的页面脚本,其中一行标记为 Selected=True. 这会使单页测试与较大的波次保持在相同的验证、身份验证和结果协定上。

批处理脚本支持默认 SitePages 库中的 Wiki 和 Web 部件页面。 它们不包括主页、零部分页面、具有唯一权限的页面以及评估后修改的页面。 通过单独审阅的路径处理这些页面。

转换所有代表页

页面波形脚本引用 中的三个嵌入式文件保存在同一文件夹中。

在:representative-page-groups.csv

  1. 为计划迁移中的每个转换模式设置 IncludePattern=True
  2. 每个包含的模式至少设置 Selected=True 在一页上。
  3. 填充 ExpectedVisibleContent 并为 ValidationOwner 每个选定页面。

预览完整波次,无需身份验证或写入 SharePoint 页面:

.\Convert-RepresentativePages.ps1 `
  -ManifestPath .\representative-page-groups.csv `
  -ClientId "<application-id>" `
  -AuthenticationMode Interactive `
  -WhatIf `
  -Force

预览版 CSV 记录 PlannedActionPlannedTargetPageUrl. 因为不进行身份验证, TargetExists is -WhatIfNotChecked.

运行经过身份验证的只读预检,以验证当前源、主页、权限、时间戳和目标缺失:

.\Convert-RepresentativePages.ps1 `
  -ManifestPath .\representative-page-groups.csv `
  -ClientId "<application-id>" `
  -AuthenticationMode Interactive `
  -PreflightOnly `
  -Confirm:$false `
  -Force

实时转换前,每一行都必须报告 TransformationStatus=PreflightPassed

使用高影响确认以交互方式运行:

.\Convert-RepresentativePages.ps1 `
  -ManifestPath .\representative-page-groups.csv `
  -ClientId "<application-id>" `
  -AuthenticationMode Interactive `
  -Confirm

脚本会立即将每个结果写入到 representative-page-results.csv. 生成的页面仍为草稿,并且创建的每一行都以 ValidationStatus=Pending开头。

关注 验证转换后的经典页面。 仅在ValidationStatus=Passed页面满足每个接受条件后设置 、 ValidationNotesValidatedByValidatedAt

展开到所有用户指定的页面

通过从原始representative-page-groups.csv文件复制用户批准的其他包含行来创建approved-pages.csv。 保留每个生成的字段,并填充 ExpectedVisibleContentValidationOwner.

使用第一个命令运行 -PreflightOnly -Confirm:$false -Force 选定页脚本。 每一行都必须报告 PreflightPassed.

运行扩展脚本:

.\Convert-SelectedPages.ps1 `
  -PagesPath .\approved-pages.csv `
  -RepresentativeManifestPath .\representative-page-groups.csv `
  -RepresentativeResultsPath .\representative-page-results.csv `
  -ClientId "<application-id>" `
  -AuthenticationMode Interactive `
  -Confirm

在以下情况下,脚本拒绝展开:

  • 代表清单和结果不匹配。
  • 任何代表页面都不是 Created and Passed.
  • 缺少验证说明、验证程序或验证时间戳。
  • PnP PowerShell 版本或三个脚本文件中的任何一个都不同于代表运行。
  • 批准的页面不是原始清单中未更改的包含行。
  • 已批准的页面属于没有传递代表的模式。

展开的波次还会创建草稿页和写入。selected-page-results.csv 在发布这些页面之前对其进行验证。

需要显式审查的选项

使用生成的 ConvertTo-PnPPage cmdlet 参考 获取完整的参数协定。

批处理脚本不支持自定义 Web 部件映射、发布页面或跨网站目标。 通过单独审查的单页或高级过程处理这些场景。

仅在批处理工作流之外查看以下选项:

  • -CopyPageMetadata-KeepPageCreationModificationInformation
  • -UrlMappingFile、、 -UserMappingFile-TermMappingFile.
  • -Overwrite 以及 -TakeSourcePageName 批准和回滚计划之后。

新式页面功能疑难解答

页面波形脚本不支持 SharePoint 功能。 此疑难解答路径仅适用于受支持的经典团队网站。 不要在经典发布门户上启用该功能;将发布页面路由到单独的跨站点发布积压工作和模型。 请参阅 新式页面支持的自定义项

连接有权管理 Web 的帐户,并列出已激活的 Web 功能:

$source = Connect-PnPOnline `
  -Url "https://<tenant>.sharepoint.com/sites/<site>" `
  -Interactive `
  -ClientId "<application-id>" `
  -ReturnConnection

$modernPageFeatureId = [guid]'B6917CB1-93A0-4B97-A84D-7CF49975D4EC'
$modernPageFeature = Get-PnPFeature `
  -Scope Web `
  -Connection $source |
  Where-Object DefinitionId -eq $modernPageFeatureId

if ($modernPageFeature) {
  Write-Host "Modern pages feature is active."
}
else {
  Write-Host "Modern pages feature is not active."
}

批处理脚本使用的委派 AllSites.Manage 应用程序不足以激活 Web 功能。 如果该功能在受支持的经典团队网站上未处于活动状态,请获取网站所有者批准并使用:

  • 具有 SharePoint AllSites.FullControl的单独委派应用程序。
  • 在 Web 上具有完全控制权的已登录帐户。
$adminConnection = Connect-PnPOnline `
  -Url "https://<tenant>.sharepoint.com/sites/<site>" `
  -Interactive `
  -ClientId "<full-control-application-id>" `
  -ReturnConnection

Enable-PnPFeature `
  -Identity $modernPageFeatureId `
  -Scope Web `
  -Connection $adminConnection

启用该功能后,重新运行经过身份验证的预检。

无人参与身份验证和其他页面类型

除非单独配置为仅应用访问,否则无法将之前创建的委派公共客户端应用程序重复用于证书仅应用身份验证。

在无人参与执行之前:

  1. 按照以下操作创建单独的仅限应用的注册和证书:注册 Entra ID 应用程序以进行仅应用访问
  2. 通过以下方式为每个源网站配置 SharePoint 应用程序 权限或网站分配: 确定所需的 PnP PowerShell 权限
  3. 授予所需的管理员同意。
  4. 使用 -PreflightOnly -Confirm:$false 和证书身份验证模式运行批处理脚本。
  5. -PreflightOnly仅在经过身份验证的预检在测试租户中通过后删除。

重要

证书参数路径经过 Pester 测试,但第 2 阶段实时租户运行的委派交互式身份验证已验证。 在无人参与写入之前,验证租户中所选的仅应用权限配置文件。

批处理脚本不支持发布页面、博客页面、外部 SitePages页面、主页、自定义 Web 部件映射和 SharePoint 服务器 源。 使用这些高级参考:

后续步骤

  1. 验证每个转换后的页面
  2. Convert-SelectedPages.ps1仅在每个具有代表性的页面通过验证后运行。

参考