[Sharepoint]PnP.PowerShellを利用したSharepointページ操作のチートシート

前提条件

以下の準備編が完了し、VS Code上で、PnP.PowerShellのライブラリが実行可能な状態になっていること。

[Sharepoint]PnP.PowerShellを利用してSharePointのページにアクセスする(準備編)

また、以降の全てのサンプルの前段には、下記の認証のコードが含まれていることとする。

$siteUrl = "https://xxxxxxx.sharepoint.com/sites/sub-site-name"
$clientId = "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx"
Connect-PnPOnline -Url $siteUrl -ClientId $clientId

PnPのコマンドレットリファレンス

コマンドレットの一覧は以下のgithubから参照できる

PnP PowerShell cmdlets | PnP PowerShell ※1

 

ページの一括取得

全てのページを取得する (Get-PnPPage)

(公式リファレンス)

$pages = Get-PnPPage

取得結果

オブジェクトの型はMicrosoft.Online.SharePoint.TenantAdministration.SPSitePage

※ 配列の2要素目以降は省略している

[
    {
      "Name": "Home.aspx",
      "Title": "Home",
      "CreatedDateTime": "2025-10-24T15:15:53Z",
      "LastModifiedDateTime": "2025-10-29T05:21:12Z",
      "UniqueId": "e8fc88ec-5917-401e-b47d-xxxxxxxxxxxxx",
      "TypeId": "{e883bb25-7c77-452e-8e4d-xxxxxxxxxxxxx}"
    }
]

 

特定のページを取得する (Get-PnPPage)

(公式リファレンス)

-Identityページ名を指定することで、1ページ当たりの情報をより詳細に取得することができる。(ただしページIDでの指定はできない)

具体的には、ページ上のセクションとWebパーツ(テキストやリンクなどのGUI要素)へアクセスができる。

# 全てのページを取得する
$pages = Get-PnPPage
foreach ($p in $pages) {
    # ファイル名を指定しページを取得する
    $page = Get-PnPPage -Identity $p.Name
}

取得結果

オブジェクトの型はPnP.Core.Model.SharePoint.Page

全てのページを取得する場合と型が異なる。

※ プロパティが大量にあるため、重要そうなプロパティのみ抜粋

{
    "PageId": "1",
    "PageTitle": "Home",
    "Name": "Home.aspx",
    "Folder": "",
    "ThumbnailUrl": "",
    "LayoutType": "Home",
    "Sections": [],
    "Controls": {
        "8c88f208-6c77-4bdb-86a0-0c47b4316588": [
            {
                "HtmlPropertiesData": "",
                "WebPartId": "8c88f208-6c77-4bdb-86a0-0c47b4316588",
                "Title": "ニュース",
                "PropertiesJson": "{\"layoutId\":\"FeaturedNews\",\"dataProviderId\":\"news\",\"emptyStateHelpItemsCount\":\"1\",\"showChrome\":true,\"carouselSettings\":{\"autoplay\":false,\"autoplaySpeed\":5,\"dots\":true,\"lazyLoad\":true},\"showNewsMetadata\":{\"showSocialActions\":false,\"showAuthor\":true,\"showDate\":true,\"showSiteTitle\":true},\"isTitleEnabled\":true,\"isChromeModified\":true,\"minimumLayoutWidth\":22,\"newsDataSourceProp\":1,\"carouselHeroWrapperComponentId\":\"\",\"prefetchCount\":4,\"filters\":[{\"filterType\":1,\"value\":\"\",\"values\":[]}],\"newsSiteList\":[],\"renderItemsSliderValue\":4,\"layoutComponentId\":\"\",\"webId\":\"00621056-fc8f-4836-8fc6-d90fd6afe489\",\"siteId\":\"bd8b50ec-738d-409c-92c1-66a8882b92b0\",\"filterKQLQuery\":\"\"}",
                "ControlType": "3",
                "Type": {
                    "FullName": "PnP.Core.Model.SharePoint.PageWebPart",
                    "Name": "PageWebPart"
                },
                "InstanceId": "f7bfdec9-09c5-4fb6-bc97-xxxxxxxxxxxx"
            }
        ]
    }
}

 

全てのページを取得する (Get-PnPListItem)

(公式リファレンス)

$items = Get-PnPListItem -List "SitePages"

