はじめに
こんにちは!インサイトテクノロジーの松尾です!
普段AWSでS3 + Lambdaで「ファイルが置かれたら後続処理」をちょくちょく利用しているのですが、Azureで同様のことをやろうとして、「なかなかうまくいかない」という洗礼を受けました。
結論から言うと、「Event Gridトリガー」こそがS3イベント通知に相当する正解でした。
試行錯誤の末にたどり着いた、最も安定する「最短開通手順」をStep by Stepで解説します。
1. 実現したいこと
今回私が実現したかったことは以下です。
- Blob Storageに
trigger.jsonというファイルを配置 - ファイルが配置されたらAzure Functions(Python)を起動
-
trigger.jsonの内容をリクエストボディとして、Azure VMで別途起動しているAPIへPOSTリクエスト - Functionsは従量課金を使用
本投稿の前提
本投稿では、事前に以下が存在するものとして記載しています。検知してFunctionsがトリガーされた後の処理は、要件に合わせて読み替えてください。
- ファイルの配置を検知を行いたいストレージアカウントとコンテナー
- 処理した後にリクエストするAPI(Azure VM上で起動)
開発環境の事前準備
Azure Functionsは、VS Codeで開発作業を行ってそれをAzureへデプロイするというのが一般的なフローのようです。VS Codeに以下の拡張機能(Extensions)を必ずインストールしてください。
- VS Code
- Azure Functions:プロジェクトの作成、デプロイ、ログ確認に必須です。
- Azure Resources:ストレージや関数アプリの状態をVS Code上から確認できます。
- Python (Microsoft公式):インテリセンスやデバッグに必須です。
- Python
- WindowsのPython環境:Windows環境にpython.orgからダウンロードしてインストールしたPython 3環境をインストールしておきます。
- PythonはMicrosoft Storeからインストールしたものだと問題があるようです。
- 私の手元の環境は3.14でしたが、安定板の3.13などを必要に応じてインストールください。
- Azure Functionsの環境は3.13を選択しましょう。
全体構成
Blob StorageへのアップロードをEvent Gridが検知し、Functionsを叩き起こす「プッシュ型」の構成です。
- Azure Blob Storage: 監視対象のストレージ
- Azure Event Grid: ファイル作成を検知し、Functionsへ通知
- Azure Functions (Python): 通知を受け取り、Blobを読み込んで外部APIへ送信
- Azure VM: POSTリクエストを受けるREST APIが稼働
2. Step 1: Azure ポータルでの「関数アプリ」作成
それでは早速進めていきましょう。
まず、Azure ポータルで、Azure Functions用の受け皿を作成します。
- Azureポータルで「関数アプリ」を検索し、[作成] をクリック。
- 最新のプランである「フレックス従量課金」を選択。
- リソースグループ、
- 関数アプリ名: 任意の名前(例: event-grid-trigger)
- リージョン: Japan East
- ランタイムスタック: Python
- バージョン: 3.13
- [確認および作成] をクリックし、[作成] をクリックして関数アプリを作成します。
3. Step 2: Azure Functionsプロジェクトの作成
VS CodeのAzure拡張機能を使用します。
プロジェクトの初期化
- VS Codeを起動して空のフォルダーを選択。
- Ctrl+Shift+Pでコマンドパレットを開き
Azure Functions: Create New Project...を選択。
- そのままフォルダを選択
-
Project type:
Pythonを選択。
-
Python interpreter: ローカルにインストールされているPythonバージョンを選択。※ここにリストされていない場合はPythonの実行にパスが通っていないなどの可能性があります。
-
Template:
Event Grid Triggerを選択。 
- 関数名を指定
- 以下のように選択フォルダに必要なファイルが生成されます!
ライブラリの準備 (requirements.txt)
既定で含まれている azure-functions に加え、Blob Storage操作とAPIリクエストに必要なライブラリを追加します。
azure-functions
azure-storage-blob
requests
実装コード (function_app.py)
Event Gridから届いた情報をもとに以下の処理を行う実装コードをfunction_app.pyファイルに上書きします。
- ファイルパスを取得
- Blob Storageにあるファイルを読み込み
- 読み込んだファイルの内容をリクエストボディとしてAPI送信
import azure.functions as app_func
import logging
import os
import requests
from azure.storage.blob import BlobClient
from urllib.parse import urlparse
app = app_func.FunctionApp()
@app.event_grid_trigger(arg_name="event")
def EventGridTrigger1(event: app_func.EventGridEvent):
logging.info("=========================================")
logging.info("[STEP 1] Python版 Event Grid通知を受信しました。")
try:
# 1. イベントデータをJSONとして取得
event_data = event.get_json()
logging.info(f"[DEBUG] 受信データ全容: {event_data}")
# urlを取得
blob_url = event_data.get('url')
if not blob_url:
logging.error("[ERROR] URLが取得できませんでした。")
return
# 2. URLからコンテナ名とパスを自動抽出
# 例: https://xxx.blob.core.windows.net/my-container/test/file.json
parsed_url = urlparse(blob_url)
# pathは "/my-container/test/file.json" となるので分解
path_parts = parsed_url.path.lstrip('/').split('/', 1)
if len(path_parts) < 2:
logging.error(f"[ERROR] URLの形式が不正です: {blob_url}")
return
container_name = path_parts[0] # "my-container"
blob_path = path_parts[1] # "test/file.json"
logging.info(f"[STEP 2] 対象特定 - コンテナ: {container_name}, パス: {blob_path}")
# 3. 業務データ用ストレージからファイルを読み込む
logging.info("[STEP 3] ストレージからファイルの中身を読み込んでいます...")
conn_str = os.getenv("DATA_STORAGE_CONNECTION_STRING")
if not conn_str:
logging.error("[ERROR] 環境変数 DATA_STORAGE_CONNECTION_STRING が未設定です。")
return
# 抽出したコンテナ名とパスを使ってクライアント作成
blob_client = BlobClient.from_connection_string(
conn_str=conn_str,
container_name=container_name,
blob_name=blob_path
)
# 中身をテキストとして取得
content = blob_client.download_blob().readall().decode('utf-8')
logging.info(f"[SUCCESS] ファイル読み込み完了 (サイズ: {len(content)}文字)")
# 4. 外部VMのAPIへPOST送信
api_url = os.getenv("EXTERNAL_WEB_API_URL")
logging.info(f"[STEP 4] 外部APIへデータを送信中... {api_url}")
response = requests.post(
api_url,
data=content.encode('utf-8'),
headers={'Content-Type': 'application/json'},
timeout=30
)
if response.status_code == 200:
logging.info(f"[FINISH] 全ての処理が正常に完了しました!")
else:
logging.error(f"[FAILED] APIがエラーを返しました。 Status: {response.status_code}")
except Exception as e:
logging.error(f"[FATAL ERROR] 処理中に例外が発生しました: {str(e)}")
logging.info("=========================================")
4. Step 3:デプロイと環境変数の設定
デプロイ
VS CodeのAzureアイコンから、Step 1で作成した新しい関数アプリを指定してデプロイします。
- VS Code の Azure アイコンを選択し、「Sign in to Azure...」を選択してAzureにログインします。

