SORACOM GPSマルチユニットのデータをTerraformでIoT RuleとDynamoDB作って受け取ってみた

SORACOM GPSマルチユニットとTerraformの肩慣らしに書いてみました。
2020.12.03

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

CX事業本部の佐藤智樹です。

今回は案件で初めてTerraformを使うことになったので軽く肩慣らしとして、IoTデバイスからのデータ受信する構成を作ってみました。

せっかくなので、最近買った「GPS マルチユニット SORACOM edition」からデータを飛ばして試してみます。

全体的には以下のような構成です。

現在はSORACOM HarvestやSORACOM Lagoonでデバイスデータの可視化は非常に簡単にできるので、すぐにデータを可視化したい場合はそちらの使用をおすすめします。

Terraformの設定

基本的に以下の書籍でCLIなどはセットアップしました。Terraformのバージョン管理には、tfenvを使っています。

項目名 バージョン
tfenv 2.0.0
Terraform 0.12.5

コードの内容

Terraformのコードは以下になります。基本的にDynamoDBとIoT Rule、実行に必要なロールとポリシーをまとめて作成しています。本番で使う際はIoT Core関連やDynamoDB関連のリソースなどの単位で別ファイルに分けた方が良さそうです。

main.tf

provider "aws" {
  region = "ap-northeast-1"
}

resource aws_dynamodb_table iot_sample {
  name         = "Terraform_Iot_Sample"
  billing_mode = "PROVISIONED"
  write_capacity = 1
  read_capacity = 1
  hash_key     = "id"

  attribute {
    name = "id"
    type = "S"
  }

}

resource "aws_iot_topic_rule" "iot_rule_for_funnel" {
  name        = "iot_rule_for_funnel"
  description = "iot rule data from funnel"
  enabled     = true
  sql         = "SELECT newuuid() as id, * FROM 'tmk/funnel/'"
  sql_version = "2016-03-23"

  dynamodbv2 {
    put_item {
      table_name = "${aws_dynamodb_table.iot_sample.name}"
    }
    role_arn = "${aws_iam_role.role.arn}"
  }
}

resource "aws_iam_role" "role" {
  name = "IotRuleToDynamoDBAccessRole"

  assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "iot.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF
}

resource "aws_iam_role_policy" "iam_policy_for_iot" {
  name = "IotRuleToDynamoDBPutPolicy"
  role = aws_iam_role.role.id

  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
        "Effect": "Allow",
        "Action": [
            "dynamodb:PutItem"
        ],
        "Resource": "${aws_dynamodb_table.iot_sample.arn}"
    }
  ]
}
EOF
}

今回は時系列データの本格的な処理は考えずに、受信したイベントに対して毎回uuidを設定します。uuidをDynamoDBのPartition Keyとして、受信した全てのデータをそのままDynamoDBに保存します。

本格的に時系列データを扱う場合は、Amazon TimestreamやDynamoDB で時系列データを処理するベストプラクティスなどをご覧ください。

assume-roleコマンドなどでクレデンシャルを取得後、以下のコマンドで、実行可能か確認後にデプロイします。

% assume-role <profile name>

% terraform init
% terraform plan
% terraform apply

SORACOM Funnel実行用のユーザ作成

SORACOM FunnelではAWSのAccess KeyとSecret Keyが必要になるので、少ない権限を持たせた新しいユーザを作成します。

こちらに関しては何度も作成するものでは無いので手動で作成しました。同じように行う場合は以下の設定でユーザ作成、ポリシーの設定を行ってください。ユーザを作成するとAccess KeyとSecret Keyがダウンロードできるので控えておいてください。

項目名 内容
IAMユーザ 任意の名前で作成
IAMポリシー arn:aws:iam::aws:policy/service-role/AWSIoTRuleActions ※

※ 今回設定したポリシーは以下のようにKinesis、s3などのサービスへの追加を全て許可しているポリシーなので、継続的に使う場合はResourceやAction先を絞ったポリシーの新規作成を推奨します。

AWSIoTRuleActions

