API Gatewayで受けたRESTをAWS IoTへPublishする方法

やりたいこと
-----------

HTTP RESTしかしゃべれないアプリからのデータでも、AWS IoTにPublishしたい！

概要
----

AWS IoTにPublishするのってMQTTだけじゃなくて[HTTP RESTでも出来る](http://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/protocols.html)んです

> Clients can publish by sending a POST message to `<AWS IoT Endpoint>/topics/<url_encoded_topic_name>?qos=1`

一方でAPI Gatewayは[AWS Service Proxy](http://docs.aws.amazon.com/apigateway/latest/developerguide/getting-started-aws-proxy.html)でAWS各種サービスへRESTリクエストをproxyできます

- [Amazon API Gateway で AWS Service Proxy を使って DynamoDB にアクセスする ｜ Developers.IO](http://dev.classmethod.jp/cloud/aws/api-gateway-aws-service-proxy-dynamodb/) 
- [Amazon API Gateway の AWS Service Proxy の使い方](http://qiita.com/j3tm0t0/items/594f7c0a0c62a5eccc18)

これ組み合わせれば、独自なRESTクライアントからのリクエストでもAWS IoTにデータ送れるようになります

![qiita aws api gateway aws iot.png](https://qiita-image-store.s3.amazonaws.com/0/22754/2f9d60ae-1371-6126-2442-51459ee73ca5.png)

論よりコード
-----------

1. IAM ポリシー作成
2. IAM ロール作成
3. API GatewayでAPI endpoint作成

### IAM ポリシー作成 ###

ポリシー

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1460435015000",
            "Effect": "Allow",
            "Action": [
                "iot:Publish"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}
```

*iot:publish* が入っていればOKです

### IAM ロール作成 & ポリシーアタッチ ###

```json
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Effect": "Allow",
			"Sid": "",
			"Principal": {
				"Service": "apigateway.amazonaws.com"
			},
			"Action": "sts:AssumeRole"
		}
	]
}
```

上記のように信頼されたエンティティのIDに *apigateway.amazonaws.com* が指定されていればOKです

また、このロールに先ほど作成したポリシーをアタッチしてください

### Endpoint 作成 ###

Integration Requestを下記のように作るだけです

| 項目              | 設定内容
|-------------------|-----------------------------------------------------------------------------------------|
| Integration type  | AWS Service Proxy                                                                       |
| AWS Region        | <AWS IoTのregion>                                                                       |
| AWS Service       | Iot Data                                                                                |
| AWS Subdomain     | <`aws iot describe-endpoint` で表示される XXXXX.iot.REGION.amazonaws.com の XXXXX の部分> |
| HTTP method       | POST                                                                                    |
| Action Type      | Path override にして `topics/{tp_lv1}/{tp_lv2}`                                                                |
| Execution role    | <先に作成したロールのARN>                                                                 |
| Credentials cache | Do not add caller credentials to cache key                                              |

画面上はこんな感じです

![Screenshot from 2016-04-12 14-24-16.png](https://qiita-image-store.s3.amazonaws.com/0/22754/8579a534-dc7c-9833-e65c-5f4bf77d4501.png)

### 解説とポイント ###

ポイントは *Action Type* を *Path override* にするところにあります
これにより、API GatewayからAWS内のサービスにHTTP RESTリクエストが発信されるようになります

また、最終的に呼ばれるURIは `<AWS Subdomain>.iot.<AWS Region>.amazonaws.com/<Path override>?<URL Query String Parameters>` となります

**URL Path Parameters;**

Path override 内の `{param_name}` を Mapped from で置き換えます
また Path override の中の {...} は URL Path Parameters で置き換わります

下記の例だと最終的には A33XXXXXXXXXT.iot.ap-northeast1.amazonaws.com/topics/sensor/geo?qos=1 が呼ばれることになり、ロールに割り当てられたポリシー(iot:publish)の権限を使ってPublishできるようになるというわけです


あとがき
-------

Content-Type: application/octet-stream なリクエストでも、API Gatewayの [$util](http://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html) と、マッピングを駆使すればJSON化できるので、AWS IoTを経由して好きなサービスを呼び出すこともできますね

※Amazon API Gatewayにおいて[バイナリデータの調査をしました](http://qiita.com/ma2shita/items/ea96fbb73b7095413d3f#_reference-b444915374e78745fdc9)

Lambdaをそのまま使えば？という声も聞こえてきそうですけど。