ログインすると権限のあるサブスクリプションの一覧が表示されます。
- 「WORKSPACE」の「Local Project」の右側のアイコン「Deploy to Azure」を選択します。
- デプロイ先のサブスクリプションを選択します。
- Step 1で作成した関数アプリを選択します。
- 関数を上書きする旨が表示されるので、「Deploy」を選択します。
- 関数アプリに関数が追加されたことを確認できます。
環境変数の追加
Azureポータルで [関数アプリ] > [設定] > [環境変数] に以下を追加。
Azure VMでは、APIに対するリクエストを受信できるよう、Network Security Groupの設定が必要です。
Event Gridの配線
- 対象の Blob Storage の画面を開き、左メニューの「イベント」をクリック。
- 「+イベント サブスクリプション」をクリック。
- 以下を選択して作成します。
- イベントサブスクリプションの名前:適当に設定
- システムトピック名:適当に設定
- フィルター:Blob Createdのみ
- エンドポイントの種類:Azure関数
- エンドポイント:作成した関数を選択
ファイルが置かれるたびに無駄に関数が走らないよう、特定のファイル名のみに限定し、かつ失敗時の再試行を制限して「1回勝負」の設定にします。
特定のファイル名(trigger.json)のみに限定する
-
作成したイベントサブスクリプション の編集画面を開きます([Blob Storage] > [イベント] > 作成したサブスクリプションを選択)。
-
「フィルター」タブをクリックします。
-
「サブジェクト フィルタリングを有効にする」にチェックを入れます。
-
「次で始まるサブジェクト」に
/blobServices/default/containers/input-container/blobs/と入力します。 -
「保存」をクリックして完了です。
- これにより、
.pngや.txtが置かれても Functions は起動せず、trigger.jsonが置かれた時だけ発火するようになります。
重複起動(再試行)を防止する
Event Gridは標準で「最大24時間、何度も再試行する」設定になっています。API疎通待ちなどで重複して動かないよう、回数を絞ります。
- 同じ画面の「追加の機能」タブをクリックします。
- 「再試行ポリシー」セクションを探します。
- 「最大再試行回数」を
1に変更します。
- 「イベントの有効期間 (TTL)」を
1分(最小値)に設定します。 - 「保存」をクリックして完了です。
5. 稼働確認
これで準備は終了です。ではtrigger.jsonファイルを監視対象のコンテナ内に配置してみましょう。
処理状況を確認するために、ログストリームを表示しておきます。
Blob Storageの対象コンテナにファイルをアップロードしてみましょう!
すると、、、、イベントを受信しFunctionsが動作したことが確認できましたね。
イベントを拾って処理ができることがわかれば、あとは、このログ出力を確認しながら、所望の処理ができるようデバッグをしましょう。
5. ハマりポイント
- ファイルを置いてもFunctionsが動作しないとき
- Azure VMに立てたAPIコールがタイムアウトになるとき
- もし
[STEP 4]で止まってタイムアウトになる場合は、VM側の設定を確認してください。 -
NSG(ネットワークセキュリティグループ): 受信規則で対象ポート(8000等)を開放します。送信元が不明な場合は、一旦
Anyでテストして切り分けるのが吉です。
- もし
おわりに
実はこの方法にたどりつくまでに、Blobトリガーでやろうとしてでかなり試行錯誤しました、、が、結局うまくいきませんでした。ファイルを置いてもFunctionsが呼ばれないのです。海外の方のブログで従量課金にしなければ動作したよ、なんて報告もありましたが、どうしても従量課金にしたく、悪戦苦闘したのですが、最終的にEvent Gridトリガーで処理を行うことができました。
S3 + Lambdaなら簡単なのに。。。。。。
本投稿が何かのお役に立てば幸いです。






