[Azure x IoT] AzureIoTHubとAzureFunctionを連携しよう(Terraform編)

# はじめに
[[Azure x IoT]Azure IoTHub とAzure Functionsを連携しよう](https://qiita.com/iron-samurai/items/d5015f4bb18609f71aaa)では、UI操作を紹介しながらアプリケーションを作成しましたが、「そんなちまちまやらずにインフラ構築はterraformに任せてとりあえず試してみたい！」という方向けの記事です

# 本記事のゴール

以下をterraformで構築します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/366323/9623fc7f-c684-7b99-b1e5-a8a01d08950f.png)


# コード

下記レポジトリを参照してください

[https://github.com/ironkicka/tff_az_templates/tree/main/iotHub_to_function](https://github.com/ironkicka/tff_az_templates/tree/main/iotHub_to_function)

# 構築

以下はレポジトリのREADMEの転載です。

1.デプロイ

```bash
terraform plan
terraform apply
```

2.関数の中身のデプロイ

```bash
cd IoTHubTriggeredFunction && npm i && npm run deploy
```

3.IoTHub上にデバイスを作成

```bash
export IOTHUB_NAME=xxxx
export DEVICE_NAME=xxxx
az iot hub device-identity create -n $IOTHUB_NAME -d $DEVICE_NAME
```

4.SASトークンを発行

```bash
export IOTHUB_NAME=xxxx
export EXPIRATION_SECONDS=xxxx
az iot hub generate-sas-token -n $IOTHUB_NAME --du $EXPIRATION_SECONDS
```

5.IoTHubにメッセージを送信

```bash
export SAS=xxxx
export IOTHUB_NAME=xxxx
export DEVICE_NAME=xxxx
sh publish.sh
```

参考 publish.sh

```
curl -i -X POST \
-H "Content-Type:application/json" \
-H "Authorization:$SAS" \
-d '{"value":"hello,world from iothub"}' \
"https://$IOTHUB_NAME.azure-devices.net/devices/$DEVICE_NAME/messages/events?api-version=2018-06-30"
```

6.確認
メッセージを送信後、FunctionAppのモニターログに以下が表示されたら成功です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/366323/a2454b9b-b534-a8a2-b82a-d74bf7971b31.png)

# 解説

今回のアプリケーションを構築する上で中心的役割を果たすリソースのみ解説していきます

## IotHub

```bash
resource "azurerm_iothub" "my-iot-hub" {
  name = "terra-iot-hub"
  resource_group_name = azurerm_resource_group.rg.name
  location = var.resource_group_location
  sku {
    capacity = 1
    name     = "F1"
  }
}
```

単にメッセージを受け取るだけであれば上記の定義だけで大丈夫です！skuはF1(無料)を選べるのは、自分のサブスクリプションに他にF1のiothubがいない時だけなので注意してください。

またIoTHubを使ったことのある方はお気づきかもしれませんが、terraformではIoTHub内のデバイスを定義する項目がありませんでした。

そのため、”構築”セクションでazコマンドにより作成しています。

[Terraform Registry](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/iothub)

## Function App周り

以下の4つを定義します

- AzureStorageAccount
Functionのトリガーの管理やログを保存するのに必要
参考:
    
    [Storage considerations for Azure Functions](https://docs.microsoft.com/en-us/azure/azure-functions/storage-considerations#storage-account-requirements)
    
- AzureServicePlan
課金プラン
- AzureFunctionApp
Function本体
- ApplicationInsights
アプリケーションログを見るためのリソース

上３つのterraform

```bash

# randomなidを生成するためのリソース
resource "random_id" "randomId" {
  keepers = {
    # Generate a new ID only when a new resource group is defined
    resource_group = azurerm_resource_group.rg.name
  }

  byte_length = 8
}

# AzureStorageAccount
resource "azurerm_storage_account" "mystorageaccount" {
  name                        = "st${random_id.randomId.hex}"
  resource_group_name         = azurerm_resource_group.rg.name
  location                     = var.resource_group_location
  account_tier                = "Standard"
  account_replication_type    = "LRS"

  tags = {
    environment = "Terraform Demo"
  }
}

resource "azurerm_service_plan" "myAppservicePlan" {
  name                = "my-app-service-plan-${random_id.randomId.hex}"
  resource_group_name = azurerm_resource_group.rg.name
  location            = azurerm_resource_group.rg.location
  os_type             = "Linux"
  sku_name            = "S1"
}

resource "azurerm_application_insights" "myAppInsight" {
  name                = "tf-test-appinsights"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  application_type    = "Node.JS"
}
```

Azure Function

```bash
resource "azurerm_function_app" "myFunction" {
  name                       = "my-iothub-triggered-function"
  location                   = azurerm_resource_group.rg.location
  resource_group_name        = azurerm_resource_group.rg.name
  app_service_plan_id        = azurerm_service_plan.myAppservicePlan.id
  storage_account_name       = azurerm_storage_account.mystorageaccount.name
  storage_account_access_key = azurerm_storage_account.mystorageaccount.primary_access_key
  os_type                    = "linux"
  version                    = "~4"

  app_settings = {
    APPLICATIONINSIGHTS_CONNECTION_STRING = azurerm_application_insights.myAppInsight.connection_string
    APPINSIGHTS_INSTRUMENTATIONKEY = azurerm_application_insights.myAppInsight.instrumentation_key
    eventHubCompatibleEndpoint = "Endpoint=${azurerm_iothub.my-iot-hub.event_hub_events_endpoint};SharedAccessKeyName=${azurerm_iothub.my-iot-hub.shared_access_policy[0].key_name};SharedAccessKey=${azurerm_iothub.my-iot-hub.shared_access_policy[0].primary_key};EntityPath=${azurerm_iothub.my-iot-hub.event_hub_events_path}"
    FUNCTIONS_WORKER_RUNTIME ="node" 
    WEBSITE_RUN_FROM_PACKAGE =1
  }

  site_config {
    linux_fx_version = "node|14"
  }
}
```

実は`azurerm_function_app`は型落ちしたリソースで、最新のリソースは`azurerm_linux_function_app`です.

なぜわざわざこのようなことをしているかというと、`azurerm_linux_function_app`でリソースを作成した際に,azure functionのコードのデプロイがうまくいかないという問題が発生したためです。自分の何らかの設定ミスなのかバグなのか定かではないですが、現状うまくいくことが確認できた`azurerm_function_app`で構成しています.もし原因ご存知の方がいたら共有いただきたいです!

- `azurerm_function_app`

[https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/function_app#attributes-reference](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/function_app#attributes-reference)

- `azurerm_linux_function_app`

[https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/linux_function_app](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/linux_function_app)

忘れてはいけないポイントは、app_settingsブロックで`eventHubCompatibleEndpoint`をきちんと定義することです。この値はIoTHubとの連携を担う値で、Functionコードから参照されます。Functionコードのfunction.jsonと同じ名前になるようにしてください。


# おわりに

いかがでしたでしょうか。UI操作より圧倒的なスピードで動作確認できたのではないでしょうか。

次はAzure専用のbicepも試してみたいと思います。