取得結果

オブジェクトの型はMicrosoft.SharePoint.Client.ListItem

※ プロパティが大量にあるため、重要そうなプロパティのみ抜粋している

※ 配列の2要素目以降は省略している

[
	  {
	    "FieldValues": {
	        "ID": 1,
	        "GUID": "128c95df-beac-43ba-90c3-xxxxxxxxxx",
	        "UniqueId": "e8fc88ec-5917-401e-b47d-xxxxxxxxxx",
	        "FileLeafRef": "Home.aspx",
	        "File_x0020_Type": "aspx",
	        "FileRef": "/sites/test-site001/SitePages/Home.aspx",
	        "FileDirRef": "/sites/test-site001/SitePages",
	        "PageLayoutType": "Home",
	        "File_x0020_Size": 805,
	        "Created": "2025/10/24 15:15:52",
	        "Modified": "2025/10/29 5:21:12",
	        "_CommentFlags": 1,
	        "_CommentCount": "",
	        "CanvasContent1": "
    }
]

 

FieldValuesの省略

.FieldValuesは省略して記述することができる。

ListItem型は[]演算子をオーバーロードしており、内部でFieldValuesを返却しているからと思われる。

# $a と $bは等価である
$a = $item.FieldValues["FileLeafRef"]
$b = $item["FileLeafRef"]

ページ情報の取得

ページのメタデータを取得する

$items = Get-PnPListItem -List "SitePages"
foreach ($item in $items) {
    # ID, ファイル名, ファイルパス, タイトル
    $fileID = $item["ID"]
    $fileName = $item["FileLeafRef"]
    $filePath = $item["FileRef"]
    $title = $item["Title"]
}

ページ全体のHTML構造を取得する

$items = Get-PnPListItem -List "SitePages"
foreach ($item in $items) {
    # HTML形式のコンテンツを取得する
    $html = $item["CanvasContent1"]
}}

ページ全体のHTML構造をプレーンテキストに変換

Get-PnPPageではWebパーツごとの情報しか取得できないため、Get-PnPListItem["CanvasContent1"]へアクセスをする必要がある。

このプロパティはページ全体を表すhtml構造の情報をテキスト形式で取得することができる。

$items = Get-PnPListItem -List "SitePages"
foreach ($item in $items) {
    # HTML形式のコンテンツを取得する
    $html = $item["CanvasContent1"]
    # タグを削除する
    $tagRemovedText = $html -replace "<[^>]+>", "`r`n"
    # デコードを行う
    $decodedText = [System.Web.HttpUtility]::HtmlDecode($tagRemovedText)
    # 連続した改行を削除
    $rowText = $decodedText -replace "(`r`n){2,}", "`r`n"
}

実行結果

整形前 (非常に長いので一部のみ抜粋)

"<div><div data-sp-canvascontrol="" data-sp-canvasdataversion="1.0" data-sp-controldata="&#123;&quot;position&quot;&#58;&#123;&quot;layoutIndex&quot;&#58;1,&quot;zoneIndex&quot;&#58;1,&quot;zoneId&quot;&#58;&quot;d8a599c0-f02c-49ac-b484-2bab9c442878&quot;,&quot;sectionIndex&quot;&#58;1,&quot;sectionFactor&quot;&#58;0,&quot;controlIndex&quot;&#58;1&#125;,&quot;id&quot;&#58;&quot;d073a475-6c3b-4a36-b0df-39f78a314cc7&quot;,&quot;controlType&quot;&#58;3,&quot;isFromSectionTemplate&quot;&#58;false,&quot;addedFromPersistedData&quot;&#58;true,&quot;webPartId&quot;&#58;&quot;cbe7b0a9-3504-44dd-a3a3-0e5cacd07788&quot;,&quot;reservedWidth&quot;&#58;1268,&quot;reservedHeight&quot;&#58;314&#125;">/* Your code... */

整形後 (AIを利用して整形 & プロパティは極力削除)

