こんにちは、あんこ先生です。
「SharePointリストの列を、画面をポチポチ押しながら1つずつ作るのがしんどい」「同じような構成のリストを何個も作らなきゃいけない」、そんな悩みを抱えたことはありませんか。
この記事では、前回の記事で作った$columnsと$dataという2つのPowerShell変数を使って、実際にSharePoint Online上にリストを作成し、データまで流し込むところまで解説します。
PowerApps│PowerShellでSharePointリストを一括作成する方法①がまだの方は、先にそちらで$columnsと$dataを作ってから読み進めてもらえると、そのまま手が動かせます。
環境準備でつまずきやすいポイントも、この記事にぜんぶ集約しておきました。
おしながき
PnP.PowerShellってなに?
この記事の主役である「PnP.PowerShell」について、先に簡単に触れておきますね。
PnP.PowerShellは、SharePoint OnlineをはじめとするMicrosoft 365環境を操作するための、900近いコマンドレット(コマンド)を提供するPowerShellモジュールです。
「PnP」はPatterns and Practicesの略で、Microsoftの製品チームではなく、Microsoft 365 & Power Platformコミュニティが開発・保守しているオープンソースのプロジェクトです。
そのため、Microsoft公式のサポート(SLA)は無く、コミュニティベースでのサポートになる点は覚えておいてください。
とはいえ実務では広く使われているモジュールで、SharePointの管理画面をポチポチ操作する代わりに、リストやサイト、列といった構成要素をコードで一括操作できるのが最大のメリットです。
今回のように「同じ構成のリストを何個も作る」ような場面はまさに得意分野なので、ここから実際に使い方を見ていきましょう。
前提条件
まずはここで、必要な環境と準備をまとめてチェックしてしまいましょう。
- PowerShell 7以降が必須です。Windows PowerShell 5.1では動きません
- VSCodeのターミナルではなく、素の「PowerShell 7」を管理者権限で起動してください
PowerShell 7を検索したら、アイコンを右クリックして「管理者として実行」を選んで起動してください。

初めての場合は、実行ポリシーの変更が必要です。
とりあえずこのコマンドを実行します。
#スクリプト実行を許可する Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
これは「PowerShellでスクリプト(ひとまとまりの処理)を実行してよいか」を許可しておく設定です。
この記事ではコマンドを1行ずつ直接コンソールに打ち込んでいきますが、そのコマンドを実現しているPnP.PowerShellやImportExcelといったモジュールの中身も、実はスクリプトファイルの集まりです。
既定の設定のままだと、こうしたモジュールの読み込み自体がブロックされてしまうことがあるため、あらかじめ許可しておきます。
PnP.PowerShellモジュールをインストールします。
#PnP.PowerShellをインストール Install-Module PnP.PowerShell -Scope CurrentUser -Force
「信頼されていないリポジトリからインストールしますか」と聞かれたら、Yと答えて進めてください。
パスの概念も一応おさらいしておきますね。
.\はいまいるフォルダを指し、フォルダ移動はcdコマンドで行います。
全体の流れ

この記事は、次の4ステップで進みます。
- 環境準備:PowerShell 7・PnP.PowerShellのインストールなど、前提条件を整える(前の章で完了済み)
- Pass1:リストを作成し、ルックアップ以外の基本列を作る
- Pass2:ルックアップ列だけ後づけで作る
- 完成:データを流し込み、PowerAppsからそのまま使える状態にする
環境準備でつまずくケースが実は9割を占めるので、ここまで読んで前提条件をクリアできていれば、もう山場は越えています。
ルックアップ列は参照先リストが無いと作れないため、通常の列(Pass1)とルックアップ列(Pass2)を分けるのがコツです。
もう1つ、作業を始める前に知っておいてほしいことがあります。
今回作るのは「名刺・連絡先管理」1つだけではありません。ルックアップ列の参照先として、「取引先企業マスター」「案件管理」という2つのリストも登場します。
先に全体像を見ておきましょう。
リストの関係性は下図のようになっています。
参照先の2リストは、Pass2に入る前の章で、空リストとして先に作成します。