{
    "Version": "2012-10-17",
    "Statement": {
        "Effect": "Allow",
        "Action": [
            "dynamodb:PutItem",
            "kinesis:PutRecord",
            "iot:Publish",
            "s3:PutObject",
            "sns:Publish",
            "sqs:SendMessage*",
            "cloudwatch:SetAlarmState",
            "cloudwatch:PutMetricData",
            "es:ESHttpPut",
            "firehose:PutRecord"
        ],
        "Resource": "*"
    }
}

設定が分からない場合は、以下の公式資料のユーザ作成部分を見てください。次章の内容も含まれています。

SORACOMの設定

次はSORACOMの設定に入っていきます。今回デバイスの基本的な設定は既に済ませている前提で進めます。デバイス側の設定方法について気になる方は以下の資料をご確認ください。

(余談ですが自分は家の通信環境が悪かったせいで、登録時のデータ送受信の際ずっとランプがオレンジで失敗していると勘違いしていました…)

SORACOMコンソールで「Menu」を押してからサイドバーの「SIMグループ」を押下します。

SIMグループに対する設定画面が出ます。その中で下の方に「SORACOM Funnel」があるので押下します。

Funnelの詳細設定画面が出ます。そこで「転送先サービス」を「AWS IoT」に設定し、認証情報を「+」ボタンから追加します。

認証IDや概要は分かりやすい名前をつけて、先ほど控えたAccess Key、Secret Keyをここで設定します。

最後に「転送先URL」をAWSのコンソールから取得して埋めます。コンソールでAWS IoT Coreの画面のサイドバーで「設定」を押下すると確認できます。

上記で設定は完了です。

データ転送実施

実際にデバイスからデータを飛ばして、DynamoDBにデータが保存されるのか確認します。 今回は定期的なデバイスからの送信と手動でスイッチ押下による送信の両方を見てみます。

結果で見ると以下のようにテーブルにデータがきていることが確認できました。

{
  "payloads": {
    "M": {
      "rs": {
        "N": "0"
      },
      "temp": {
        "N": "27.2"
      },
      "bat": {
        "N": "3"
      },
      "humi": {
        "N": "34.4"
      },
      "x": {
        "N": "0"
      },
      "y": {
        "N": "0"
      },
      "lon": {
        "NULL": true
      },
      "z": {
        "N": "-960"
      },
      "type": {
        "N": "0"
      },
      "lat": {
        "NULL": true
      }
    }
  },
  "sourceProtocol": {
    "S": "udp"
  },
  "imsi": {
    "S": "449999999999999"
  },
  "operatorId": {
    "S": "OP9999999999"
  },
  "timestamp": {
    "N": "1606904270769"
  },
  "destination": {
    "M": {
      "resourceUrl": {
        "S": "https://xxxxxxxxxxxxxxxxxxx.iot.ap-northeast-1.amazonaws.com/tmk/funnel/"
      },
      "provider": {
        "S": "aws"
      },
      "service": {
        "S": "aws-iot"
      }
    }
  },
  "credentialsId": {
    "S": "{SORACOM Funnelで設定した認証ID}"
  },
  "id": {
    "S": "4d5749be-ba6f-47cb-97cc-a50039da5ccc"
  }
}

データは自体は大体問題なく保存できていそうです。

もし手順を真似して失敗している場合は、おそらくトピック名の誤り(/が多い or ついてない)や認証情報の設定ミスなどだと思うので、設定からAWS IoTのログを有効化してどこまで処理が通ってるのか確認してみてください。

終わったら以下のコマンドで不要なリソースを削除してください。IAMユーザについても継続的に使わない場合は削除してください。

% terraform destroy

所感

初めてTerraformを使っていますが、Roleなどの設定が隠蔽されていないので最初に扱う分には裏側の仕組みが知れて分かりやすいと思います。

本記事には関係ないですが、個人的にはDynamoDBのGSI変更を複数同時にやってもTerraform側でよしなにしてエラーにならないのは最高だと思います。CloudFormation経由のサービスだと毎回エラーになるので…

家でとりあえず買っておいたGPSマルチユニットも使う機会が出せてよかったです。少し使って分かりましたがSORACOMにはまだまだ多くの機能があるようなので今後も積極的に記事を上げていきたいと思います。