SORACOM BeamでバイナリデータをAPIGateway経由でS3にアップロードする

# はじめに

あるお客様からLTEモジュールからS3に画像ファイルをアップロードしたとの要望があったので、いろいろな方法を模索したのですが、AWS-CLIなんて積めないし、HTTPリクエストのヘッダーに必要なAWS認証情報を作成できないし、といった感じでどうやって実現しようかと悩んでいました。(AWS-CLIやAWS-SDKのすばらしさに感動したw)
そんなとき「SORACOM Beamを使ってAPIGateway経由で何とかならんかね？バイナリデータのサポートもされたことだし」という神のお告げが降ってきたので実装してみた

## どんな感じ？

![apigateway_post.PNG](https://qiita-image-store.s3.amazonaws.com/0/95875/83a24ef5-fd8c-cd65-1801-297acd336747.png)

## SORACOM Beamって？

下記公式ページから抜粋

```
SORACOM Beam（以下、Beam）は、IoT デバイスにかかる暗号化等の高負荷処理や接続先の設定を、クラウドにオフロードできるサービスです。
Beam を利用することによって、クラウドを介していつでも、どこからでも、簡単に IoT デバイスを管理することができます。
大量のデバイスを直接設定する必要はありません。
```
(参考：https://soracom.jp/services/beam/)

## S3バケットの作成

まず画像ファイルをアップロードするバケットを作成しましょう
詳しい手順は割愛しますが、バケットポリシーを設定しないとPutObjectできないのでこんな感じで設定しておきます

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AddPerm",
            "Effect": "Allow",
            "Principal": "*",
            "Action": [
                "s3:PutObject"
            ],
            "Resource": "arn:aws:s3:::[Bucket Name]/*"
        }
    ]
}
```

## APIGatewayの作成

次にAPIGatewayを作成します

1. APIGatewayのコンソール画面を開き、「Create API」をクリックします
2. 「API name」に任意の文字列を入力し「Create API」で作成します
3. リソースを作成します。「Actions」から「Create Resource」を選択し「upload」というリソースを作成します
4. 「/upload」の子階層に「/{key}」といリソースを作成します。「/{key}」はURLパスパラメータとなり、今回はS3にPutする際のKey情報となります
5. 「/{key}」を選択した状態で「Actions」から「Create method」を選択し「POST」メソッドを作成します。画面はこのような感じになっていると思います。この時点で「/upload/{key}」に対してPOSTメソッドを受け付けるように設定されています
![apigateway_post_test.PNG](https://qiita-image-store.s3.amazonaws.com/0/95875/3dcfab56-b887-6f30-b214-7e1d68db7688.png)
6. 「Integration type：HTTP」「HTTP method：PUT」「Endpoint URL:S3のエンドポイント(ex：https://s3-ap-northeast-1.amazonaws.com/[Bucket Name]/{key})」を選択・入力し「save」をクリックします。そうするとPOSTメソッドが作成されます
![apigateway_post_method.PNG](https://qiita-image-store.s3.amazonaws.com/0/95875/15cbc57a-ea19-b968-e658-c2bbdc414a99.png)
「Method Request」を選択して「API Key Required」を「ture」に変更します
7. 「Actions」から「Deploy API」を選択し、APIをデプロイします。初めての場合は[New Stage]を選択して新しいステージを作成してください。「Invoke URL」を控えておいてください
8. バイナリデータを受け取る設定をします。「Binary Support」を選択し、「Edit」をクリックして「application/octet-stream」と「image/png」を追加します
9. 次にSORACOM BeamからAPIGatewayを利用するにあたってAPIキーが必要になるので、APIキーを作成してAPIGatewayと紐付けます。
10. 「API Keys」を選択し、「Actions」から「Create API Key」をクリック。「Name」に任意の文字列を入力し、「Save」をクリックします。これでAPIキーが作成されました。「API Key」の文字列をどこかに控えておいてください
11. 「Usage Plans」を選択し、「Create」をクリック。各必須項目に任意の値を入力して「next」をクリック
12. 「Add API Stage」をクリックして、先ほどデプロイしたAPIGatewayを選択し「Next」をクリック
13. 「Add API Key to Usage Plan」を選択し、先ほど作成したAPIキーを入力し「Done」をクリック
14. これで、APIGatewayとAPIキーが結びつきました

## SORACOM Beamの設定

最後にSORACOM Beamの設定をします
1. SORACOMのコンソール画面を開きグループを作成します
2. 作成したグループのBeam設定を開き、プラスのアイコンから「HTTPエントリポイント」を選択します
3. 下記内容を入力・設定していきます

```
設定名：任意の文字列
エントリポイント
  パス：/
転送先
  ホスト名：APIGatewayのInvoke URL
  パス：/upload/test.png
ヘッダ操作
  IMSIヘッダ付与：ON
  署名ヘッダ付与：ON
  事前共有鍵：作成しておいたものを選択。新たに作成する場合は後ほど記述します
カスタムヘッダ：「アクション：置換」「ヘッダ名：X-API-KEY」「値：APIキーの文字列」
```
※事前共有鍵の作成
「認証情報ID」と「事前共有鍵」に任意の文字列を入力し「登録」をクリックしてください
![soracom_key.PNG](https://qiita-image-store.s3.amazonaws.com/0/95875/1091cda3-b0d3-cfb2-4231-70540f6b4f43.png)

これで「http://beam.soracom.io:8888/ 」に対してリクエストを送信すると、APIGatewayにPOSTメソッドが送信されます

## 送信テスト

SIMのグループをBeamが設定されているグループに変更し、デバイスにSSHでログインします
テスト送信用のpngファイル(下記コマンドでは「test.png」)があるディレクトリに移動し、下記コマンドを入力しS3に画像がアップロードされているかを確認してください

```
curl -X POST --data-binary "@test.png" -H  "Content-Type: application/octet-stream" http://beam.soracom.io:8888/
```

成功していれば、S3の対象バケット内に「test.png」がアップロードされていると思います

## まとめ

最近よくIoT案件に関わらせていただいているのですが、そもそもセキュアな通信を前提としていないデバイスをクラウドなどと接続したいという要望が増えてきているように感じてきました
SORACOMさんのようなSIMに認証情報を持たせてセキュアな通信を担保してくれるようなサービスを最大限に活用してそういった要望にこたえていきたいと思う今日この頃でした
ただクラウド側での処理が複雑になればなるほどどこかで破綻するんじゃないかなーとも感じていたりいなかったり

あと、SORACOMBeamでAPIGatewayのパスを変数的に持たせることってできないんだろうか
APIGateway側でURLパスパラメータを使ってS3へアップロードするキー名を決めているので、今回のように固定にしてしまうと、アップロードされる画像がずっと上書きされてしまうので、あまりよろしくない･･･
S3のイベントで画像がアップロードされたタイミングでLambdaキックしてリネームしてアップロードしなおすという手もあるけど･･･
Beamの仕様を漁るか･･･
もし、何かよい方法があればコメント等で教えてください！

ではまた！