コードを実行する前に、リストを作りたいSharePointサイトのURLを確認しておきましょう。
いちばん簡単なのは、ブラウザで対象のSharePointサイトを開き、アドレスバーのURLをそのままコピーする方法です。
URLがわからない場合は、Microsoft 365のポータル画面(office.com)からアプリ一覧の「SharePoint」を開き、対象のサイトをクリックしてから、アドレスバーのURLを確認してください。
URLはhttps://会社名.sharepoint.com/sites/サイト名のような形になっているはずです。この記事では、これを$SiteUrlという変数に入れて使います。
https://会社名-admin.sharepoint.com)の「アクティブなサイト」からも一覧とURLを確認できます。次に、Entra ID(旧Azure AD)にPnP.PowerShell専用のアプリを登録して、クライアントIDを発行します。
このアプリ登録は、PnP.PowerShellがSharePointに安全にアクセスするための「通行証」を発行するようなイメージです。
まずはPnP.PowerShellモジュールを読み込んでおきます。
#PnP.PowerShellモジュールを読み込む Import-Module PnP.PowerShell -Force
PowerShellには「使いたいコマンドが見つかったら、そのコマンドを含むモジュールを自動で読み込む」という機能があるのですが、インストール直後の新しいウィンドウなどではこの自動読み込みがうまく働かないことがあります。
そのため、PnP.PowerShellのコマンドを使う前には、念のためImport-Moduleで明示的に読み込んでおくと安心です。
#Entra IDアプリを登録してクライアントIDを取得 Register-PnPEntraIDAppForInteractiveLogin -ApplicationName "SampleListCreator" -Tenant "yourtenant.onmicrosoft.com"
-Tenantには、自分の組織の会社名.onmicrosoft.comを指定するのが基本ですが、組織で使っている独自ドメインでも接続できる場合があります。
例えば会社名(組織のドメイン)が「office7793.com」なら、次のようになります。
Register-PnPEntraIDAppForInteractiveLogin -ApplicationName "SampleListCreator" -Tenant "office7793.com"
「.onmicrosoft.com」形式で通らない場合は、組織で使っている独自ドメイン(会社のメールアドレスの@より後ろの部分)を試してみてください。
実行するとブラウザが開いて管理者としてのログインを求められるので、画面の指示に沿って許可してください。
ログイン後には「要求されているアクセス許可」という画面が表示され、「組織の代理として同意する」というチェックボックスがあります。

