[AWS IoT]クライアントからのメッセージをS3へ保存する環境をRakeで構築する

2016.01.14

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

はじめに

AWS IoTをぼちぼちと触り始めた、t-hondaです。手始めにクライアントからのメッセージをS3に保存する設定を行ってみたのですが、その流れをソースにしておきたいと思い、Rakeで設定を書いてみました。ソースにすることで

  • 処理の流れが分かり易くなる
  • 再利用がしやすくなる

などのメリットがあるかと思います。

今回行ったこと

先にも書いたように、クライアントからのメッセージを送信し、AWS IoTで受信してS3に保存するようにしました。クライアントとしてはMQTTプロトコルで送信できるruby-mqttを使用しました。

AWS IoTの設定をRakeで書いたのですが、処理の流れは以下のようになります。

  1. Thingを作成する
  2. 証明書を作成する
  3. Policyを作成する
  4. 証明書をPolicyとThingに紐づける
  5. IAMロールを作成する
  6. Ruleを作成する


この処理の流れに沿って、Rakeを記述しました。

作成したRake

では、Rakeについてです。Rakefile本体と、Gemfileやヘルパーなども作成しました。それらについて書いていきます。尚、実行環境はRuby 2.2です。

1.Gemfile

Rakeと、AWS IoTの設定にAWS SDK for Ruby V2を使用するので、それらを記述してbundle installしました。

Gemfile
# A sample Gemfile
source "https://rubygems.org"

# gem "rails"
gem "rake"
gem "aws-sdk"

2.ヘルパー

AWS SDKのオブジェクトの作成は、証明書ファイルの記述処理などは、ヘルパーファイルに外出ししました。

rake_helper
require "aws-sdk"

def iot
  @iot ||= Aws::IoT::Client.new(region: @region)
end

def iam
  @iam ||= Aws::IAM::Client.new(region: @region)
end

def write_cert_file(file_path, value)
  File.open(file_path, "w") do |f|
    f.print value
  end
end

AWS IoT、IAMを操作するためのクライアントオブジェクトを返却するメソッドを定義しています。また証明書ファイルをローカルに出力するための「write_cert_file」も定義しています。

3.Rakefile

ではRakefileです。

Rakefile
require_relative "rake_helper"

THING_NAME = "t-honda-thing"
POLICY_NAME = "t-honda-dsl-policy"
ROLE_NAME = "t_honda_dsl_role"
ROLE_POLICY_NAME = "t-honda-dsl-role-policy"
RULE_NAME = "t_honda_dsl_rule"
S3_BACKET_NAME = "t-honda-iot"

@region = "ap-northeast-1"

task :default => [
    :create_thing,
    :create_certificate_keys,
    :create_policy,
    :attach_certificate,
    :create_role,
    :create_rule,
]

task :create_thing do
  iot.create_thing({thing_name: THING_NAME})
end

task :create_certificate_keys do
  resp = iot.create_keys_and_certificate({set_as_active: true})
  write_cert_file("./pems/thing-public-key.pem", resp.key_pair.public_key)
  write_cert_file("./pems/private-key.pem", resp.key_pair.private_key)
  write_cert_file("./pems/cert.pem", resp.certificate_pem)
  @created_certificate_arn = iot.
      describe_certificate({certificate_id: resp.certificate_id}).
      certificate_description.
      certificate_arn
end

task :create_policy do
  File.open("./policy.json", "r") do |f|
    iot.create_policy({policy_name: POLICY_NAME,
                       policy_document: f.read
                      })
  end
end

task :attach_certificate do
  iot.attach_principal_policy({policy_name: POLICY_NAME,
                               principal: @created_certificate_arn
                              })

  iot.attach_thing_principal({thing_name: THING_NAME,
                              principal: @created_certificate_arn,
                             })
end

task :create_role do
  File.open("./assume_role_policy.json", "r") do |f|
    iam.create_role({role_name: ROLE_NAME,
                     assume_role_policy_document: f.read
                    })
  end

  File.open("./role_policy.json", "r") do |f|
    iam.put_role_policy({role_name: ROLE_NAME,
                         policy_name: ROLE_POLICY_NAME,
                         policy_document: f.read,
                         })
  end

  @created_role_arn = iam.
      get_role({role_name: ROLE_NAME}).
      data.
      role.
      arn
