はじめに
AWS IoT と、Timestream の学習&アウトプットになります。
簡単に作り直せるように AWS CLI で構築しています。
2021年4月現在 Timestream は東京リージョンにローンチされていないので Lambda を利用してデータ転送しています。
構成
利用環境
- AWS CLI: aws-cli/2.1.18 Python/3.7.9 Windows/10 exe/AMD64 prompt/off
- AWS SAM CLI: version 1.22.0
- AWS IoT Core
- AWS Timestream
- Owntracks (Android): Version 2.3.0 (23024)
- Windows 10 Pro: 20H2
- Git Bash for Windows (AWS CLI実行ターミナル): 2.28.0.windows.1
- Windows PowerShell: 5.1.19041.868
構築フロー
-
- AWS環境変数セット
-
- AWS IoT Core 構築
- 2-1. AWS IoT core > ACT > ルール 作成
- S3バケット作成
- Timestream 構築
- Lambda ビルド&デプロイ
- ルールのロール作成
- ルール作成
- 2-2. AWS IoT core > 安全性 > 証明書 作成
- 証明書、秘密鍵、公開鍵の作成
- PKCS #12 個人情報交換ファイル(p12)作成
- 2-3. AWS IoT core > 安全性 > ポリシー 作成
- 2-4. AWS IoT core > 管理 > モノ 作成
- 2-5. IOT Core の ATS エンドポイント(ホスト)取得
-
- Owntracks 設定
-
- AWS Timestream 構築
- 4-1. Lambda ビルド&デプロイ
- 4-2. AWS IoT core > ACT > ルール 追加
1. AWS環境変数、作業変数の設定
ここでは kikudai プロファイル、リージョンを環境変数にセットして利用しますが、defaultのプロファイルを利用する場合は設定不要です。
export AWS_PROFILE=kikudai
export AWS_DEFAULT_REGION=ap-northeast-1
bucket_name="qiita-f52c0841775f9fc4d0c5-owntracks"
common_name="qiita_f52c0841775f9fc4d0c5_owntracks"
mkdir ~/${common_name}
cd ~/${common_name}
2. AWS IoT core の構築
2-1. AWS IoT core > ACT > ルール 作成
Owntracks テレメトリデータ(MQTT Publish データ)を S3 に連携するルールとロールを作成します。
この S3 連携の主な目的はテレメトリデータのバックアップ、デバック、リストアになります。
後述の Lambda による Timestream 連携ルールの設定の方で、Timestream にデータ(緯度・経度など)をストアします。
S3 バケット作成
Owntracks テレメトリデータを保存する S3 バケットを作成します。
aws s3 mb "s3://${bucket_name}"
Timestream 構築
Owntracks のテレメトリデータ(MQTT Publish メッセージ)を AWS Lambda アクションで Timestream に連携します。
Lambda サンプルCode(Python)
Lambda ビルド&デプロイ
SAM cli で Lambda をビルド、デプロイします。
Windows の場合、
pip install -U aws-sam-cli
で sam cli をインストールするのが一番手っ取り早いと思います。
git clone https://github.com/kikudai/qiita_f52c0841775f9fc4d0c5_owntracks.git
cd qiita_f52c0841775f9fc4d0c5_owntracks
sam build
# 対話形式のデプロイ
sam deploy --guided | tee deploy.log
sam ddeploy --guided を実行すると下記のような対話形式となるので [XXXX]: はエンター、 Y/n は一律 y を入力します。
Configuring SAM deploy
======================
Looking for config file [samconfig.toml] : Found
Reading default arguments : Success
Setting default arguments for 'sam deploy'
=========================================
Stack Name [to-timestream]: エンター
AWS Region [ap-northeast-1]: エンター
#Shows you resources changes to be deployed and require a 'Y' to initiate deploy
Confirm changes before deploy [Y/n]: yエンター
#SAM needs permission to be able to create roles to connect to the resources in your template
Allow SAM CLI IAM role creation [Y/n]: yエンター
Save arguments to configuration file [Y/n]: yエンター
SAM configuration file [samconfig.toml]: エンター
SAM configuration environment [default]: エンター
Looking for resources needed for deployment: Not found.
Creating the required resources...
Successfully created!
...
Previewing CloudFormation changeset before deployment
======================================================
Deploy this changeset? [y/N]:
yエンター
...
ローカル docker で動作確認したい場合は以下のコマンド
sam build --use-container
sam local invoke OwntracksFunction --event /events/event_is_u.json
ただ、このテスト event.json が tst (Timestream に書き込むときの Time)のままだと、Timestream のメモリストアの期限に(今回は1時間としてます)ヒットして、 RejectedRecordsException が発生するはずなので、 sed などで今のUNIX時間に書き換えてご利用ください。
# 本当はこういう感じでファイルを書き換えることなく、 sed を利用したいところです
sam local invoke OwntracksFunction \
--event <(sed -e "/tst/ s/[0-9]+/$(date +'%s')/g" ./events/event_is_u.json)
>
# ただし、Git Bash for Windows では上記のような bash のプロセス置換がうまくいかず
# 普通にファイルを書き換えました
sed -ie --regexp-extended "/tst/ s/[0-9]+/$(date +'%s')/g" ./events/event.json
参考
ルールのロール作成
AWS IoT core > ACT > ルール のロールを作成します。
また、 AWS 管理ポリシーで用意されている次の Iot 関連ポリシー( IAM ポリシー - AWS IoT Core )をアタッチします。
- AWSIoTThingsRegistration
- AWSIoTLogging
- AWSIoTRuleActions
Git Bash for Windows による aws cli 実行で
file://
は、カレントのファイルでないと読み込めないようです。
例えば、こういったことができません。echo 'hoge' > /tmp/hoge.json
aws iot hoge --hogehoge file:///tmp/hoge.json
(Windows のリアルパスを指定すると可能かもしれません)
cat <<'EOF' > ${common_name}_iot_role.json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "iot.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
aws iam create-role \
--role-name ${common_name}_iot_role \
--assume-role-policy-document file://${common_name}_iot_role.json
aws iam attach-role-policy \
--role-name ${common_name}_iot_role \
--policy-arn "arn:aws:iam::aws:policy/service-role/AWSIoTThingsRegistration"
aws iam attach-role-policy \
--role-name ${common_name}_iot_role \
--policy-arn "arn:aws:iam::aws:policy/service-role/AWSIoTLogging"
aws iam attach-role-policy \
--role-name ${common_name}_iot_role \
--policy-arn "arn:aws:iam::aws:policy/service-role/AWSIoTRuleActions"
ルール作成
AWS IoT core > ACT > ルール を作成します。
--topic-rule-payload の sql の FROM 句 'owntracks/#'` は Owntracksアプリで設定されるTopicを指定しています。
この指定で、Owntracks のすべてテレメトリを取得するようにします。
Lambda のトリガーを aws cli で追加するコマンドがなかなか探し出せず時間がかかりました。
トリガー追加の cli コマンドはlambda add-permission
になります。
コンソール画面だと、ルールのアクション追加でさりげなく Lambda のトリガーも追加されますが、 aws cli の場合は、iot create-topic-rule
で作成しただけでは lambda トリガーの設定がされません。
また、 lambda トリガーの状態確認は以下になります。
aws lambda get-policy --function-name ${lambda}) | jq -r .Policy | jq .
このことから lambda トリガーはポリシーとして設定されているようですね。
arn_iot_role=$(
aws iam get-role \
--role-name ${common_name}_iot_role \
| jq .Role.Arn
)
lambda_arn=$(aws lambda list-functions \
| jq -rc .Functions[].FunctionArn \
| grep to-timestream-OwntracksFunction
)
cat <<EOF > ${common_name}_iot_s3_and_timestream_rule.json
{
"sql": "SELECT clientid() AS clientid, timestamp() AS event_t, * FROM 'owntracks/#'",
"ruleDisabled": false,
"awsIotSqlVersion": "2016-03-23",
"actions": [
{
"lambda": {
"functionArn": "${lambda_arn}"
}
},
{
"s3": {
"roleArn": ${arn_iot_role},
"bucketName": "${bucket_name}",
"key": "\${clientId()}/\${timestamp()}.json"
}
}
]
}
EOF
aws iot create-topic-rule \
--rule-name ${common_name}_topic_rule \
--topic-rule-payload file://${common_name}_iot_s3_and_timestream_rule.json
lambda=$(aws lambda list-functions \
| jq -rc .Functions[].FunctionName \
| grep to-timestream-OwntracksFunction
)
source_arn=$(aws iot get-topic-rule \
--rule-name ${common_name}_topic_rule \
| jq -r .ruleArn)
# 素直に上記 source_arn で利用した aws iot get-topic-rule の結果から
# Account 部分を切り取ったほうがいいケースがありそう
source_account=$(aws sts get-caller-identity | jq -r .Account)
aws lambda add-permission \
--function-name ${lambda} \
--principal iot.amazonaws.com \
--source-arn "${source_arn}" \
--action "lambda:InvokeFunction" \
--statement-id ${common_name}_topic_rule \
--source-account ${source_account}
参考情報
- JSON - OwnTracks Booklet
- FROM 句 - AWS IoT Core
- 【小ネタ】AWS CLIでAWS Account IDが取れるようになりました! | DevelopersIO
- CodeCommitのLambdaトリガーを作るときはパーミッションをお忘れなく - Qiita
- Lambda - AWS IoT Core
2-2. AWS IoT core > 安全性 > 証明書 作成
証明書、秘密鍵、公開鍵 作成
aws iot create-keys-and-certificate \
--certificate-pem-outfile "${common_name}.cert.pem" \
--public-key-outfile "${common_name}.public.key" \
--private-key-outfile "${common_name}.private.key" \
| tee "${common_name}_certificate.json"
証明書のアクティブ化
作成した証明書を有効化します。
aws iot update-certificate \
--certificate-id $(
cat ${common_name}_certificate.json | jq -r .certificateId
) \
--new-status ACTIVE
個人情報交換ファイル(PKCS #12)作成
Owntracks では拡張子が p12 の PKCS #12 個人情報交換ファイル必要なため、 openssl コマンド を利用し上記で取得した証明書と秘密鍵とパスワードから p12 ファイルを作成します。
Git Bash for Windows で openssl コマンドを実行すると Git Bash が固まったので、 PowerShell で実行しました。
Enter Export Password:
とパスワードを求められるので(確認パスワードも)入力します。
このパスワードは Owntracks アプリで証明書として p12 ファイルを登録するとき必要になります。
# PowerShellによる実行
$common_name = "qiita_f52c0841775f9fc4d0c5_owntracks"
cd ~/${common_name}
openssl pkcs12 `
-export `
-in "${common_name}.cert.pem" `
-inkey "${common_name}.private.key" `
-out "${common_name}.identity.p12"
参考情報
CA 証明書取得(サーバー認証用)
Owntracks アプリの証明書設定で必要な CA 証明書を取得します。
URLリンク切れの場合は サーバー認証 - AWS IoT Core や aws iot ca証明書 - Google 検索 で、探してください。
また、Windows10 や Android に既に設定されている Amazon CA証明書 があれば、それでも利用できると思います。
curl -O https://www.amazontrust.com/repository/AmazonRootCA1.pem
2-3. AWS IoT core > 安全性 > ポリシー 作成
ポリシーを作成し、そのポリシーを証明書にアタッチします。
cat <<EOF > ${common_name}_policy.json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iot:Publish",
"iot:Subscribe",
"iot:Receive",
"iot:Connect"
],
"Resource": [
"*"
]
}
]
}
EOF
aws iot create-policy \
--policy-name ${common_name}_policy \
--policy-document file://${common_name}_policy.json
aws iot attach-policy \
--policy-name ${common_name}_policy \
--target $(
cat ${common_name}_certificate.json | jq -r .certificateArn
)
2-4. AWS IoT core > 管理 > モノ 作成
モノを作成し、証明書をアタッチします。
aws iot create-thing \
--thing-name ${common_name}_android
aws iot attach-thing-principal \
--thing-name ${common_name}_android \
--principal $(
cat ${common_name}_certificate.json | jq -r .certificateArn
)
2-5. IOT Core の ATS エンドポイント(ホスト)取得
Owntracks のホストに設定するATSエンドポイントを取得します。
aws iot describe-endpoint \
--endpoint-type iot:Data-ATS
参考情報
- AWS IoT Core に Amazon Trust Sevices (ATS) の署名付き証明書が提供される新しいエンドポイントが追加され、Symantec が信頼されなくなることに伴う問題を回避
Owntracks の設定
カレントディレクトリにある、以下のファイルを Google Drive などに保存します。
AmazonRootCA1.pem
qiita_f52c0841775f9fc4d0c5_owntracks.identity.p12
Connection > Mode 設定
- MQTT に設定します
Connection > Host 設定
-
Host
に 2-5. IOT Core の ATS エンドポイント(ホスト)取得 の ホスト を設定します
endpointAddress で取得できた値の
xxxxxxxxxxxxxx-ats.iot.ap-northeast-1.amazonaws.com
を設定します。
{
"endpointAddress": "xxxxxxxxxxxxxx-ats.iot.ap-northeast-1.amazonaws.com"
}
* `Port` を `8883` に設定します
* `Client ID` に 適当な ID を設定します
(自動で設定されていればそのまま利用で問題ありません)
> IoT ルール作成で設定した sql
`"SELECT clientid() AS clientid, timestamp() AS timestamp, * FROM 'owntracks/#'"`
の `clientid()` で取得できる値になります。

#### Connection > Security 設定
* `TLS` をオンにします
* `CA certificate` に Google Drive などに保存した `AmazonRootCA1.pem` を選択します
* `Client certificate` に Google Drive などに保存した `qiita_f52c0841775f9fc4d0c5_owntracks.identity.p12` を選択します
* `Client certificate password` に p12ファイルを設定したときの パスワードを入力します

#### Owntracks MQTT 接続確認
画面右上の 丸 i マーク を押して Status を確認します。

Endpoint state が Connected になっていれば、問題なく接続されています。
Error になっている場合は 設定の何かで不足か間違いがあります。

ここまでの設定が問題なければ、 Timestream 米国東部 (バージニア北部: us-east-1) にデータが書き込まれ、 S3 のバケット qiita-f52c0841775f9fc4d0c5-owntracks に Owntracks からのテレメトリデータ(json)が格納されていきます。
### クリーニング(削除)
#### 削除のための環境変数、変数設定
```bash
export AWS_PROFILE=kikudai
export AWS_DEFAULT_REGION=ap-northeast-1
bucket_name="qiita-f52c0841775f9fc4d0c5-owntracks"
common_name="qiita_f52c0841775f9fc4d0c5_owntracks"
cd ~/${common_name}
S3バケット削除
aws s3 rb s3://${bucket_name} \
--force
IoTルールのロール削除
aws iam detach-role-policy \
--role-name ${common_name}_iot_role \
--policy-arn "arn:aws:iam::aws:policy/service-role/AWSIoTThingsRegistration"
aws iam detach-role-policy \
--role-name ${common_name}_iot_role \
--policy-arn "arn:aws:iam::aws:policy/service-role/AWSIoTLogging"
aws iam detach-role-policy \
--role-name ${common_name}_iot_role \
--policy-arn "arn:aws:iam::aws:policy/service-role/AWSIoTRuleActions"
aws iam delete-role \
--role-name ${common_name}_iot_role
IoTルールの削除
aws iot delete-topic-rule \
--rule-name ${common_name}_topic_rule
IoTポリシー削除
aws iot delete-policy \
--policy-name ${common_name}_policy
IoTモノ削除
aws iot delete-thing \
--thing-name ${common_name}_android
IoT証明書削除
aws iot update-certificate \
--certificate-id $(
cat ${common_name}_certificate.json | jq -r .certificateId
) \
--new-status INACTIVE
aws iot delete-certificate \
--certificate-id $(
cat ${common_name}_certificate.json | jq -r .certificateId
)
作業ディレクトリの削除
cd
rm -rf ~/${common_name}
CloudFormation スタック削除
SAM CLI のデプロイ時に利用したスタックとLambdaの削除
aws-sam-cli-managed-default というスタックも作成されますが、他の方が SAM CLIを利用していた場合は削除しないほうが無難です。
S3バケット、aws-sam-cli-managed-default-samclisourcebucket-XXXXXXXXも同様です。
削除しても問題ない場合は、S3バケットを空にしてから
aws cloudformation delete-stack --stack-name aws-sam-cli-managed-default
してください。
aws cloudformation delete-stack --stack-name to-timestream
Timestream 削除
aws timestream-write delete-table \
--region us-east-1 \
--database-name ${common_name} \
--table-name location
aws timestream-write delete-table \
--region us-east-1 \
--database-name ${common_name} \
--table-name lwt
aws timestream-write delete-database \
--region us-east-1 \
--database-name ${common_name}