これはチェックを入れたまま「承諾」を押してもらってOKです。
このチェックを外すと、あなた個人としての同意にとどまり、他のメンバーがこのアプリ経由でSharePointに接続しようとしたときに、その都度また同意を求められてしまいます。
組織全体で使うことを見越して、管理者の権限でまとめて同意しておく、という意味のチェックです。
完了すると、コンソールにClientIdという項目でIDの文字列(英数字とハイフンが並んだもの)が表示されます。
ClientIdは、あとでConnect-PnPOnlineを実行するときに必要になります。表示されたら、メモ帳などにコピーして残しておいてください。閉じてしまうと再確認が面倒なので、忘れずにメモしておきましょう。発行されたクライアントIDと、さきほど確認したサイトのURLを使って、対話形式でSharePointサイトに接続します。
次の3行は、1行ずつ貼り付けてEnterを押しながら実行してください。
$SiteUrl = "https://会社名.sharepoint.com/sites/サイト名"
$ClientId = "さきほどメモしたClientId"
#SharePointサイトに接続 Connect-PnPOnline -Url $SiteUrl -Interactive -ClientId $ClientId
$SiteUrlと$ClientIdは、それぞれ自分の環境の値に書き換えてください。
実行するとまたブラウザが開くので、対象サイトにアクセスできるアカウントでログインすれば接続完了です。
$columnsと$dataを準備する
ここから先は、前回の記事で作った$columnsと$dataという2つの変数を使っていきます。
ただし、この2つはPowerShellを開いている間だけ有効な一時的な変数です。
前回の記事を実行したPowerShellをそのまま開きっぱなしにしている場合は、この章は読み飛ばして構いません。
一方、PowerShellを閉じてしまった場合や、日をまたいで続きから始める場合は、$columnsと$dataは消えてしまっているので、次のコマンドで作り直してください。
使用するExcelファイルはこちらからダウンロードできます。
デスクトップに保存してみてください。
次の3行も、1行ずつ貼り付けてEnterを押しながら実行してください。
#デスクトップに移動する
cd ([Environment]::GetFolderPath('Desktop'))#列設計シートを$columns変数へ読み込む $columns = Import-Excel -Path "BusinessCards_サンプル.xlsx" -WorksheetName "列設計"
#データシートを$data変数へ読み込む $data = Import-Excel -Path "BusinessCards_サンプル.xlsx" -WorksheetName "データ"
これは前回の記事の手順3とまったく同じ内容です。$columns | Format-Tableを実行して、意図した中身が入っているか確認してから先に進んでもらえればと思います。
Connect-PnPOnlineで接続したのと同じPowerShellの画面のまま、続けて$columns・$dataを読み込んでも問題ありません。接続とデータ読み込みの順番はどちらが先でも大丈夫です。Pass1:基本列を作る
ここからが本題です。
まずは空っぽのリストを1つ作ります。次のコードは1行なので、そのまま貼り付けてEnterを押してください。
#リストを新規作成 New-PnPList -Title "名刺・連絡先管理" -Template GenericList
次に、そこへ列を追加していきます。
次のコードはforeach ( ) { }という1つのまとまりなので、途中で区切らず、これ全体をまとめて貼り付けてください。
#$columnsをループしてルックアップ以外の列を作成
foreach ($col in $columns | Where-Object { $_.データ型 -ne "Lookup" -and $_.データ型 -ne "LookupMulti" }) {
#UserMulti(複数ユーザー選択)は、そのままだとエラーになるのでUserに置き換える
$fieldType = if ($col.データ型 -eq "UserMulti") { "User" } else { $col.データ型 }
#列作成に必要なパラメーターを、いったん1つの箱にまとめる
$params = @{
List = "名刺・連絡先管理"
DisplayName = $col.表示名
InternalName = $col.内部名
Type = $fieldType
AddToDefaultView = $true
}
#Choice・MultiChoice型のときだけ、選択肢も箱に追加する
if ($fieldType -eq "Choice" -or $fieldType -eq "MultiChoice") {
$params.Choices = ($col.'選択肢(カンマ区切り)' -split ",").Trim()
}
Add-PnPField @params
#元の型がUserMultiだった列だけ、あとから複数選択できるように変更する
if ($col.データ型 -eq "UserMulti") {
Set-PnPField -List "名刺・連絡先管理" -Identity $col.内部名 -Values @{AllowMultipleValues=$true}
}
}1つずつ、何をしているか見ていきましょう。
New-PnPListは、新しいSharePointリストを1つ作るコマンドです。-Titleでリスト名、-Template GenericListで「特別な機能のない、標準的なリスト」を作ることを指定しています。
次のforeachは「$columnsの中身を1行ずつ順番に取り出して、同じ処理を繰り返す」という意味の書き方です。
Where-Object { }の部分は、ルックアップ型(Lookup・LookupMulti)の行だけを一旦除外するためのフィルターです。$_は「いま処理している1行分のデータ」を指します。
ループの中身にある$fieldTypeの行は、前回からある部分です。SharePointの列の型(-Typeパラメーター)には、実は「複数のユーザーを選択できるUser型」を表す値が用意されていません。Text・Choice・Userなどは指定できても、UserMultiは使えない、ということです。
そこでいったんUser型として列を作ってから、Set-PnPFieldで「複数選択を許可する(AllowMultipleValues)」という設定だけを後から追加しています。
次の$paramsが、今回あらためて追加した部分です。
$params = @{ ... }は、Add-PnPFieldに渡すパラメーターを、名前と値のペアでいったん箱(ハッシュテーブル)にまとめておく書き方です。
「分類」(Choice型)や「タグ」(MultiChoice型)の列は、選択肢を渡してあげないと、選べる項目が1つも無い空っぽの列になってしまいます。
そこで$fieldTypeがChoiceかMultiChoiceのときだけ、列設計シートの「選択肢(カンマ区切り)」の値をカンマで分割し、$params.Choicesとして箱に追加しています。
最後のAdd-PnPField @paramsは、@paramsという書き方で、箱の中身をまとめてAdd-PnPFieldのパラメーターとして渡しています(この書き方を「スプラッティング」と呼びます)。
つまり、選択肢が必要な列のときだけ箱の中にChoicesを追加してから渡すことで、1つのループの中でChoice型・MultiChoice型・それ以外の列を、まとめて正しく作れるようにしています。
Add-PnPFieldには用意されていません。色を付けたい場合は、作成後にリストの列設定画面から手動で設定してください。| パラメーター | 意味 |
|---|---|
-List | どのリストに列を追加するか |
-DisplayName | SharePoint上に表示される列名(表示名) |
-InternalName | システム内部で使われる列名(内部名) |
-Type | Text・Choice・Numberなどのデータ型 |
-AddToDefaultView | 既定のビュー(一覧画面)にこの列を表示するかどうか |
-Choices | Choice・MultiChoice型のときだけ指定する選択肢の一覧 |
つまりこのループは、「列設計シートに書いた表示名・内部名・データ型・選択肢を、1行ずつSharePointの列として作っていく」という処理です。
選択肢列についてはPowerApps│SharePointリストの選択肢列に書き込む方法でも、PowerApps側からの書き込み方をあわせて解説しています。
またOwnerやSupportMembersのようなUser型の列は、PowerApps側での書き込み方にいくつかクセがあるので、PowerApps│様々なアプローチでユーザー列に書き込む方法もあわせて確認しておくと安心です。
Field already existsというエラーが出ます。これは「もう存在しているので作れません」という意味のエラーで、実行自体は止まらずに次の列の処理へ進むので、慌てなくて大丈夫です。最初からやり直したいときは、Remove-PnPList -Identity "名刺・連絡先管理" -Forceでリストごと削除してから、この章の手順を最初から実行し直してください。特に「分類」「タグ」を選択肢なしのバージョンで一度作ってしまった場合は、あとから選択肢だけ追加することができないため、リストごと作り直すのが確実です。Pass2:ルックアップ列を作る
ルックアップ列は、Pass1のやり方だと作れません。
現行のPnP.PowerShellでは、Add-PnPFieldから-LookupListパラメーターが削除されているためです。
そのため、ルックアップ列だけはAdd-PnPFieldFromXmlを使い、CAML(SharePointの列定義に使うXML形式)で定義する方式を採用します。
ルックアップ列を使った親子関係のリストをPowerApps側でどう表示するかは、PowerApps│親子関係のSharePointリストを思い通りにギャラリー表示する方法で解説しているので、作成後はそちらも参考にしてもらえればと思います。
ここでもう1つ、忘れがちな準備があります。
ルックアップ列は「参照先のリストがすでに存在している」ことが前提です。この記事のサンプルでは、「取引先企業マスター」と「案件管理」という2つのリストを参照先にしているので、これらが無ければ先に作っておく必要があります。
すでにお持ちの実際のリストを参照先にする場合はこの手順は不要です。まだ無い場合は、動作確認用として次のコマンドで空のリストを作っておいてください。
#ルックアップ先のリストを準備(無ければ作成) New-PnPList -Title "取引先企業マスター" -Template GenericList
New-PnPList -Title "案件管理" -Template GenericList
ShowField='Title'としているので、参照先リストの既定のTitle列(1行目の項目名にあたる列)がそのままルックアップの表示対象になります。テスト用に何件かダミーの項目を追加しておくと、あとでPowerAppsから見たときに選択肢が表示されて分かりやすくなります。参照先のリストが用意できたら、ここからが本題です。
まず、ルックアップ型の行だけを取り出しておきます。
#$columnsのうちルックアップ型だけを抽出
$lookupColumns = $columns | Where-Object { $_.データ型 -eq "Lookup" -or $_.データ型 -eq "LookupMulti" }続けて、次のforeach ( ) { }のまとまりを、途中で区切らずそのまま貼り付けてください。
foreach ($col in $lookupColumns) {
$lookupList = Get-PnPList -Identity $col.ルックアップ先リスト
$isMulti = if ($col.データ型 -eq "LookupMulti") { "TRUE" } else { "FALSE" }
$schemaXml = "<Field Type='Lookup' DisplayName='$($col.表示名)' Name='$($col.内部名)' Mult='$isMulti' ShowField='Title' List='$($lookupList.Id)' />"
Add-PnPFieldFromXml -List "名刺・連絡先管理" -FieldXml $schemaXml
}こちらも1つずつ見ていきましょう。
最初の1行は、Pass1と逆で「ルックアップ型の行だけ」を$lookupColumnsという変数に集めています。
Get-PnPList -Identity $col.ルックアップ先リストは、列設計シートに書いた「参照先リスト名」から、実際のリストの情報を取得するコマンドです。参照先のリスト(例:「取引先企業マスター」)がまだ存在しないと、ここでエラーになるので注意してください。
$isMultiの行は、「データ型がLookupMulti(複数選択)ならTRUE、そうでなければFALSE」という値を作っています。
そのあとの$schemaXmlが今回の主役です。SharePointの列は、実は裏側でCAML(キャメル)と呼ばれるXML形式で定義されていて、Add-PnPFieldコマンドの多くはこのCAMLを人間にわかりやすく組み立て直したものにすぎません。
ルックアップ列については、この組み立て直しの部分がまだ用意されていないため、CAMLを直接書く必要がある、というわけです。
| CAMLの属性 | 意味 |
|---|---|
Type='Lookup' | ルックアップ型の列であることを示す |
DisplayName | 表示名 |
Name | 内部名 |
Mult | 複数選択を許可するか(TRUE/FALSE) |
ShowField | 参照先リストのどの列を表示するか(ここでは既定のTitle列) |
List | 参照先リストのID(さきほどGet-PnPListで取得したもの) |
最後のAdd-PnPFieldFromXmlで、このCAMLの内容どおりに列を作成しています。