end

task :create_rule do
  iot.create_topic_rule({rule_name: RULE_NAME,
                         topic_rule_payload: {sql: "SELECT * FROM 'topic/test'",
                                              actions: [
                                                  {
                                                      s3: {
                                                          role_arn: @created_role_arn,
                                                          bucket_name: S3_BACKET_NAME,
                                                          key: "ruby-dsl",
                                                      }
                                                  }
                                              ],
                                              rule_disabled: false
                                              }
                        })
end

必要な処理を「タスク」に分割し、各タスクを「task ・・・ do」のブロック内に記述しています。「task :create_thing do」以下のタスクは、それぞれが先に「今回行ったこと」で書いた各処理に当たります。一番上の「task :default => 」はRakeのデフォルトタスクを定義しており、デフォルトで「task :create_thing do」以降の全タスクを順次実行することを定義しています。

以下、各タスクについての簡単な説明です。

3-1.create_thing

Thingの作成を行っています。これは単純にAWS SDKの「create_thing」メソッドを呼び出しているだけです。

3-2.create_certificate_keys

「create_keys_and_certificate」メソッドを呼び出して証明書を作成しています。戻り値より作成した3種類の証明書を取得し、ローカルに出力しています。また後に使用するため、証明書のcertificate_arnを変数に保存しています。

3-3.create_policy

Policyを作成しています。Policyの設定をjsonで渡していますが、そのjsonは以下のようになります。

policy.json
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action":["iot:*"],
    "Resource": ["*"]
  }]
}

3-4.attach_certificate

証明書をPolicyとThingに紐づけています。「attach_principal_policy」「attach_thing_principal」メソッドを呼び出していますが、このとき3-2.で作成した証明書のcertificate_arnを渡しています。

3-5.create_role

AWS IoTがS3に保存するためのIAMロールを作成しています。信頼関係、ロールポリシーをjsonで渡していますが、それぞれのjsonは以下のようになります。

assume_role_policy.json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "iot.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
role_policy.json
{
  "Version": "2012-10-17",
  "Statement": {
    "Effect": "Allow",
    "Action": "s3:PutObject",
    "Resource": "arn:aws:s3:::t-honda-iot/*"
  }
}

3-6.create_rule

AWS IoTからS3に出力するためのルールを作成しています。S3のロールには、3-4.で作成したロールのarnを渡しています。

作成したAWS IoTの設定

上記のRakefileを実行すると以下の様なAWS IoTの設定が作成されます。 aws-iot-rake-capture

クライアントからのメッセージ送信

最後にクライアントからメッセージを送信してみます。先に書いたようにruby-mqttを使用し、以下の様なソースでメッセージを送信してみました。

require "mqtt"

MQTT::Client.connect(host: "XXXXXXX.iot.ap-northeast-1.amazonaws.com",
                     port: 8883,
                     ssl: true,
                     cert_file: "./pems/cert.pem",
                     key_file: "./pems/private-key.pem",
                     ca_file: "rootCA.pem") do |client|

  client.publish("topic/test", "publish test")
end

「host〜」の箇所には、AWS IoTのendpointの値を記述します。マネージメントコンソールよりThingを選択し「REST API endpoint」の値を参照してください。 aws-iot-rake-thing2

「cert_file」「key_file」はRakeにて作成した証明書ファイルです。「ca_file」は以下のコマンドでダウンロードしました。

$ wget https://www.symantec.com/content/en/us/enterprise/verisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem -O rootCA.pem

このRubyスクリプトを実行すると、S3のバケットにメッセージを出力したファイルが作成されると思います。

まとめ

AWS IoTの設定については作成するモノが多いのですが、流れを把握すれば分かり易いように感じました。また設定をソースにすることで、例えば証明書のcertificate_arnをPolicy・Thingに渡すところなどの流れが分かり易くなったと感じました。

参考サイト

AWS IoTとRuby製MQTTクライアントでPub/Subしてみた
OpenBlocks BX1とAWS IoTをつなげてAmazon S3にメッセージをなげてみる