はじめに
Azure Functions で IoT Hub のデータを Cosmos DB に保存する備忘録です。
改訂履歴
- 2026/09/14 : 初版公開。
本文
1. 概要
Azure Functions は、サーバーレスでプログラムを実行するクラウドサービスです。普通の Web アプリケーションでは、サーバーやコンテナを常時動かしておく必要があります。一方 Azure Functions では、関数 (Function) 単位で処理を作り、HTTP リクエストやタイマー、ファイルのアップロードなどのトリガーをきっかけとして処理を実行できます。
料金については、以下を参照してください。
記事執筆時点では、以下の条件だと無料利用が可能です。
Azure Functions には無料枠がありますが、Azure Function が必要とする Storage Account は無料枠に含まれません。罠ですね。なるべく無料で頑張る場合 IoT Hub → Message Routing → Cosmos DB の構成になるのかなと思います。
2. 手順
2-1. Azure IoT Hub の作成
過去記事を参照してください。
組み込みのエンドポイントから、イベントハブ互換名とイベントハブ互換エンドポイントをコピーしておきます。
イベントハブ互換エンドポイントは接続文字列のため、取扱注意です。
2-2. Azure Functions の作成
Azure ポータルから関数アプリ (Function App) のページに移動し、作成します。
今回は無料試用版サブスクリプションのため、フレックス従量課金がサポートされていません。従量課金を選択します。
次の基本設定画面では、以下の内容を入力しました。
| 設定項目 | 設定内容 |
|---|---|
| サブスクリプション | Azure subscription 1 |
| リソース グループ | rg-azure-test |
| 関数アプリ名 | func-iot-test-20260914 |
| ランタイムスタック | .NET |
| バージョン | 8 (LTS), isolated worker model |
| リージョン | Japan West |
このリージョンはおそらくリソースグループと同一のリージョンでないとダメなようです。別のリージョンを選択すると作成時にエラーが発生します。
基本設定の入力後、次へ進みます。
仮想ネットワーク統合は、今回は不要です。オンにすると追加のネットワーク設定が必要になります。
Application Insights も使用しません。自動監視は便利そうですが、ログやテレメトリーのデータ量に応じて料金が発生するためです。
Durable Functions は関数の実行状態や結果を自動的に管理し、一連のワークフローとして複数の Functions をオーケストレーションできる仕組みだそうです。今回は単純なイベント処理のため、不要です。
継続的デプロイはしないので、不要です。
リソース認証はデフォルトのままです。2 つのストレージが自動生成されています。ホストストレージは Azure Functions の実行に必要なストレージ、Azure Files はコードやデプロイ用のパッケージを一時的に保持するためのストレージだそうです。
タグは設定しませんでした。
最後に「作成」ボタンをクリックして関数アプリを作成します。
デプロイの完了まで待ちます。
無事に完了しました。
2-3. プログラムの作成
Visual Studio 2022 で Azure Functions プロジェクトを作成します。
プロジェクト名は FunctionExample にしました。
Function は Event Hub trigger、Connection string setting name には EventHubConnectionAppSetting、Event Hub name は Azure IoT Hub でコピーしたイベントハブ互換名 iothub-ehub-iot-hub-te-56673887-0f9514615c を設定します。
プロジェクトが生成されたら local.settings.json に赤枠の部分を追加します。値は Azure IoT Hub で確認したイベントハブ互換エンドポイントを設定します。
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"EventHubConnectionAppSetting": "イベントハブ互換エンドポイント",
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated"
}
}
デバイスから Azure IoT Hub に送信している状態で、ビルドしてみます。今回は公式のラズパイシミュレーターを使用しています。System.ReadOnlyMemory<Byte>[113] で中身は分かりませんが、とりあえず正常に動作していそうです。
2-4. Azure Cosmos DB の作成
過去記事を参照してください。
設定のキーから、プライマリ接続文字列をコピーしておきます。
2-5. プログラムに DB 処理を追加する
local.settings.json に赤枠の部分を追加します。値は Azure Cosmos DB でコピーしたプライマリ接続文字列を設定します。
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"CosmosDbConnectionString": "プライマリ接続文字列",
"EventHubConnectionAppSetting": "イベントハブ互換エンドポイント",
"FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated"
}
}
NuGet パッケージとして Microsoft.Azure.Cosmos と Newtonsoft.Json を追加します。その後 Function1.cs に DB への保存処理を追加します。
using Azure.Messaging.EventHubs;
using Microsoft.Azure.Cosmos;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Extensions.Logging;
namespace FunctionExample;
public class Function1
{
private readonly ILogger<Function1> _logger;
+ private readonly Container _container;
public Function1(ILogger<Function1> logger)
{
_logger = logger;
+ string connectionString = Environment.GetEnvironmentVariable("CosmosDbConnectionString") ?? throw new InvalidOperationException("CosmosDbConnectionString is not configured.");
+ var cosmosClient = new CosmosClient(connectionString);
+ _container = cosmosClient.GetContainer("test-database", "test-container");
}
- [Function(nameof(Function1))]
- public void Run([EventHubTrigger("iothub-ehub-iot-hub-te-56673887-0f9514615c", Connection = "EventHubConnectionAppSetting")] EventData[] events)
- {
- foreach (EventData @event in events)
- {
- _logger.LogInformation("Event Body: {body}", @event.Body);
- _logger.LogInformation("Event Content-Type: {contentType}", @event.ContentType);
- }
- }
+ [Function(nameof(Function1))]
+ public async Task Run([EventHubTrigger("iothub-ehub-iot-hub-te-56673887-0f9514615c", Connection = "EventHubConnectionAppSetting")] EventData[] events)
+ {
+ foreach (EventData @event in events)
+ {
+ string message = Encoding.UTF8.GetString(@event.EventBody.ToArray());
+
+ _logger.LogInformation("IoT message: {message}", message);
+
+ var document = new
+ {
+ id = Guid.NewGuid().ToString(),
+ deviceId = "iot-hub-device-test-20260911",
+ timestamp = DateTime.UtcNow,
+ message = message
+ };
+
+ await _container.CreateItemAsync(document, new PartitionKey(document.id));
+
+ _logger.LogInformation("Cosmos DB saved. id={id}", document.id);
+ }
+ }
}
改めて実行してみます。データの DB 登録が実行されていそうです。
Azure Cosmos DB を確認すると、無事に登録されていることが確認できます。
2-6. Azure Functions にデプロイする
ローカルでの動作確認ができたので、次は Azure Functions にデプロイします。プロジェクトの右クリックメニューから発行を選択します。
Azure を選択します。
Azure 関数アプリを選択します。
デプロイ先のリソースを選択します。Visual Studio から Microsoft アカウントへのログインが必要です。
発行を選択します。
実行プロファイルの作成が完了したら、閉じます。
右上の「発行」ボタンをクリックして、Azure Functions にデプロイします。
デプロイが完了しました。
最初に試した際は以下のエラーが表示され、デプロイに失敗しました。原因はおそらくソースの配置先を含めたパスが長すぎるためです。ソースを C ドライブ直下に移動したところ、再度デプロイを実行できました。
"C:\Users\<アカウント名>.nuget\packages\system.security.cryptography.protecteddata\4.5.0\runtimes\win\lib\netstandard2.0\System.Security.Cryptography.ProtectedData.dll" を "bin\Release\net8.0\runtimes\win\lib\netstandard2.0\System.Security.Cryptography.ProtectedData.dll" にコピーできませんでした。1000 ミリ秒以内に 1 回目の再試行を開始します。パス 'bin\Release\net8.0\runtimes\win\lib\netstandard2.0\System.Security.Cryptography.ProtectedData.dll' の一部が見つかりませんでした。
Azure ポータルから関数アプリ (Function App) のページを見ると、無事にデプロイされていることが確認できます。
2-7. ストレージを設定する
デプロイ後、以下のエラーが発生していました。
Microsoft.Azure.WebJobs.Extensions.EventHubs: Storage account connection string 'AzureWebJobsStorage' does not exist. Make sure that it is a defined App Setting.
通常、Azure Functions 用のストレージやその設定情報である AzureWebJobsStorage は自動生成されるらしいですが、今回は何故か作られなかったようです。仕方ないため、以前作成した storagetest20260905 を AzureWebJobsStorage に設定して先へ進めます。ストレージの作成方法は過去記事を参照してください。
ストレージのアクセスキーから、接続文字列をコピーしておきます。
Azure Functions の環境変数から AzureWebJobsStorage を追加します。値はストレージでコピーした接続文字列を設定します。
追加後、適用を押すと反映できます。
概要に戻って再起動すると、エラーが消えました。
2-8. Azure Functions の接続設定を行う
Azure Functions の環境変数に CosmosDbConnectionString と EventHubConnectionAppSetting に追加、適用します。値はローカルでの動作確認時に設定した local.settings.json と同じ内容です。
動作確認のため、デバイスから Azure IoT Hub に送信します。Azure Cosmos DB を確認すると、無事に登録されていることが確認できます。
3. 参考
おわりに
IoT のクラウド連携よりも、保存したデータの活用の方が難しいと思います。