SORACOM FunkでAWS Lambdaを呼んでみた

# SORACOM Funkとは

2019年7月2日に発表されたSORACOMの新サービスの1つで、AWS LambdaなどのFaaSの関数を直接実行できるサービスです。

これまでFaaSにSORACOMからデータを渡す際は、API GatewayでHTTP(s)のエンドポイントを作ってそこにBeamで送り込むか、自前でHTTP(s)を喋るか、FunnelのAWS IoTアダプターを使うかという所だったと思います(FunkはAzureやGCPのFaaSも呼べますが今回は割愛)。
これが何と「直接」呼べます。これによって何が嬉しいかというと

- API GatewayやAWS IoTの準備が不用
- 使う物が少ないと言うことは、運用の面倒くささも減る
- REST API叩くためだけにインターネットにも出ないのでより安全
- API Gatewayでの独自の認証不要。データの改ざんを気にしてBeamのヘッダ検証などもしなくていいし、認証情報をデバイスに含めないでいい

ということで、相当楽ちんになります。

[SORACOMのドキュメント](https://dev.soracom.io/jp/start/funk_aws_lambda/)を参照しつつ、実際にやってみました。

# 呼び出すLambda関数を準備する

まず、Lambda関数を準備します。とにかく動いてることが確認できれば良いので、Serverless Frameworkのスケルトンをそのまま利用します(pythonで記載してます)。

```python
def hello(event, context):
    body = {
        "message": "Go Serverless v1.0! Your function executed successfully!",
        "input": event
    }
    response = {
        "statusCode": 200,
        "body": json.dumps(body)
    }

    return response
}
```

入力をダンプしつつメッセージを添えて返すだけです。こいつをAWS Lambdaに登録し、そのARNを取得しておきます。

# 認証情報を作成する

IAMユーザを作成します。SORACOMのドキュメントにもあるように、アタッチする権限は以下のようにします。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowAuroraToExampleFunction",
            "Effect": "Allow",
            "Action": "lambda:InvokeFunction",
            "Resource": "Lambda関数のARN"
        }
    ]
}
```

作成したIAMアカウントのAccess Key IDとSecret Access Keyを保存しておきます。

# Funkの設定

SORACOMのコンソールから、SIMをSIMグループに入れ、SIMグループのFunkの設定を行います。

- サービス: AWS Lambdaを選択
- 送信データ形式: JSON
- 認証情報: 新規に作成し、先ほど作成したIAMアカウントのAccess Key IDとSecret Access Keyを入れる
- 関数のARN: 呼び出すLambda関数のARNを入れる
- スライダーを「ON」にして「保存」を押す

これだけです。超簡単ですね！

# 呼んでみよう

それでは早速呼び出してみましょう。手軽にやるために、私はSORACOM AirのSIMを挿したWindows端末から実行しました。
curlコマンドでFunkのエンドポイントにデータを送ってみましょう。

```bash
%curl -X POST http://funk.soracom.io/ -d '{"hoge":40}'
{"body": "{\"input\": {\"hoge\": 40}, \"message\": \"Go Serverless v1.0! Your function executed successfully!\"}", "statusCode": 200}
```

はい、関数の戻り値がそのまま出てきましたね。渡したデータはそのままeventに入っていることが分かります。

せっかくですので、Harvestも有効にした上で、unified endpointに送ってみましょう。

```bash
%curl -X POST http://uni.soracom.io/ -d '{"hoge":10}'
{"result":"ok","detail":{"SoracomFunk":{"statusCode":200,"body":"eyJib2R5IjogIntcImlucHV0XCI6IHtcImhvZ2VcIjogMTB9LCBcIm1lc3NhZ2VcIjogXCJHbyBTZXJ2ZXJsZXNzIHYxLjAhIFlvdXIgZnVuY3Rpb24gZXhlY3V0ZWQgc3VjY2Vzc2Z1bGx5IVwifSIsICJzdGF0dXNDb2RlIjogMjAwfQ==","encoding":"base64"},"SoracomHarvest":{"statusCode":201}}}
```

unifiedエンドポイントでは戻り値のJSONの「detail」の所に、ONにしたサービスごとの戻り値が入っています。
Funkの戻り値は"SoracomFunk"の所ですが、bodyの値はbase64されてますね。JSONの戻り値をJSONに埋めるのは面倒だしバイナリが戻ることもあり得るからこうなってるのは納得です。

[base64decode.org](https://www.base64decode.org/)辺りで元に戻すと、先ほどと同じ内容になっているのが分かります(画像が微妙・・)。

![funk-result-decode-b64.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59415/124f4b3b-1664-6a49-60f3-8ae953575179.png)

# IMSI辺りの情報を取得してみよう

SORACOM特有のIMSIといった情報も取得してみましょう。
この辺は、context.client_context.customに入っていますので、先ほどのコードを以下のように修正してみます。

```python
def hello(event, context):
    body = {
        "message": "Go Serverless v1.0! Your function executed successfully!",
        "input": event,
        "context": context.client_context.custom
    }
    response = {
        "statusCode": 200,
        "body": json.dumps(body)
    }

    return response
}
```

では、実行してみましょう。

```bash
curl -X POST http://funk.soracom.io/ -d '{"hoge":40}'
{"body": "{\"input\": {\"hoge\": 40}, \"message\": \"Go Serverless v1.0! Your function executed successfully!\", \"context\": {\"operatorId\": \"OP********\", \"resourceType\": \"Subscriber\", \"resourceId\": \"*******\", \"srn\": \"srn:soracom:OP******:jp:Subscriber:*******\", \"coverage\": \"jp\", \"imei\": \"********\", \"sourceProtocol\": \"http\", \"imsi\": \"*******\"}}", "statusCode": 200}
```

最後のjson.dumps()でちょっと見にくくなってますね(すいません、これ今回実は不要でした・・)。contextのところだけ取り出して成形してみますとこうなります。

```json
{
  "operatorId": "OP********",
  "resourceType": "Subscriber",
  "resourceId": "*******",
  "srn": "srn:soracom:OP******:jp:Subscriber:*******",
  "coverage": "jp",
  "imei": "********",
  "sourceProtocol": "http",
  "imsi": "*******"
}
```

バッチリ取れてますね！

# まとめ

IoTのデータはいつどれだけ来るか分からない(しかも大量に来てこそのIoT)ので、所謂サーバレスとの相性は高いと言われます。
データの処理にFaaSを使う場面は多いと思いますが、それが直接呼び出せるようになることでよりメンテナンス性やセキュリティも高まるFunkは最高のサービスだと思います。