[Azure x IoT] Azure IoTHubで受け取ったメッセージをDBに保存しよう

# 本記事のゴール

本記事ではIoTHubで受信したメッセージをRDBに保存できるようにします.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/366323/353d6f3c-2564-b7d7-231e-ed2f9e848c5f.png)


AzureFunctionからAzureDatabaseへのアクセスについてはVnet統合という機能を使います。

本記事の内容は筆者の次の3つの記事を組み合わせた内容になります。
IotHubは使わないけど、部分的に参考にしたいという方は以下を参考にしてください。
- [[Azure x IoT]Azure IoTHubにメッセージを送ろう](https://qiita.com/iron-samurai/items/3015971f15a20ccf954c)
- [[Azure x IoT]Azure IoTHub とAzure Functionsを連携しよう](https://qiita.com/iron-samurai/items/d5015f4bb18609f71aaa)
- [[Azure] Azure FunctionsからVnet内のRDBを操作する](https://qiita.com/iron-samurai/items/b86777889df16829775d)


# ****準備するもの****

- AzureIoTHub
- Azure Functions(Function PremiumかApp Service のStandardプラン以上)
- Azure Database For MySQL
- 踏み台仮想マシン
- vnet
- Vnet統合に使うFunction用のsubnet

# **1. Azure IoTHubを作成する**

AzurePortalでAzure IoTHubサービスを作成します。

プランはF1(無料）で大丈夫です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/366323/6c08b8b1-cdea-83af-78f7-e5e0d3637273.png)


# 2. Azure Database For MySQLと踏み台サーバーを用意する

1. 以下のチュートリアルを参考にDBと踏み台サーバーの作成を行う
    
    [Azure portal でプライベート アクセスを使用して Azure Database for MySQL フレキシブル サーバーに接続する](https://docs.microsoft.com/ja-jp/azure/mysql/flexible-server/quickstart-create-connect-server-vnet)
    
2. テスト用のデータベース、テーブルを作成
    
    ```sql
    CREATE DATABASE test_database;
    
    CREATE TABLE
        test_table (
            id INTEGER AUTO_INCREMENT PRIMARY KEY,
            message VARCHAR(255),
            created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
            updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
        );
    ```
    

ざっくりとした解説にとどめましたが、筆者の以下記事にもう少しだけ詳しく書いてあるのでよかったら参考にしてください

[[Azure] Azure FunctionsからVnet内のRDBを操作する - Qiita](https://qiita.com/iron-samurai/items/b86777889df16829775d#db%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B)

# 3. Function用のSunetを用意する

vnetはDB作成時に既に作成されているはずなので、そこにFunction用のsubnetを作成します。
先程DBに紐付けたvnetのページにいき、サイドメニューの”サブネット”を選択し、サブネットの追加を行います。名前は適当なもので構いません。サブネットアドレス範囲は自動で入力されるので名前さえ入力できればあとは保存して終了です！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/366323/85932665-1b9e-0387-6174-e7b4714301e1.png)


# 4. FunctionAppを作成する

1. AzurePortal上でFunctionAppを作成する
Vnet統合が使えるのは`AppServiceプランのStandard`以上だけなのでそこだけ注意してください。
参考:[https://docs.microsoft.com/ja-jp/azure/app-service/overview-vnet-integration](https://docs.microsoft.com/ja-jp/azure/app-service/overview-vnet-integration)
2. `Azure Functions Core Tools`のfuncコマンドで関数を作成.
    
    ```bash
    func new --name IoTHubTriggeredFunction --template "IoT Hub (Event Hub)"
    ```
    
3. functions.jsonを編集
    
    ```bash
    {
          "type": "eventHubTrigger",
          "name": "IoTHubMessages",
          "direction": "in",
          "eventHubName": "MyEventHub",  ←テンプレートから変更
          "connection": "myEventHubReadConnectionAppSetting",  ←テンプレートから変更
          "cardinality": "one", ←テンプレートから変更
          "consumerGroup": "$Default"
    }
    ```
    
4. index.tsを編集
    
    ```tsx
    import { AzureFunction, Context } from "@azure/functions"
    import {createConnection} from 'mysql2/promise'
    
    const IoTHubTrigger: AzureFunction = async function (context: Context, IoTHubMessage: any): Promise<void> {
    // ①接続情報オブジェクトを作成し、コネクションを確立する
        const config =
            {
                host: process.env['DB_HOST'],
                user: process.env['DB_USER_NAME'],
                password: process.env['DB_PASSWORD'],
                database: process.env['DB_DATABASE_NAME'],
                port: 3306,
                ssl : {
                    rejectUnauthorized: false
                 }
            };
    
        const conn = await createConnection(config);
    
    //②DBへinsert
        try {
            const [rows,fields] =
              await conn.query(`insert into test_table (message) values ('${IoTHubMessage.value}')`)
                .catch(e=>{throw Error(e)});
            context.log(fields)
          } catch(e) {
            context.log(e)
          } finally {
            conn.end();//接続を切る
          }
    };
    
    export default IoTHubTrigger;
    ```
    
5. デプロイ
    
    ここまででコードは完成したのでデプロイします。デプロイ前にビルドするのを忘れずに
    
    ```bash
    npm run build
    func azure functionapp publish ${FunctionAppの名前}
    ```
    
6. AzurePortalで環境変数を設定する
    
    作成した関数アプリのサイドメニューから「構成」を選びます。
    
    そこで”新しいアプリケーション設定”をクリックして以下を作成します。
    
    - DB_HOST
    - DB_USER_NAME
    - DB_PASSWORD
    - MyEventHub
    - myEventHubReadConnectionAppSetting
    
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/366323/840f530d-fc87-ec1b-7e6c-e94dca22b757.png)

    
    `MyEventHub`と`myEventHubReadConnectionAppSetting`
    に設定するIoTHubの名前とEvent Hub-compatible endpointはそれぞれIoTHubから確認できます
    
    - IoTHubの名前
        
        ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/366323/2e506a8e-fc17-6ea2-4056-4ba24b55d6b6.png)

        
    - Event Hub-compatible endpoint
        
        ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/366323/2fa46140-7341-b196-7e81-e9d47b74a071.png)

        

# 5. 動作確認

1. IotHubに対してメッセージを送信します
    
    必要な環境変数を設定して以下を実行します。
    
    ```bash
    curl -i -X POST \
    -H "Content-Type:application/json" \
    -H "Authorization:$SAS" \
    -d '{"value":"hello,world via iothub"}' \
    "https://$IOTHUB_NAME.azure-devices.net/devices/$DEVICE_NAME/messages/events?api-version=2018-06-30"
    ```
    
    SASは次のコマンドで取得できます
    
    ```bash
    az iot hub generate-sas-token -n $IOTHUB_NAME --du $EXPIRATION_SECONDS
    ```
    
    詳しくは筆者の[こちらの投稿](https://qiita.com/iron-samurai/items/3015971f15a20ccf954c)をご覧ください
    
2. VMからDBにログインしてレコードが挿入されているか確認する
以下のようにIoTHubに送ったメッセージの内容がinsertされていれば成功です！
    
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/366323/df989fed-35ab-414d-7511-7c167138c001.png)

    

# おわりに

いかがだったでしょうか。これでIoTデバイスから得られたセンシングデータをDBに格納することができるようになりました。あとはこのデータをアプリケーションに合わせて加工して、必要ならフロントエンドも足して、簡単なIoTシステムの完成です!