| name | power-pages |
| description | Power Pages Code Site (SPA) の開発・ビルド・デプロイ。pac pages upload-code-site でサイト作成からデプロイまで完結する。 |
| category | ui |
| triggers | ["Power Pages","pac pages","upload-code-site","コードサイト","code site","SPA","ポータル","外部サイト","Power Pages デプロイ","site settings","テーブル権限","table permissions","Web ロール","Enhanced Data Model","powerpagecomponent","403 Forbidden","404 Resource not found","9004010C"] |
Power Pages Code Site (SPA) 開発・デプロイスキル
公式リファレンス: Power Pages でシングルページ アプリケーションを作成して展開する | Microsoft Learn
新スキル参照: microsoft/power-platform-skills - power-pages
サブリファレンス(必要に応じて参照)
Dataverse 接続と認証の実装方法はこのファイルで概要を説明し、完全なサンプルコードは上記 References にまとめている。
刷新版の構成原則(upstream 優先)
このスキルは microsoft/power-platform-skills/plugins/power-pages の以下 4 スキルを優先参照して構成する。
| 領域 | upstream スキル | このスキル内の着地 |
|---|
| 認証・認可 | setup-auth | references/authentication.md |
| Web ロール | create-webroles | references/enhanced-data-model-permissions.md |
| Dataverse CRUD | integrate-webapi | references/dataverse-client.md |
| 権限監査 | audit-permissions | reviews/* + scripts/review_pre_deploy.py |
標準実行順序(刷新後)
- デプロイ基盤準備(
.powerpages-site 作成)
- Web ロール整備
- 認証導線(SSO/ログイン/ログアウト)整備
- Dataverse Web API CRUD 実装
- 権限監査(ロール・テーブル権限整合)
⚠️ デプロイ後の必須ステップ: pac pages upload-code-site でテーブル権限 YAML をデプロイしても、
type=18 の content JSON 内 adx_entitypermission_webrole が空のまま残り、Web ロール紐付けが効かない。デプロイ直後に
python scripts/setup_permissions.py を実行して content の adx_entitypermission_webrole を書き込み、review_pre_deploy.py の
チェック 3.7 が ✅ になることを確認する(さもないと管理者を含む全ユーザーが 403)。詳細は教訓 14。
詳細な責務分離と判断基準は upstream 優先構成ガイド を正本として扱う。
microsoft/power-platform-skills 比較(認証・認可 vs Dataverse CRUD)
| 観点 | ユーザー認証・Webロール認可 | Dataverse Web API 連携 CRUD |
|---|
| 上流スキル | setup-auth + create-webroles | integrate-webapi |
| 主目的 | ログイン/ログアウト、認証状態判定、ロールベース UI 制御 | /_api 経由の読み書き(powerPagesFetch/powerPagesFetchResponse、OData ヘルパー) |
| 主な成果物 | authService.ts(AUTH_PROVIDERS 配列)・use-auth.ts・ログイン UI・Web ロール YAML(.powerpages-site/web-roles/) | powerPagesApi.ts、テーブル別 service/hooks、CRUD 画面 |
| サーバー側必須設定 | IdP site settings、Web ロール、テーブル権限へのロール紐付け | テーブル権限(type=18 + adx_entitypermission_webrole)、必要時のみ Webapi 設定 |
| 失敗時の代表症状 | ログインループ、/profile 強制遷移、未認証判定ミス | 401(90040107) / 403 / 404(9004010C, 9004010D) |
| 依存関係 | 先に認証導線を整える(ユーザー実体: contact) | 認証済みセッション Cookie 前提で CRUD を実行 |
推奨適用順:
setup-auth で認証導線を整備
create-webroles でロールを確定
integrate-webapi で CRUD 実装
audit-permissions で権限妥当性を最終監査
核心原則
pac pages upload-code-site がサイト作成とデプロイの両方を行う — API でサイトを事前作成する必要はない
- 初回は Inactive Sites に作成される → PP API (
2022-03-01-preview) で activate_site.py を使ってアクティブ化
- デプロイは upload-code-site → relink → restart の3ステップ —
upload-code-site は既存テーブル権限の Web ロール紐付けを消すため、毎回 scripts/relink_table_permissions.py(または統合の scripts/deploy_site.py)で再付与する(教訓 15)。npm run deploy(build && upload-code-site だけ)で終わらせない
- Post-Upload Fix は不要 —
upload-code-site が header/footer/page を正しく構成する
.powerpages-site/ は upload-code-site が自動管理する — ただし site-settings/ YAML は手動追加して永続化できる(下記参照)
adx_website レコードは絶対に削除しない — EDM 2.0 でもランタイムが起動時に参照する
- 環境のクリーンアップ時は PP API のサイト一覧と照合してから削除する — 誤削除で 500 エラー
credentials: "same-origin" を使う — "include" ではない(same-site Cookie 認証)
- powerpagecomponent type=18 には
powerpagesitelanguageid が必須 — 未設定だと 404 になる
- 報告者・作成者は Contact Lookup で追跡する — Power Pages では
createdby はアプリケーションユーザーになるため使えない。ログインユーザーの Contact 情報を自動取得し入力不要にする(教訓 19)
- デザイン系の変更は必ず
npm run dev(localhost)で見た目を確認してから本番デプロイする — 本番デプロイ → ブラウザ確認 → 再デプロイのループは 1 サイクルあたり数十秒〜数分かかり非効率。レイアウト・配色・レスポンシブ崩れは localhost で先に潰し、本番デプロイは最終確認のみに留める(教訓 20)。ただし Power Pages 本体のテーマ CSS(Bootstrap 既定の見出し色など)はローカルには存在せず本番でのみ衝突しうるため、色指定は見出し要素に明示的な color を必ず設定し、デプロイ後の最終確認も省略しない(詳細は トラブルシューティング)
ワークフロー
初回:
npm run build
→ pac pages upload-code-site ← Inactive Sites に作成
→ py portal/scripts/activate_site.py ← PP API でアクティブ化 (api-version=2022-03-01-preview)
→ py portal/scripts/setup_contact_webapi.py
→ py .github/skills/power-pages/scripts/setup_inquiry_reporter.py ← 報告者 Contact Lookup (教訓 19)
→ py .github/skills/power-pages/scripts/relink_table_permissions.py ← ★ロール再付与+再起動
2回目以降(推奨: 統合スクリプトで一括実行):
py .github/skills/power-pages/scripts/deploy_site.py
(ビルド → upload-code-site → ★relink → 検証 → 再起動 を 1 コマンドで実行)
2回目以降(手動の場合):
npm run build → pac pages upload-code-site
→ py .github/skills/power-pages/scripts/relink_table_permissions.py ← ★必須(省くと 403)
⚠️ npm run build && pac pages upload-code-site だけで終わらせると、既存テーブル権限の
Web ロール紐付けが消えて全件 403 になる(教訓 15)。relink_table_permissions.py を
毎回実行するか、deploy_site.py で一括実行すること。両スクリプトともハードコードなし・
すべて .env 管理(DATAVERSE_URL / ENV_ID / PAGES_WEBSITE_ID / PP_SUBDOMAIN /
RELINK_WEBROLE_NAMES / PORTAL_DIR)。
アクティベーション詳細
参照: microsoft/power-platform-skills activate-site
初回の pac pages upload-code-site はサイトを Inactive Sites に作成する。
アクティブ化は Power Platform API (api-version=2022-03-01-preview) で行う。
POST https://api.powerplatform.com/powerpages/environments/{ENV_ID}/websites?api-version=2022-03-01-preview
Body:
{
"name": "<PAGES_SITE_NAME>",
"subdomain": "<PAGES_SUBDOMAIN>",
"templateName": "DefaultPortalTemplate",
"dataverseOrganizationId": "<org_id>",
"selectedBaseLanguage": 1033,
"websiteRecordId": "<powerpagesiteid>" ← pac pages upload-code-site が作った ID
}
Response: 202 Accepted + Operation-Location ヘッダー
→ Operation-Location を 10 秒間隔でポーリング
→ OperationComplete = 成功、OperationFailed = 失敗
| パラメータ | 取得方法 |
|---|
ENV_ID | .env |
PAGES_SITE_NAME | .env / powerpages.config.json の siteName |
PAGES_SUBDOMAIN | .env / ユーザー指定 |
dataverseOrganizationId | GET /api/data/v9.2/organizations?$select=organizationid |
websiteRecordId | GET /api/data/v9.2/powerpagesites から name で検索 |
⚠️ API バージョン注意: 2022-03-01-preview を使用すること。2024-10-01 ではアクティベーションが正しく動作しない。
前提条件
| ツール | バージョン | 用途 |
|---|
pac (Power Platform CLI) | 1.44+ | サイト作成・アップロード |
node + npm | 18+ | SPA ビルド |
| Python 3 | 3.10+ | デプロイスクリプト(任意) |
pac CLI 注意: サブコマンドは pac pages (例: pac pages list, pac pages upload-code-site)。
pac power-pages は無効。pac pages help でコマンド一覧を確認できる。
.env パラメータ
DATAVERSE_URL=https://{org}.crm.dynamics.com/
ENV_ID= # Power Platform 環境 ID
PAGES_SITE_NAME= # サイト名 (powerpages.config.json の siteName と一致)
PAGES_SUBDOMAIN= # サブドメイン (例: myportal → myportal.powerappsportals.com)
プロジェクト構造(公式準拠 / upstream 推奨)
portal/
├── src/
│ ├── App.tsx ← ルート (HashRouter + Routes)
│ ├── main.tsx ← エントリポイント
│ ├── index.css ← Tailwind CSS
│ ├── components/
│ │ ├── site-layout.tsx ← ヘッダー・ナビ・プロフィールドロップダウン
│ │ ├── require-auth.tsx ← 認証ガードコンポーネント
│ │ ├── mode-toggle.tsx ← ダーク/ライト切替
│ │ └── ui/ ← shadcn/ui コンポーネント
│ ├── hooks/
│ │ └── use-auth.ts ← SSO 認証フック
│ ├── shared/
│ │ ├── powerPagesApi.ts ← ★ Web API 共有クライアント (powerPagesFetch/buildODataUrl 等)
│ │ └── services/
│ │ └── <table>Service.ts ← テーブルごとの CRUD サービス
│ ├── types/
│ │ └── <table>.ts ← エンティティ型・ドメイン型・マッパー
│ ├── lib/
│ │ └── utils.ts ← cn() ユーティリティ
│ ├── config.ts ← サイト名・ロゴ等のブランディング設定(.env の VITE_* を集約)
│ └── pages/
│ ├── home.tsx ← ランディングページ
│ └── profile.tsx ← プロフィール編集 (★ powerPagesApi.ts を使用)
├── dist-site/ ← ビルド出力 (compiledPath)
├── .powerpages-site/ ← upload-code-site が管理 + site-settings YAML 手動追加可
│ └── site-settings/ ← Webapi/* 設定を YAML で永続化
├── .env.example ← ★ ブランディング等の VITE_* 変数サンプル(コピーして .env を作成)
├── powerpages.config.json ← CLI 設定ファイル (必須)
├── package.json
├── vite.config.ts
└── scripts/
├── deploy.py ← デプロイスクリプト (Build→Upload→Restart)
├── activate_site.py ← PP API サイトアクティベーション
├── setup_auth.py ← Entra ID SSO 認証設定 (Site Settings + Liquid 注入)
└── setup_contact_webapi.py ← Contact Web API 有効化 (EDM 2.0 対応)
サイト名・ロゴのブランディング設定(.env / src/config.ts)
デプロイごとに変わるブランディング値はコードに直書きせず、ビルド時の環境変数で差し替える。
テンプレートの .env.example を .env にコピーして値を編集する(.env は .gitignore 済み)。
cp .env.example .env
| 変数 | 用途 | 既定値 |
|---|
VITE_SITE_NAME | サイト/ブランド表示名(ヘッダーロゴ・フッター・ブラウザタブのタイトル) | Power Pages |
VITE_SITE_LOGO_MARK | ヘッダーロゴのマーク(1〜2 文字の頭文字) | P |
- すべて
VITE_ プレフィックス必須(Vite はこの接頭辞の変数のみクライアントへ公開)。
- 値は
src/config.ts(SITE_NAME / SITE_LOGO_MARK)に集約し、未設定時は既定値へフォールバック。
- バンドルに同梱されブラウザに露出するため、秘密情報は置かない。
home.tsx / site-layout.tsx は @/config を import して参照、main.tsx が document.title を設定。
powerpages.config.json(必須)
{
"siteName": "MySite",
"compiledPath": "dist-site",
"defaultLandingPage": "index.html"
}
bundleFilePatterns(オプション)
pac pages upload-code-site は古いバンドルファイルを自動クリーンアップする。
デフォルトパターン: main.*.js, main.*.css, vendor.*.js, index-*.js, index-*.css 等 10 種。
Vite のデフォルト出力(index-{hash}.js)はカバーされるが、カスタムの命名規則を使う場合は明示指定する:
{
"siteName": "MySite",
"compiledPath": "dist-site",
"defaultLandingPage": "index.html",
"bundleFilePatterns": ["app.*.js", "app.*.css", "style.*.css"]
}
Step 1: SPA 開発
Vite 設定(必須制約)
export default defineConfig({
base: "./",
build: {
outDir: "dist-site",
rollupOptions: {
output: { inlineDynamicImports: true },
},
},
});
| 制約 | 理由 |
|---|
base: "./" | Power Pages のパス構造に対応 |
inlineDynamicImports: true | コード分割するとロード順問題が発生 |
| Hash ルーティング必須 | History API モードは直接 URL アクセスで 404 |
| 静的 SPA のみ | SSR / ISR 非対応 |
package.json scripts
{
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
"upload": "pac pages upload-code-site --rootPath . --compiledPath ./dist",
"deploy": "py ../.github/skills/power-pages/scripts/deploy_site.py"
}
}
★ ローカル確認を先に行う(デザイン変更時は必須)
レイアウト・配色・レスポンシブ対応など見た目に関わる変更は、本番デプロイ前に必ず npm run dev で確認する。
cd portal
npm run dev
| 確認できること | 確認できないこと(デプロイ後に要再確認) |
|---|
| コンポーネント構造・レイアウト崩れ・レスポンシブ | Power Pages 本体のテーマ CSS との衝突(例: Bootstrap 既定の見出し色が明示指定のない <h1> 等に強制適用される) |
| 自前 CSS の配色・グラデーション・アニメーション | 認証リダイレクト・Web ロール・テーブル権限まわりの挙動 |
| コンポーネント間の状態遷移・フォームバリデーション | Power Pages ランタイムが注入する外部スクリプト/スタイルとの相互作用 |
教訓 20: <h1> 等の見出し要素は、親要素に color: #fff を設定していても Power Pages 本体のテーマ CSS(h1 への既定色指定)に上書きされることがある(継承より明示指定が常に優先されるため)。見出し要素には必ず明示的に color を指定し、ローカル確認だけで満足せず本番デプロイ後にも目視確認する。
Step 2: 初回デプロイ
2-A: JavaScript ファイルのアップロード許可
環境で .js がブロックされている場合:
- Power Platform 管理センター → 環境選択
- 設定 → プライバシー + セキュリティ
- ブロックされた添付ファイルから
js を削除
2-B: ビルド & アップロード
cd portal
npm run build
pac pages upload-code-site --rootPath .
2-C: サイトのアクティブ化(PP API 経由)
py portal/scripts/activate_site.py
スクリプトが以下を自動実行:
- PP API でサイトが既にアクティブか確認
- Dataverse から Organization ID と Website Record ID を取得
- パラメータ確認後、PP API に POST
- Operation-Location をポーリングして完了待ち(最大 5 分)
カスタムサブドメインを指定する場合: py portal/scripts/activate_site.py --subdomain my-portal
2-D: Contact Web API 有効化(★初回必須)
py portal/scripts/setup_contact_webapi.py
2-E: サイト再起動
py .github/skills/standard/scripts/_restart.py
アクティブ化後、URL にアクセスできるまで 60〜90秒 かかる。
Step 3: 再デプロイ(2回目以降)
推奨: 統合スクリプトで一括実行(再現性が高い)
cd portal
py ../.github/skills/power-pages/scripts/deploy_site.py
手動で段階実行する場合
cd portal
npm run build
pac pages upload-code-site --rootPath . --compiledPath ./dist
py ../.github/skills/power-pages/scripts/relink_table_permissions.py
/profile(contact Self)を使う場合は、初回のみ setup_contact_self.py で
contact 権限と Webapi/contact/enabled|fields を作成しておく(教訓 16):
py ../.github/skills/power-pages/scripts/setup_contact_self.py
レビュースキル(品質ゲート)
Power Pages の品質を標準的に維持するための 設計前レビュー と デプロイ前レビュー を提供する。
呼び出し方
cd portal
python ../.github/skills/power-pages/scripts/review_pre_design.py
cd portal
python ../.github/skills/power-pages/scripts/review_pre_deploy.py
SKIP_REMOTE=1 python ../.github/skills/power-pages/scripts/review_pre_deploy.py
標準フロー: 設計前レビュー → 実装 → npm run dev でローカル確認(デザイン変更時は必須・教訓 20) → ビルド → デプロイ前レビュー → デプロイ → 本番での目視確認