<div>
    <div>
        <div>
            <h2>テストページ</h2>
        </div>
        <div>
            <img src="/_layouts/15/images/sleektemplateimagetile.jpg">
        </div>
    </div>
    <div>
        <p>1つ目のテキストです</p>
        <p><strong>太字です</strong></p>
    </div>
    <div>
        <h2>2つ目のテキストです</h2>
        <p><span>色付き文字です</span></p>
    </div>
    <div>
        <p>3つ目のテキストです</p>
        <p><span>背景が付いています</span></p>
    </div>
    <div>
        <div>
            <div>Home</div>
            <div>FAQPage</div>
            <div>よく寄せられる質問</div>
            <a href="/sites/test-site001"></a>
            <a href="/sites/test-site001/SitePages/ITHelpdeskHome.aspx"></a>
            <a href="/sites/test-site001/SitePages/FAQPage.aspx"></a>
            <a href="/sites/test-site001/SitePages/Frequently-asked-questions.aspx"></a>
        </div>
    </div>
</div>

テキスト変換

1つ目のテキストです
太字です
2つ目のテキストです
色付き文字です 
3つ目のテキストです
背景が付いています 
Home
FAQPage
よく寄せられる質問

利用ページのレイアウト

 

 

Webパーツの操作

テキストを取得する

$page = Get-PnPPage -Identity "テストページ.aspx"
$page.Controls[i].Text

実行結果

<p class="noSpacingAbove spacingBelow" data-text-type="withSpacing">1つ目のテキストです</p><p class="noSpacingAbove spacingBelow" data-text-type="withSpacing"><strong>太字です</strong></p>

セクション順序, 列順序, セクション内IDを取得する

各Webパーツは以下の座標情報を持っている。

  1. セクション順序:何番目のセクションか
  2. 列順序:セクション内で、左から何番目の要素か
  3. 順序:セクション内で、上から何番目の要素か
$page = Get-PnPPage -Identity "テストページ.aspx"
# 相対的なWebパーツを取得する
foreach ($control in $page.Controls) {
    $obj = [PSCustomObject]@{
        sectionOrder = $control.Section.Order
        columnOrder = $control.Column.Order
        order = $control.Order
        text = $control.Text
    }
    Write-Output($obj)
}

実行結果

sectionOrder | columnOrder | order | text
-------------|-------------|-------|--------------------------------------------------------------------------------
1       | 1       | 1   | 
2       | 1       | 1   | <p class="noSpacingAbove spacingBelow" data-text-type="withSpacing">1つ目のテ...
2       | 1       | 2   | <p class="noSpacingAbove spacingBelow" data-text-type="withSpacing">3つ目のテ...
2       | 2       | 1   | ■更新対象■
2       | 2       | 2   | <p class="noSpacingAbove spacingBelow" data-text-type="withSpacing">4つ目のテ...
2       | 3       | 1   | <p class="noSpacingAbove noSpacingBelow" data-text-type="noSpacing">2つ目のテ...
3       | 1       | 1   | 
3       | 2       | 1   | リンクの隣にあるテキストです

利用ページのレイアウト

 

テキストを更新する (座標指定)

Set-PnPPageTextPartコマンドレットを利用する。(公式リファレンス)

Webパーツを更新するにはWebパーツのID(インスタンスID)を特定する必要がある

