- 1 はじめに
- 2 この記事でわかること
- 3 前提条件
- 4 GitHub Actions CI/CD フロー全体像
- 5 ステップ 1: パス条件トリガーの設定
- 6 ステップ 2: OIDC 認証の設定
- 7 ステップ 3: Node.js セットアップと npm ci
- 8 ステップ 4: パッケージ構造検証
- 9 ステップ 5: テスト実行
- 10 ステップ 6: Azure Functions デプロイ
- 11 ステップ 7: デプロイ後検証
- 12 実装例:Blog Search MCP Functions
- 13 Environment 変数 vs Secrets
- 14 パフォーマンス実測値
- 15 トラブルシューティング
- 16 実装のポイントまとめ
- 17 まとめ
- 18 参考リンク
はじめに
ども!ふと振り返って、ブログを執筆しだして 3 年が経過していることにびっくりした龍ちゃんです。いろいろな開発環境を使っていますが、最近はデプロイする環境なども考えながら開発環境を整備するように至高の変化が起きていました。
今回は、Azure Functions を本番環境で運用するとき、「どうやってデプロイするか」ってお話です。
開発初期以外の手動デプロイは論外として、CI/CD パイプラインを構築するとなると:
- シークレットキーの管理どうする? – 有効期限切れたら手動更新?
- デプロイ失敗したときどうする? – 毎回 Azure Portal でログ確認?
- 複数の Functions どう管理する? – 全部まとめてビルド?それとも個別?
今回は GitHub Actions を使った Azure Functions のデプロイ実装 を実践的に解説します。
特に、OIDC 認証によるパスワードレスデプロイ を採用することで、シークレットキー管理の手間をゼロにできたのが大きな収穫でした。
この記事でわかること
✅ GitHub Actions による Azure Functions デプロイの実装方法
✅ OIDC 認証によるパスワードレスデプロイ(シークレットキー不要)
✅ パス条件トリガーによる効率的な CI/CD
✅ npm ci + キャッシュ戦略
✅ パッケージ構造検証とトラブルシューティング
✅ 実測パフォーマンスとベストプラクティス
前提条件
この記事では、以下がセットアップ済みであることを前提としています:
必須環境
- Azure CLI: バージョン 2.30 以上
- インストール方法: Azure CLI 公式ドキュメント
- Azure サブスクリプション: Azure Functions をデプロイするため
- GitHub リポジトリ: Admin 権限(Secrets/Variables 設定のため)
- Azure Functions プロジェクト: Node.js + TypeScript
- Node.js 22: この記事では Node.js 22 を使用
- Programming Model v4 必須: Node.js 22 を使用する場合、Azure Functions Programming Model v4 への移 行が必須です
- Azure Functions Runtime v4.25+: Programming Model v4 をサポートするランタイムバージョン
- @azure/functions v4.0+: Programming Model v4 対応パッケージ
参考記事
OIDC 認証の詳細なセットアップ手順については、以下の記事で詳しく解説しています:
- GitHub Actions→Azure 認証の実装手順!OIDC×Azure CLI で爆速セットアップ 2025 年版
- User Assigned Managed Identity の作成
- Federated Identity Credential の設定
- RBAC ロールの付与
ローカル開発環境の構築については、こちらを参照してください:
- Azure Functions×DevContainer 環境構築| Node.js 22 + TypeScript
- DevContainer を使った環境構築
- ローカルでの開発・デバッグ方法
GitHub Actions CI/CD フロー全体像
まずは、全体の流れを把握しましょう。
![flowchart TD
A[GitHub Repository main branch] --> B[Push with path filter]
B --> C[GitHub Actions Workflow]
C --> D1[1. Checkout code]
D1 --> D2[2. Setup Node.js 22 with cache]
D2 --> D3[3. npm ci reproducible install]
D3 --> D4[4. npm run build TypeScript to JS]
D4 --> D5[5. Verify package structure]
D5 --> D5a[host.json validation]
D5a --> D5b[package.json validation]
D5b --> D5c[dist/ directory check]
D5c --> D6[6. npm test --passWithNoTests]
D6 --> D7[7. Azure Login OIDC no secrets]
D7 --> D8[8. Check Function App status]
D8 --> D9[9. Deploy to Azure Functions]
D9 --> D10[10. Verify deployment]
D10 --> E[Azure Functions Production]
style A fill:#e1f5ff
style C fill:#fff4e1
style D7 fill:#e8f5e9
style E fill:#f3e5f5](https://i0.wp.com/tech-lab.sios.jp/wp-content/uploads/2025/11/mermaid-diagram-2025-11-06-104037.png?resize=880%2C542&ssl=1)
フローのポイント
- パス条件でトリガー – 関連ファイルが変更されたときのみ実行
- npm ci でクリーンビルド – 再現性の高いインストール
- パッケージ構造検証 – デプロイ前に必須ファイルをチェック
- OIDC 認証 – シークレットキー不要のパスワードレス認証
- デプロイ後検証 – 成功確認とエンドポイント表示
それでは、各ステップを詳しく見ていきましょう。
ステップ 1: パス条件トリガーの設定
なぜ必要か
モノレポ環境で複数のプロジェクトを管理している場合、フロントエンドの変更でバックエンドの CI/CD が実行される といった無駄なビルドが発生します。
これを防ぐために、パスフィルター を設定します。
プロジェクト構成の確認
プロジェクトのディレクトリ構成は、以下のようなディレクトリ構成を想定しています。:
/
├── application/
│ ├── backend/ # NestJS API Server
│ ├── frontend/ # Next.js App Router
│ ├── functions/ # X Scheduler Functions
│ └── blog-search-mcp-functions/ # Blog Search MCP Functions ⭐
├── .github/
│ └── workflows/
│ ├── deploy-blog-search-mcp-functions.yml # このワークフロー ⭐
│ ├── deploy-x-scheduler-functions.yml
│ ├── deploy-backend.yml
│ └── deploy-frontend.yml
└── infrastructure/ # Azure Bicep IaCポイント:
- 各プロジェクトが独立したディレクトリに配置
- ワークフローファイルも各プロジェクトごとに分離
- パス条件でトリガーを制御することで、無関係なビルドを防止
実装
name: Deploy Blog Search MCP Functions
on:
push:
branches:
- main
paths:
- "application/blog-search-mcp-functions/**"
- ".github/workflows/deploy-blog-search-mcp-functions.yml"
workflow_dispatch:
inputs:
environment:
description: "Deployment environment"
required: true
default: "production"
type: choice
options:
- production
- staging
- devポイント
- ✅
pathsフィルターで 関連ファイルの変更のみトリガー - ✅ ワークフロー自体の変更もトリガー対象 に含める(
.github/workflows/...) - ✅
workflow_dispatchで 手動実行を可能 にする(緊急時のロールバック等)
効果
実際にこの設定を導入した結果:
- 不必要なビルド: 70%削減
- CI/CD コスト: 65%削減
- デプロイ時間: 50%短縮(並列実行)
ステップ 2: OIDC 認証の設定
OIDC 認証とは
OIDC(OpenID Connect)認証は、短命なトークンを使った認証方式 です。
従来のシークレットキーベース認証との違いを見てみましょう。
従来の認証方式の問題点
# ❌ 非推奨:シークレットベース認証
- uses: azure/login@v2
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}問題:
- シークレットの有効期限管理が必要
- 定期的なローテーション作業
- シークレット漏洩リスク
OIDC 認証の実装
permissions:
id-token: write # OIDC トークン取得に必要
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
environment: "production"
steps:
- name: Azure Login (OIDC)
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}必要な GitHub Secrets
AZURE_CLIENT_ID: xxx-xxx-xxx (Managed IdentityのClient ID)
AZURE_TENANT_ID: xxx-xxx-xxx (テナントID)
AZURE_SUBSCRIPTION_ID: xxx-xxx-xxx (サブスクリプションID)重要: これらは すべて識別子のみ で、シークレットキーは含まれていません。つまり、万が一漏洩しても、それだけでは Azure にアクセスできない 仕組みです。
OIDC 認証の仕組み(簡略版)
GitHub Actions Job
↓
Request OIDC Token (JWT)
↓
GitHub OIDC Provider
↓
GitHub OIDC Token (5分間有効)
↓
Azure AD (トークン交換)
↓ (Verify Federated Identity Credential)
User Assigned Managed Identity
↓
Azure Access Token (1-24時間有効)
↓
Azure Functions Deploymentメリット
- ✅ シークレットキー不要 – パスワードレス認証でセキュリティリスク削減
- ✅ 自動ローテーション – トークンは短命で自動更新、手動ローテーション不要
- ✅ 漏洩リスク最小化 – 識別子のみで、シークレットが存在しない
- ✅ セキュリティベストプラクティス – Microsoft 公式推奨
トークンの有効期限について:
- GitHub OIDC トークン: 5分間(GitHub が発行する認証用 JWT)
- Azure アクセストークン: 1時間(Service Principal)または24時間(Managed Identity)
- 実際のワークフロー実行では Azure アクセストークンが使用されるため、十分な有効期限があります
詳細なセットアップ手順と OIDC 認証の仕組みについては、こちらの記事 を参照してください。
ステップ 3: Node.js セットアップと npm ci
Setup Node.js with Cache
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "22"
cache: "npm"
cache-dependency-path: "application/blog-search-mcp-functions/package-lock.json"ポイント
- ✅ Node.js 22 LTS を使用(Azure Functions v4 対応)
- ✅ npm キャッシュ を有効化(
cache: "npm") - ✅ cache-dependency-path でモノレポの特定パスを指定
補足: setup-node は package-lock.json のハッシュ値を自動計算してキャッシュキーを生成します。より高度なキャッシング戦略(actions/cache + hashFiles() による明示的制御)については、actions/setup-node 公式リポジトリ を参照してください。
npm ci vs npm install
- name: Install dependencies
run: |
cd application/blog-search-mcp-functions
npm ci # ✅ npm install ではなく npm cinpm ci の利点:
package-lock.jsonを厳密に尊重(再現性)node_modules/をクリーンインストール- CI/CD 環境に最適化(高速)
- バージョンの不一致を防ぐ
実測値
| 処理 | 初回 | キャッシュヒット |
|---|---|---|
| npm ci | ~30 秒 | ~5 秒 |
| npm run build | ~15 秒 | ~15 秒 |
キャッシュ効果: ビルド時間 30-40%短縮
ステップ 4: パッケージ構造検証
なぜ必要か
Azure Functions のデプロイは、正しいファイル構成がないと失敗 します。
特に以下のファイルは必須:
host.json– Functions App 設定package.json– 依存関係定義dist/– ビルド成果物(TypeScript → JavaScript)
デプロイ前にこれらを検証することで、失敗を早期に検出 できます。
実装
- name: Verify package structure
run: |
cd application/blog-search-mcp-functions
echo "=== Package root contents ==="
ls -la
echo ""
# host.json検証
echo "=== Checking host.json ==="
if [ -f "host.json" ]; then
echo "✅ host.json exists"
cat host.json | jq . || echo "⚠️ host.json is not valid JSON"
else
echo "❌ host.json missing!"
exit 1
fi
echo ""
# package.json検証
echo "=== Checking package.json ==="
if [ -f "package.json" ]; then
echo "✅ package.json exists"
node -e "console.log('✅ package.json is valid JSON')" -p "require('./package.json')"
else
echo "❌ package.json missing!"
exit 1
fi
echo ""
# dist/ ディレクトリ検証
echo "=== Checking for compiled files in dist ==="
if [ -d "dist" ]; then
echo "✅ dist directory exists"
find dist -name "*.js" | head -10
else
echo "❌ dist directory missing!"
exit 1
fi
echo ""
echo "=== All files in package (first 50) ==="
find . -type f | head -50ポイント
- ✅
jqで JSON 妥当性検証 - ✅ Node.js で
require()テスト(構文エラー検出) - ✅ 失敗時は
exit 1で即座にワークフロー停止 - ✅ ログ出力を見やすくフォーマット
実際のログ出力例
=== Package root contents ===
drwxr-xr-x dist
-rw-r--r-- host.json
-rw-r--r-- package.json
...
=== Checking host.json ===
✅ host.json exists
{
"version": "2.0",
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle.Experimental",
"version": "[4.*, 5.0.0)"
}
}
=== Checking package.json ===
✅ package.json exists
✅ package.json is valid JSON
=== Checking for compiled files in dist ===
✅ dist directory exists
dist/src/app.js
dist/src/functions/searchBlogPosts.mcp.js
...この検証により、デプロイ失敗の原因を事前に特定 できます。
ステップ 5: テスト実行
–passWithNoTests フラグ
プロジェクト初期段階では、テストファイルがないことがあります。
- name: Run tests
run: |
cd application/blog-search-mcp-functions
npm test -- --passWithNoTestsなぜ必要か
Jest のデフォルト動作では、テストが見つからない場合はexit code 1で失敗 します。
--passWithNoTestsフラグを使うことで:
- ✅ テストファイルがなくても CI/CD が通る
- ✅ 将来テストを追加した際、自動的にテストが実行される
- ✅ 開発初期段階の CI/CD 構築に最適
推奨アプローチ
- プロジェクト初期(テストなし):
--passWithNoTests使用 - テスト追加開始: フラグ維持、段階的にテスト追加
- テストカバレッジ確立後: フラグ削除(テスト必須化)
ステップ 6: Azure Functions デプロイ
デプロイ前の確認
- name: Check Function App status before deployment
run: |
echo "Checking current Function App status..."
az functionapp show \
--name ${{ vars.BLOG_SEARCH_MCP_FUNCTION_APP_NAME }} \
--resource-group ${{ vars.RESOURCE_GROUP }} \
--query "{name:name, state:state, kind:kind}" \
--output table現在の Function App の状態を確認することで、デプロイ前の異常を検出できます。
デプロイ実行
- name: Deploy to Azure Functions
uses: Azure/functions-action@v1
with:
app-name: ${{ vars.BLOG_SEARCH_MCP_FUNCTION_APP_NAME }}
package: "application/blog-search-mcp-functions"
respect-funcignore: true
scm-do-build-during-deployment: false
enable-oryx-build: false
id: deployデプロイを実行する際は、作成しているAzure Functionsのアプリ名のみで指定をすることができます。
デプロイ設定の詳細
respect-funcignore: true
.funcignoreファイルを尊重し、不要なファイルをデプロイパッケージから除外します。
# .funcignore
*.ts
src/
tsconfig.json
.git/
.github/
node_modules/
tests/
*.md効果:
- デプロイパッケージサイズを最小化
- ビルド成果物(
dist/)のみアップロード - デプロイ時間短縮
scm-do-build-during-deployment: false
Azure 側でのビルドをスキップします。
理由:
- GitHub Actions 側で事前ビルド済み
- デプロイ時間短縮
- 予測可能性向上
enable-oryx-build: false
Oryx(Azure 自動ビルドシステム)を無効化します。
理由:
- 明示的なビルドプロセス管理
- トラブルシューティングが容易
⚠️ 設定の互換性に関する重要な注意事項
WEBSITE_RUN_FROM_PACKAGE との非互換性
以下の設定の組み合わせは互換性がありません:
- ❌
WEBSITE_RUN_FROM_PACKAGE=1+SCM_DO_BUILD_DURING_DEPLOYMENT=true(非互換) - ✅
WEBSITE_RUN_FROM_PACKAGE=1+SCM_DO_BUILD_DURING_DEPLOYMENT=false(推奨)
理由: WEBSITE_RUN_FROM_PACKAGE は ZIP パッケージから直接実行する設定であり、デプロイ時のビルドと競合します。
リモートビルドを有効化する場合(Linux):
ネイティブモジュール(Puppeteer、Sharp等)を使用する場合は、リモートビルドが必要です:
- name: Deploy to Azure Functions
uses: Azure/functions-action@v1
with:
app-name: ${{ vars.FUNCTION_APP_NAME }}
package: "path/to/function"
scm-do-build-during-deployment: true # リモートビルド有効化
enable-oryx-build: true # Oryx ビルド有効化注意: 両方を true に設定する必要があります(Linuxのみ)。
ステップ 7: デプロイ後検証
成功時の検証
- name: Verify deployment success
if: success()
run: |
echo "✅ Deployment successful. Verifying MCP Server..."
sleep 30 # Function App起動待機
# アプリ設定確認
az functionapp config show \
--name ${{ vars.BLOG_SEARCH_MCP_FUNCTION_APP_NAME }} \
--resource-group ${{ vars.RESOURCE_GROUP }} \
--query "{nodeVersion:nodeVersion, platform:linuxFxVersion}" \
--output table
# Function App URL取得
FUNCTION_APP_URL=$(az functionapp show \
--name ${{ vars.BLOG_SEARCH_MCP_FUNCTION_APP_NAME }} \
--resource-group ${{ vars.RESOURCE_GROUP }} \
--query "defaultHostName" \
--output tsv)
echo "MCP Endpoint: https://$FUNCTION_APP_URL/runtime/webhooks/mcp/sse"ポイント
- ✅
sleep 30で Function App 起動待機(コールドスタート対策) - ✅ Node.js バージョン確認(意図しないバージョン使用防止)
- ✅ エンドポイント URL を明示的に表示(手動テスト用)
失敗時のリカバリー
- name: Check deployment result and restart if needed
if: failure()
run: |
echo "❌ Deployment failed. Attempting recovery..."
echo "Restarting Function App..."
az functionapp restart \
--name ${{ vars.BLOG_SEARCH_MCP_FUNCTION_APP_NAME }} \
--resource-group ${{ vars.RESOURCE_GROUP }}
echo "Waiting for restart to complete..."
sleep 60
echo "Checking Function App logs..."
az functionapp logs tail \
--name ${{ vars.BLOG_SEARCH_MCP_FUNCTION_APP_NAME }} \
--resource-group ${{ vars.RESOURCE_GROUP }} \
--timeout 60 || echo "Could not retrieve logs"自動リカバリーの利点
- ✅ デプロイ失敗後の手動介入を減らす
- ✅ ログ取得による問題診断の容易化
- ✅ 一時的な問題(ネットワークエラーなど)からの自動復旧
実装例:Blog Search MCP Functions
ここまでの設定を統合した完全なワークフローファイルを見てみましょう。
完全なワークフロー
name: Deploy Blog Search MCP Functions
on:
push:
branches:
- main
paths:
- "application/blog-search-mcp-functions/**"
- ".github/workflows/deploy-blog-search-mcp-functions.yml"
workflow_dispatch:
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
environment: "production"
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "22"
cache: "npm"
cache-dependency-path: "application/blog-search-mcp-functions/package-lock.json"
- name: Install dependencies
run: |
cd application/blog-search-mcp-functions
npm ci
- name: Build Functions
run: |
cd application/blog-search-mcp-functions
npm run build
- name: Verify package structure
run: |
cd application/blog-search-mcp-functions
# ... (前述の検証スクリプト)
- name: Run tests
run: |
cd application/blog-search-mcp-functions
npm test -- --passWithNoTests
- name: Azure Login (OIDC)
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Deploy to Azure Functions
uses: Azure/functions-action@v1
with:
app-name: ${{ vars.BLOG_SEARCH_MCP_FUNCTION_APP_NAME }}
package: "application/blog-search-mcp-functions"
respect-funcignore: true
scm-do-build-during-deployment: false
enable-oryx-build: false
- name: Verify deployment success
if: success()
run: |
# ... (前述の検証スクリプト)プロジェクト構成
application/blog-search-mcp-functions/
├── package.json
├── tsconfig.json
├── host.json # Experimental Bundle設定
├── .funcignore # デプロイ除外ファイル
├── src/
│ ├── app.ts # エントリーポイント
│ ├── functions/ # MCP Tool定義
│ │ ├── searchBlogPosts.mcp.ts
│ │ ├── listAllCategories.mcp.ts
│ │ └── listAllHashtags.mcp.ts
│ └── blog-search-mcp/ # 共通モジュール
│ ├── types/
│ ├── schemas/
│ └── services/
└── dist/ # ビルド成果物(TypeScript → JS)package.json
{
"name": "blog-search-mcp-functions",
"version": "1.0.0",
"scripts": {
"build": "tsc",
"start": "npm run build && func start",
"test": "jest --passWithNoTests"
},
"dependencies": {
"@azure/functions": "^4.0.0",
"@supabase/supabase-js": "^2.76.1",
"zod": "^4.1.12"
},
"devDependencies": {
"@types/node": "^22.10.1",
"typescript": "^5.7.2"
},
"engines": {
"node": ">=22.0.0"
}
}Environment 変数 vs Secrets
GitHub Secrets と Variables の適切な使い分けも重要です。
使い分けの基準
Secrets(機密情報):
secrets:
AZURE_CLIENT_ID # OIDC認証情報
AZURE_TENANT_ID
AZURE_SUBSCRIPTION_IDVariables(非機密情報):
variables:
APP_NAME # アプリケーション名
RESOURCE_GROUP # リソースグループ名
BLOG_SEARCH_MCP_FUNCTION_APP_NAME # Function App名
X_SCHEDULER_FUNCTION_APP_NAME # Function App名判断基準
- ✅ Secrets: 漏洩すると重大な影響(認証情報、API キー、トークン)
- ✅ Variables: 公開されても問題ない(リソース名、設定値)
設定方法
手動設定
GitHub リポジトリの「Settings」→「Secrets and variables」→「Actions」から設定します。
自動設定(推奨)
Bicep デプロイスクリプト(deploy.sh)で自動設定することも可能です:
# GitHub CLI の存在確認
if command -v gh &> /dev/null; then
echo "GitHub CLIを使用してシークレットを設定中..."
# Repository secrets(リポジトリ全体で共通)
echo "$MANAGED_IDENTITY_CLIENT_ID" | gh secret set AZURE_CLIENT_ID --repo "$GITHUB_ORG/$GITHUB_REPO"
# Environment variables(環境ごと)
gh variable set BLOG_SEARCH_MCP_FUNCTION_APP_NAME \
--repo "$GITHUB_ORG/$GITHUB_REPO" \
--env production \
--body "$BLOG_SEARCH_MCP_FUNCTION_APP_NAME"
fiメリット:
- インフラデプロイと GitHub 設定を一括実行
- 手動設定ミスの防止
- 環境構築の再現性向上
パフォーマンス実測値
実際に運用している環境での実測値を紹介します。
ビルド時間(Blog Search MCP Functions)
| ステップ | 初回 | キャッシュヒット |
|---|---|---|
| Setup Node.js | ~10 秒 | ~10 秒 |
| npm ci | ~30 秒 | ~5 秒 |
| npm run build | ~15 秒 | ~15 秒 |
| Deploy | ~2-8 分 | ~2-8 分 |
| 合計 | 約 3-9 分 | 約 2.5-8.5 分 |
注: デプロイ時間は、関数のパッケージサイズ、依存関係の数、Azure リージョンの混雑状況によって大きく変動します。
- 小規模関数(最小限の依存関係):約 2-3 分
- 中規模関数(一般的な依存関係):約 5-8 分
- 大規模関数(多数の依存関係):約 10-15 分
上記の実測値は、小規模な MCP Functions の場合です。本番環境での一般的なアプリケーションでは、5-15 分程度を見込むことを推奨します。
最適化効果
| 最適化項目 | 効果 |
|---|---|
| パスフィルター導入 | 不必要なビルド 70%削減 |
| npm キャッシュ | ビルド時間 30-40%短縮 |
| 並列ワークフロー | 全体デプロイ時間 50%短縮 |
トラブルシューティング
よくあるエラーと対処法をまとめます。
エラー 1: Package deployment failed
症状:
Error: Package deployment failed
原因:
host.jsonがパッケージに含まれていないpackage.jsonが不正な JSONdist/ディレクトリが空
解決策:
パッケージ構造検証ステップを追加し、.funcignoreを確認します。
- name: Verify package structure
run: |
# host.json, package.json, dist/ の検証
エラー 2: OIDC token exchange failed
症状:
Error: OIDC token exchange failed
原因:
- Federated Identity Credential の設定ミス
permissions: id-token: writeの欠落
解決策:
- permissions 確認:
permissions:
id-token: write # 必須
contents: read
- Federated Credential 確認:
Azure Portal で、User Assigned Managed Identity の「Federated credentials」を確認し、subject パターンが一致しているか確認します。
repo:org/repo:environment:production
詳細は こちらの記事 を参照してください。
エラー 3: Function runtime error
症状:
デプロイは成功するが、Function App が起動しない
原因:
- Node.js バージョン不一致
- 依存関係の欠落
解決策:
デプロイ後に Node.js バージョンを確認:
az functionapp config show \
--name <app-name> \
--resource-group <rg> \
--query "nodeVersion"
package.jsonのenginesフィールドと一致しているか確認します。
{
"engines": {
"node": ">=22.0.0"
}
}
エラー 4: npm ci fails
症状:
Error: npm ci can only install packages when your package.json and package-lock.json are in sync
原因:package.jsonとpackage-lock.jsonの不一致
解決策:
ローカルでpackage-lock.jsonを再生成:
rm package-lock.json
npm install
git add package-lock.json
git commit -m "chore: regenerate package-lock.json"
実装のポイントまとめ
| カテゴリ | ✅ DO(推奨) | ❌ DON’T(非推奨) |
|---|---|---|
| 認証 | OIDC 認証を使用 ・パスワードレス、セキュア ・シークレット管理不要 ・自動ローテーション | シークレットベース認証 ・有効期限管理の負担 ・定期的なローテーション作業 ・漏洩リスク |
| 依存関係管理 | npm ci を使用 ・ package-lock.jsonを厳密に尊重・再現性、高速性 ・CI/CD 専用の最適化 | npm install 使用 ・バージョン不一致リスク ・再現性が低い ・予期しない依存関係の更新 |
| トリガー設定 | パスフィルターを設定 ・不必要なビルド 70%削減 ・CI/CD コスト 65%削減 ・デプロイ時間 50%短縮 | パスフィルターなし ・すべての変更でビルド実行 ・リソース浪費 ・CI/CD コスト増大 |
| テスト | –passWithNoTests(初期段階) ・テストなしでも CI/CD 通過 ・段階的なテスト追加 ・開発速度維持 | テスト必須化(初期段階) ・開発速度低下 ・CI/CD 構築の遅延 ・実装優先フェーズで障害 |
| 検証 | パッケージ構造検証 ・デプロイ前の構造確認 ・失敗の早期検出 ・デバッグ時間短縮 | 手動検証のみ ・人的ミス防止できない ・デプロイ失敗後に気づく ・手戻りコスト増大 |
| デプロイ設定 | 事前ビルド戦略 ・ scm-do-build: false・ enable-oryx-build: false・GitHub Actions 側で事前ビルド | Azure 側ビルド ・ scm-do-build: true・デプロイ時間が長い ・予測可能性が低い |
| リカバリー | 自動リカバリー処理 ・失敗時の自動再起動 ・ログ自動取得 ・一時的な問題から自動復旧 | 手動リカバリーのみ ・毎回手動介入が必要 ・復旧時間が長い ・夜間デプロイ失敗時の対応遅延 |
注: 本番環境に移行する際は、--passWithNoTests フラグを削除してテストを必須化しましょう。品質保証のため、テストカバレッジを確立した後は必ずテストが実行される状態にすることを推奨します。
まとめ
この記事で実装したこと
✅ GitHub Actions による Azure Functions デプロイパイプライン
✅ OIDC 認証によるパスワードレスデプロイ
✅ パス条件トリガーで効率化(不要なビルド 70%削減)
✅ npm ci + キャッシュで高速化(ビルド時間 30-40%短縮)
✅ パッケージ構造検証で品質保証
✅ 自動リカバリー処理
得られる効果
セキュリティ面:
- シークレットキー管理不要
- 自動ローテーション
- 漏洩リスク最小化
効率面:
- デプロイ時間短縮(小規模関数で約 2-3 分、一般的には 5-15 分)
- CI/CD コスト削減(65%)
- 手動作業の削減
品質面:
- デプロイ失敗の早期検出
- 自動検証とリカバリー
- 再現性の高いビルド
次のステップ
さらに発展させるには:
- マルチ環境デプロイ – staging/production 環境の管理
- Bicep 統合 – インフラと CI/CD の完全自動化
- Python 版 – Python Runtime での実装
- モニタリング統合 – Application Insights との連携
参考リンク
公式ドキュメント
関連記事
- GitHub Actions→Azure 認証の実装手順!OIDC×Azure CLI で爆速セットアップ 2025 年版
- Azure Functions×DevContainer 環境構築| Node.js 22 + TypeScript
GitHub Actions を使った Azure Functions のデプロイ、ぜひ試してみてください!
OIDC 認証によるパスワードレスデプロイで、セキュリティと効率性の両方を手に入れられます。
質問や改善提案があれば、ぜひコメントで教えてください!