Add-PnPFieldに-LookupListパラメーターがありましたが、いまは廃止されています。ルックアップ列は必ずAdd-PnPFieldFromXmlで作りましょう。データを流し込む
最後に、$dataをループしてAdd-PnPListItemでデータを投入します。
その前に、1つだけ準備しておきたいことがあります。
Add-PnPListItemの-Valuesは、表示名ではなく内部名をキーにする必要があります。
ところがデータシートの見出しは「氏名」「会社名」といった表示名なので、そのままでは内部名と一致せず、うまく登録できません。
そこで、列設計シートを読み込んだ$columnsを使って、「表示名→内部名」の対応表を先に作っておきます。
#表示名→内部名の対応表を作る
$nameMap = @{}
foreach ($col in $columns) {
$nameMap[$col.表示名] = $col.内部名
}これで、「氏名」と入力すれば「Title」が、「会社名」と入力すれば「CompanyName」が返ってくる、変換用の対応表ができました。
この対応表を使って、データを投入します。これもforeach ( ) { }のひとかたまりなので、そのまま全体を貼り付けてください。
#$dataをループしてリストにデータを投入
foreach ($row in $data) {
$values = @{}
foreach ($prop in $row.PSObject.Properties) {
$internalName = $nameMap[$prop.Name]
$values[$internalName] = $prop.Value
}
Add-PnPListItem -List "名刺・連絡先管理" -Values $values
}ここも順番に見ていきましょう。
外側のforeachは「データシートの行(1人分の名刺情報)を、1行ずつ順番に処理する」という意味です。
$values = @{}は、空の「連想配列(ハッシュテーブル)」を用意する書き方です。「列名(キー)と値(バリュー)のペア」を入れる箱、とイメージしてもらえればOKです。
内側のforeach ($prop in $row.PSObject.Properties)は、1行分のデータが持っている表示名を1つずつ取り出します。
そのままではキーが表示名のままなので、さきほど作った$nameMapを使って$internalName(内部名)に変換してから、$valuesという箱に「内部名=値」の形で詰め込んでいます。
こうして1行分の情報を$valuesに詰め終えたら、Add-PnPListItem -List "名刺・連絡先管理" -Values $valuesで、その内容をそのままSharePointリストの1件として登録します。
つまりこの処理は、「データシートの1行分を、内部名に変換しながらSharePointリストの1件として貼り付ける」という作業を、行の数だけ自動で繰り返しているイメージです。
例によってコピペで試せる形にしてありますが、データシートの見出し(表示名)と列設計シートの表示名が1文字でも違っていると、$nameMapで変換できず値が抜け落ちてしまうので、投入前に一度Format-Tableで見出しを見比べておくと安心です。
Field ○○ not present in list.というエラーは、-Valuesのキーに表示名がそのまま渡ってしまい、内部名と一致しないときに出ます。この記事のとおり$nameMapで変換していれば起きませんが、もし出た場合は、データシートの見出しと列設計シートの表示名が一致しているか見直してみてください。「’○○’は、コマンドレット…として認識されません」と出るときは?
Register-PnPEntraIDAppForInteractiveLoginやConnect-PnPOnlineを実行したときに、次のようなエラーが出ることがあります。
Register-PnPEntraIDAppForInteractiveLogin : 用語 'Register-PnPEntraIDAppForInteractiveLogin' は、コマンドレット、関数、スクリプト ファイル、または操作可能なプログラムの名前として認識されません。
これは、PnP.PowerShellのインストールはできているものの、そのコマンドがまだこのPowerShellの画面に読み込まれていない状態です。
この記事の手順どおり、コマンドの前にImport-Module PnP.PowerShell -Forceを実行してから、もう一度試してみてください。
PowerShellの画面を開き直した直後も同じ状態になるので、その都度Import-Moduleを実行し直す必要があります。
「リスト ‘○○’ はサイトには存在しません」と出るときは?
Pass2を実行したときに、次のようなエラーが出ることがあります。
Get-PnPList: リスト '取引先企業マスター' は URL '...' のサイトには存在しません。
これは、列設計シートの「ルックアップ先リスト」に書いた名前のリストが、まだSharePoint上に存在していないときに出るエラーです。
この記事のPass2の直前で案内している通り、New-PnPListで参照先のリストを先に作成するか、実際に使う既存のリスト名に列設計シートの「ルックアップ先リスト」を書き換えてから、もう一度実行してみてください。
あわせて表示されるAdd-PnPFieldFromXml : Value does not fall within the expected range.というエラーも、参照先リストが見つからず$lookupListが空になったまま次に進んでしまったために起きる、いわば「連鎖的な」エラーです。参照先リストの問題を解決すれば、あわせて解消します。
文字化けが心配なときは?
Excelファイルを保存するときの文字コードも、地味に落とし穴になりがちなポイントです。
BOM付きUTF-8で保存していないと、Windows PowerShell 5.1環境やダブルバイト文字を扱う環境では文字化けすることがあります。
ただしPowerShell 7であれば、通常はこの問題は起きないので、そこまで神経質にならなくて大丈夫です。
Add-PnPFieldでルックアップ列が「パラメーターが見つかりません」と出るときは?
これはこの記事のPass2でまさに解説した内容そのものです。
Add-PnPField -LookupList ...のように書くと、現行のPnP.PowerShellでは-LookupListパラメーターが存在しないため、このエラーになります。
対処法はシンプルで、ルックアップ列だけAdd-PnPFieldFromXmlとCAMLの組み合わせに切り替えることです。
まとめ
今日の内容を3点にまとめますね。
- PowerShell 7以降・管理者権限での起動・実行ポリシーの変更・PnP.PowerShellのインストールを、事前にまとめて準備しておく
- 通常の列はPass1で
$columnsをループしてAdd-PnPField、ルックアップ列だけはPass2でAdd-PnPFieldFromXmlとCAMLで作る - 最後に
$dataをループしてAdd-PnPListItemで流し込めば、一括作成の完成
次の一手としては、作成したリストにPower Appsからフォームを接続して、実際の業務アプリとして使える形に仕上げていく、という展開がおすすめです。
PowerApps│PowerShellでSharePointリストを一括作成する方法①で、そもそもの$columnsと$dataの作り方をおさらいするのもよいと思います。
それでは、また次の記事でお会いしましょう。

