$page = Get-PnPPage -Identity "テストページ.aspx"
foreach ($control in $page.Controls) {
    if ($control.Text -eq "■更新対象■") {
        # テキストが「■更新対象■」であるWebパーツの1つ下のWebパーツを取得する
        $target = $page.Controls | Where-Object {
            $_.Section.Order -eq $control.Section.Order -and
            $_.Column.Order  -eq $control.Column.Order  -and
            $_.Order         -eq ($control.Order + 1)
        } | Select-Object -First 1

        # Webパーツのテキストを「更新しましたよ」で更新する
        Set-PnPPageTextPart `
            -Page $page `
            -InstanceId $target.InstanceId `
            -Text "更新しましたよ"
    }
}
$page.Save()
$page.Publish()

実行結果

テキストを更新する (インスタンスID指定)

ページをメンテナンスモードで開くことで、インスタンスIDを直接確認することもできる。

メンテナンスモードの実行方法

URLの末尾に「?maintenanceMode=true」を加える。

  • 通常
    https://{site-url}/sites/test-site001/SitePages/ページ名.aspx
  • メンテナンスモード
    https://{site-url}/sites/test-site001/SitePages/ページ名.aspx?maintenanceMode=true

実行結果

ただし、Webパーツの種類がテキストに限っては、インスタンスIDは表示されないようだ。

よって、この方法を採用することはできない

 

Webパーツのテキストを追加する

Add-PnPPageTextPartコマンドレットを利用する。(公式リファレンス)

$page = Get-PnPPage -Identity "テストページ.aspx"
Add-PnPPageTextPart `
    -Page $page `
    -Text "<p>新しいテキストを<strong>座標指定で</strong>追加しますよ.</p>" `
    -Section 2 `
    -Column 2 `
    -Order 1

実行結果

 

注意点

リファレンスを見る限り、Section,Column,Orderの座標パラメータは必須ではないが、省略すると以下のエラーが発生する。

You cannot host text controls inside a one column full width section, only an image web part or hero web part are allowed
MethodInvocationException:

レイアウトが全幅タイプ?なセクションには画像とヒーローWebパーツしか配置できませんよだそうだ。

実質、座標の指定は必須となる。

 

ページを保存する

$page.Save()

ページを公開する

# Get-PnPPageで取得した「PnP.Core.Model.SharePoint.Page」オブジェクトのPublishメソッドを呼び出す場合
$page.Publish()

# ファイル名を指定する場合
Set-PnPPage -Identity "ページ名.aspx" -Publish

ページ操作

ページのタイトルを変更する

Set-PnPPageコマンドレットを利用する。(公式リファレンス)

# ページのタイトルを変更する
$pageName = "テストページ.aspx"
$newTitle = "new title!"
Set-PnPPage -Identity $pageName -Title $newTitle

ページを作成する

Add-PnPPageを利用する。(公式リファレンス)

# サイトページ直下に作成する
Add-PnPPage -Name "NewPage1" -Publish

# サイトページの特定のディレクトリ内に作成する
Add-PnPPage -Name "テストフォルダ/NewPage2" -Publish

# タイトルを指定する
Add-PnPPage -Name "NewPage3" -Title "新しいタイトル" -Publish

ページをコピーする

テンプレートのページを複製するというコマンドレットはないので、実質コピーが複製に当たる。

Copy-PnPFileを利用する。(公式リファレンス)

# ページ名
$templatePage = "テストページ.aspx"
$newPage = "テストページ_copied.aspx"

# テンプレートをコピーする
Copy-PnPFile `
    -SourceUrl "SitePages/$templatePage" `
    -TargetUrl "SitePages/$newPage" `
    -Force

# 公開する
Set-PnPPage -Identity $newPage -Publish

ファイル名を変更する

Rename-PnPFileコマンドレットを利用する。(公式リファレンス)

# 変換前と変換後のファイル名
$oldFileName = "テストページ.aspx"
$newFileName = "テストページ_updated.aspx"

# 指定のファイル名を持つページを取得する
$item = Get-PnPListItem -List "SitePages" | Where-Object {
    $_["FileLeafRef"] -eq $oldFileName
}
# -SiteRelativeUrl にはサイト内の相対パスを指定する必要がある。
# 変換前:sites/{site-name}/SitePages/pageName.aspx
# 変換後:SitePages/pageName.aspx
$relativeUrl = $item["FileRef"] -replace "sites/[^/]+/", ""

# ファイル名を変更する
Rename-PnPFile `
        -SiteRelativeUrl $relativeUrl `
        -TargetFileName $newFileName `
        -Force

 

フォルダを取得する

Get-PnPFolderコマンドレットを利用する。(公式リファレンス)

フォルダが存在しない場合、例外が発生する。

$RootFolderName = "SitePages"
$targetFolderName = "subFolderName"
$folder = $null
try {
    $folder = Get-PnPFolder -Url ($RootFolderName + "/" + $title)
} catch [System.Management.Automation.PSInvalidOperationException]{
    Write-Output "Sharepointへの接続に失敗しました"
} catch {
    Write-Output "予期せぬエラーが発生しました"
} finally {

}

フォルダを作成する

Add-PnPFolderコマンドレットを利用する。(公式リファレンス)

Add-PnPFolder -Name $title -Folder $RootFolderName

フォルダが存在しない場合、作成する

Resolve-PnPFolderコマンドレットを利用する。(公式リファレンス)

$RootFolderName = "SitePages"
$targetFolderName = "subFolderName"
Resolve-PnPFolder -SiteRelativePath ($RootFolderName + "/" + $title)