SharePoint リストの列を取得すると、Title や Created などの標準列に加え、利用者が追加したカスタム列、さらにシステム用の非表示列まで混在します。この記事では CSOM(PowerShell)で list.Fields を列挙し、標準列を除外して「カスタム列だけ」を抽出する現実的な判定方法をまとめます。
SharePoint の「列(Field)」が混在する理由
SharePoint のリスト/ライブラリは、見た目以上に多くの列を内部に持っています。列は大きく分けると、次のような由来を持ちます。
- 標準(組み込み)列:Title、Created、Modified、Author、Editor、ID、Attachments など、SharePoint が最初から提供する列
- テンプレート/機能(Feature)由来の列:ドキュメント ライブラリ特有の列や、特定の機能を有効化したときに追加される列
- サイト列(Site Column):サイトレベルで作られ、複数のリストで再利用できる列
- リスト列(List Column):特定のリストにだけ存在する列(列の作成場所がリスト)
- システム列/内部列:画面には表示されないが、検索、権限、バージョン管理、アイテムの状態管理などを支える列
問題は、「これは標準列」「これはカスタム列」と明示したフラグが CSOM の Field に用意されていない点です。結果として、Field のプロパティの傾向から「カスタムっぽい列」を抽出する、という現実解になります。
まず押さえる:表示名と内部名が違う(そして内部名が重要)
列には、ユーザーが画面で見る表示名(Title)と、API やコードで扱う内部名(InternalName)があります。内部名は作成時に確定し、表示名を変更しても内部名は基本的に変わりません。移行・連携・スクリプト化を考えるなら、表示名だけでなく内部名も必ず一緒に扱うのが安全です。
| 例 | 表示名(Title) | 内部名(InternalName) | よくある落とし穴 |
|---|---|---|---|
| 空白を含む列 | 顧客 名 | 顧客_x0020_名 | 空白がエンコードされ、見た目と一致しない |
| 作成後に名前変更 | 案件名(現表示) | ProjectName(作成時) | 画面上の名称は変わるが、内部名は変わらない |
| 同名列を作り直した | 担当者 | 担当者0 / 担当者1… | 重複回避のため内部名が自動採番されることがある |
結論:カスタム列は「プロパティの特徴」でフィルタするのが実務的
SharePoint では Field に「標準/カスタム」の明確なラベルがありません。そこで、現場でよく採られるのが次の 3 条件です。
- Hidden = false(非表示ではない)
- ReadOnlyField = false(読み取り専用ではない)
- CanBeDeleted = true(削除可能)
これらを満たす列を「カスタム列候補」とみなすと、標準列や内部列の大半を除外できます。特に CanBeDeleted は強力で、組み込み列は削除不可のものが多いため、候補がかなり絞れます。
| プロパティ | 意味 | カスタム列抽出に効く理由 | 例外になりやすいケース |
|---|---|---|---|
| Hidden | UI から非表示の列か | 内部列・補助列の多くは Hidden = true | 運用で「非表示のカスタム列」を作ると取りこぼす |
| ReadOnlyField | 読み取り専用の列か | システム列は ReadOnlyField = true が多い | ワークフローや自動更新前提でカスタム列を ReadOnly にすると取りこぼす |
| CanBeDeleted | 削除可能な列か | 組み込み列は削除不可が多く、強いフィルタになる | 機能追加列や保護された列は「カスタムでも削除不可」の場合がある |
PowerShell + CSOM で「カスタム列候補」を抽出するサンプル
ここでは list.Fields を読み込み、上記 3 条件でフィルタして、表示名と内部名を一覧出力します。認証部分は環境によって異なるため、SharePoint Online で扱いやすい「PnP PowerShell で接続し、CSOM コンテキストを取り出す」形にしています(列の列挙ロジック自体は CSOM です)。
準備:PnP PowerShell のインストール
Install-Module PnP.PowerShell -Scope CurrentUser
抽出スクリプト(カスタム列候補)
$siteUrl = "https://tenant.sharepoint.com/sites/YourSite"
$listTitle = "YourList"
# SharePoint Online / MFA 環境でも扱いやすい接続
Connect-PnPOnline -Url $siteUrl -Interactive
# CSOM コンテキストを取得(以降は CSOM で Fields を扱う)
$ctx = Get-PnPContext
$web = $ctx.Web
$list = $web.Lists.GetByTitle($listTitle)
$fields = $list.Fields
# 必要なプロパティだけを Include して通信量を抑える
$ctx.Load($fields, "Include(Title,InternalName,Id,TypeAsString,Hidden,ReadOnlyField,CanBeDeleted,Sealed,FromBaseType,Group,Required)")
$ctx.ExecuteQuery()
# 基本フィルタ:非表示ではない/読み取り専用ではない/削除可能
$customCandidates = $fields | Where-Object {
($*.Hidden -eq $false) -and
($*.ReadOnlyField -eq $false) -and
($_.CanBeDeleted -eq $true)
}
# 画面で見て確認しやすいように並べ替えて表示
$customCandidates |
Sort-Object Title |
Select-Object Title, InternalName, TypeAsString, Required, Group, Id |
Format-Table -AutoSize
# CSV に吐き出しておくと後工程が楽
$customCandidates |
Sort-Object Title |
Select-Object Title, InternalName, TypeAsString, Required, Group, Id |
Export-Csv -Path ".\custom-fields.csv" -NoTypeInformation -Encoding UTF8
診断用:なぜ除外されたのかを見える化する
「思ったより列が取れない」「標準列が混ざる」と感じたら、まずはフィルタ前の全列を診断して、どのフラグが効いているかを確認するのが近道です。
$fields |
Select-Object Title, InternalName, TypeAsString, Group,
Hidden, ReadOnlyField, CanBeDeleted, Sealed, FromBaseType,
@{Name="IsCustomCandidate";Expression={ (-not $_.Hidden) -and (-not $_.ReadOnlyField) -and ($_.CanBeDeleted) }} |
Sort-Object IsCustomCandidate, Title |
Format-Table -AutoSize
この「診断」を 1 回挟むだけで、環境固有の列(テンプレート列、ソリューション列、運用で非表示化された列など)が見えて、フィルタ調整がスムーズになります。
判定精度を上げる追加条件(目的別に使い分ける)
3 条件だけでも実務では十分役立ちますが、「この列は本当に標準列ではないのか?」をもう少し確度高くしたい場合、追加で見られる代表的なプロパティがあります。全部を一律に厳しくすると取りこぼすため、目的に応じて段階的に足すのがおすすめです。
| 追加条件 | 見方 | 効きどころ | 注意点 |
|---|---|---|---|
| Sealed = false | 保護された列(システムが封印)かどうか | 標準列・機能列は Sealed = true が混ざりやすく、除外の精度が上がる | 「どうしても消してほしくない」列を運用で保護している場合は、ここだけで判断しない |
| FromBaseType = false | ベース定義(基盤)由来かどうか | 組み込み列の除外に効きやすい | テンプレートやソリューション由来の列を「標準扱い」にしたい場合は要調整 |
| Group の傾向 | 列グループ(UI の分類) | カスタム列を「カスタム列」などのグループに揃えていると判定が安定する | 過去の運用でグループがバラバラだと当てにならない |
| TypeAsString で除外 | Computed / ContentTypeId など | 内部計算列や管理用の列をさらに減らせる | 計算列(Calculated)を「カスタム扱い」にしたい場合は除外しない |
強化フィルタの例(より「標準列っぽいもの」を落とす)
$customCandidatesStrict = $fields | Where-Object {
($_.Hidden -eq $false) -and
($_.ReadOnlyField -eq $false) -and
($_.CanBeDeleted -eq $true) -and
($_.Sealed -eq $false) -and
($_.FromBaseType -eq $false)
}
ただし、FromBaseType / Sealed は環境や列の由来で挙動が変わることがあります。「ユーザーが追加した列だけ欲しい」のか、「システム列を除いた入力対象列が欲しい」のかで、最適な条件は変わります。
パターンで見分ける:標準列・内部列に多い「内部名」と「グループ」
プロパティ判定に加えて、列名のパターンを知っておくと「この列はシステムっぽい」を素早く見抜けます。もちろん例外はありますが、トラブルシュートに強い武器になります。
| よくあるパターン | 例(InternalName) | 傾向 | 扱いの目安 |
|---|---|---|---|
| 先頭がアンダースコア | _UIVersionString / _ModerationStatus | 内部管理用の列に多い | まずは標準/内部列として疑う |
| OData__ で始まる | OData__ColorTag | モダン UI や表示制御関連で見かけやすい | 入力項目の抽出目的なら除外候補 |
| ドキュメント ライブラリ特有 | FileRef / FileLeafRef / FSObjType | ファイルパスや種別など、ライブラリ内部の根幹情報 | ほぼ標準/内部列(消さない) |
| リンク表示用 | LinkTitle / LinkTitleNoMenu | ビュー表示に使われる計算列的なもの | 入力項目ではないので通常は除外 |
また、Group(列グループ)に「Base Columns」「Core Document Columns」「_Hidden」などが含まれる場合、標準列や内部列の可能性が高いです。逆に、組織ルールでカスタム列を「Custom Columns」「業務項目」などのグループに統一していると、Group 条件が強いフィルタになります。
「カスタム列だけ」の定義を揃えると、判定が一気に安定する
現場で混乱しやすいのが、関係者によって「カスタム列」の意味がズレることです。例えば、テンプレートが追加した列や、アプリ/ソリューションが作った列をカスタムに含めるのか、含めないのかで結果が変わります。そこで、用途別にフィルタのプリセットを作ると運用が安定します。
| 目的 | おすすめ条件 | 得られるもの | 向いている作業 |
|---|---|---|---|
| 入力対象の列を取りたい | Hidden = false AND ReadOnlyField = false | ユーザーが触れる可能性の高い列 | フォーム設計、入力項目の棚卸し |
| 「ユーザー作成列っぽい」ものを取りたい | Hidden = false AND ReadOnlyField = false AND CanBeDeleted = true | 標準列をかなり避けつつ、実用的な候補 | 移行、連携、列一覧の自動生成 |
| 削除候補(掃除対象)を厳しめに取りたい | 上記 + Sealed = false + FromBaseType = false | 標準/保護列をより避けた候補 | 不要列の整理、ガバナンス |
| 100% を狙いたい | プロパティ判定 + 除外リスト(InternalName の denylist) | 環境に最適化された確実な結果 | 本番自動処理、監査、定期ジョブ |
例外パターン:この方法でもズレることがある(対策込み)
プロパティ判定は実用的ですが、厳密な保証ではありません。よく起きる例外と、その対策を押さえておくと運用が楽になります。
カスタム列を非表示にしている
入力フォームからは消したいが保持はしたい、といった理由で Hidden = true にされるカスタム列があります。この場合、基本フィルタでは取りこぼします。
- 対策:目的が「ユーザー作成列の棚卸し」なら Hidden 条件を外す、または「非表示のカスタム列も別枠で抽出」する
自動更新前提で ReadOnly にしている
Power Automate などで値を入れる運用だと、意図的に ReadOnlyField = true にされることがあります。
- 対策:ReadOnlyField は「入力対象」を探すときに強い。棚卸しなら ReadOnly を許容し、別途「入力可否」フラグとして扱う
テンプレート列やソリューション列をどう扱うか
テンプレートやアドインが追加した列は「組み込みではないが、利用者が直接作ったわけでもない」中間的な存在です。ここをカスタムに含めるかどうかで結果がぶれます。
- 対策:列の Group や命名規則(接頭辞)を組織ルールで決め、機械判定しやすくする
- 対策:InternalName の denylist / allowlist を併用して“最終確定”する
実務で効く小技:除外リスト(denylist)を「内部名」で持つ
自動化を本番で回すなら、最後は InternalName の除外リストが最も堅いです。理由はシンプルで、標準列の内部名は基本的に不変だからです(表示名は変わる)。
例えば、最低限除外しておくことが多い内部名は次のようなものです(リスト種別で差があるため、ここでは代表例だけに留めます)。
- ID
- Title
- Created
- Modified
- Author
- Editor
- Attachments
- ContentType
- ContentTypeId
プロパティ判定で候補を絞ったうえで、最後に denylist を当てると、誤判定がぐっと減ります。
$denyInternalNames = @(
"ID","Title","Created","Modified","Author","Editor","Attachments",
"ContentType","ContentTypeId"
)
$customCandidatesStable = $customCandidates | Where-Object {
$denyInternalNames -notcontains $_.InternalName
}
CSOM 以外の方法も知っておく(ただし最終的な考え方は同じ)
列の取得は CSOM 以外にも、PnP PowerShell のコマンドだけで行う方法や、REST API で取得する方法もあります。ただし「標準/カスタムのラベルが無いので、結局はプロパティや名前で判定する」という本質は変わりません。
PnP PowerShell だけで取得する例
Connect-PnPOnline -Url $siteUrl -Interactive
Get-PnPField -List $listTitle |
Where-Object { $*.Hidden -eq $false -and $*.ReadOnlyField -eq $false -and $_.CanBeDeleted -eq $true } |
Select-Object Title, InternalName, TypeAsString, Required, Group
REST API の例(取得はできるが、判定は結局必要)
REST でもフィールド一覧は取得できますが、返ってくる列情報は多く、ここから「カスタムだけ」を切り出すには同様に Hidden や ReadOnlyField、Sealed などでのフィルタが必要です。PowerShell で完結させたいなら、CSOM または PnP に寄せたほうが実装・保守ともに楽です。
まとめ:100% の正解より、目的に合う「条件設計」が成果につながる
SharePoint の列には「標準/カスタム」の明確なラベルが無いため、Hidden / ReadOnlyField / CanBeDeleted を軸に「カスタム列候補」を抽出するのが現実的です。さらに精度が必要なら Sealed / FromBaseType を追加したり、最後は InternalName の denylistで確定させると安定します。列の取得結果は、必ず表示名と内部名をセットで出力し、後工程の事故を防ぎましょう。

コメント