Alternative Architecture DOJO

オルターブースのクラウドネイティブ特化型ブログです。

SORACOM LTE-M ButtonをAzure Functionsに接続する

こんにちは、最近エレベーターのボタンを自分で押すようになった娘に「エレベーター呼んで~」とお願いしたら、ボタンを押さずに「エレベーター!」と大きな声で呼んでくれて日本語って難しい&早くエレベーターもVUI対応になって欲しいよねと思った木村です。

本記事はオルターブースAADOJOアドベントカレンダー2020の9日目の記事になります。昨日はハイパーエンジニアのみっつー、明日は独ペ(ドイツ語ペラペラ)なスーパーマンしんばくんと、超有能な若手2人に挟まれてドキドキしてますが頑張ります。

 

さて、私は以前から個人的にSORACOM User Groupに参加してソラコムさんのサービスも色々触っているので、今日はSORACOMとAzureとの関係のお話を書いてみようと思います。

SORACOMとは

SORACOMは、株式会社ソラコムの提供するIoT通信プラットフォームです。ちなみにカタカナで書いた「ソラコム」は会社の名前、アルファベットで書いた「SORACOM」はサービスの名前となります(豆知識)。

SORACOMとは一体どういうサービスで、どういうところが良いのか・・・を話し始めるとそれだけで1記事になってしまいますのでここでは省略します。

SORACOM LTE-M Buttionとは

そんなSORACOMのサービスの中で、私のお気に入りのサービスの一つが「LTE-M Button」です。

ボタンを押すだけでAmazonの製品が買える「Amazon Dashボタン」や、ボタンを押したときに自分でAWSで準備した処理をさせられる「AWS IoT エンタープライズボタン」はご存じでしょうか?これらはWifiで通信するのですが、そこをLTEで通信できるようにし、いつでもどこでも押せるようにしたものがこのLTE-M Buttonになります。

soracom.jp

LTE-M Buttonには3種類の製品があり、それぞれに特徴があります。3種類のボタンについて、以下に簡単にまとめます。

  • LTE-M Button powered by AWS
    • AWS IoT 1-clickに接続し、LambdaなどAWSの機能を呼び出せる
    • AWS以外には繋がらない
    • 通称「あのボタン」
  • LTE-M Button for Enterprise
    • SORACOM Beam/Funnel/Funk等を使って、AWS以外のサービスに接続することが出来る
    • LTE-Mの簡易位置測位機能(基地局情報を用いて、ボタンを押したおおよその場所を取得する)が使える
    • 通称「しろボタン」
  • LTE-M Button Plus
    • 機能はLTE-M Button for Enterpriseと同じ
    • 接点情報入力用の端子があり、ここをONにすることでボタンを押したのと同じ動作をするので、色々なセンサー/スイッチと連動できる
    • 通称「ひげボタン」

電池を入れてボタンを押せば動く、LTE-Mでどこでも繋がる、ひげボタンだと手軽に既存の機械や色んなセンサーとも連携でき、クラウドサービスと連携させることでアイデア次第で何でも出来るというお手軽さと無限の可能性があるガジェットです。 ちなみにこれから始めてみたいという方は、個人的にはひげボタンを買われるのをお勧めします。

今回の記事はひげボタンで検証しており、以下「ボタン」と記載したときにはひげボタンまたはしろボタンのことを指しています。あのボタンはAWSにしか繋がりませんので、今回の検証の対象外となります。

この記事は

さて、本題です(前置きが長すぎる)。「ボタンを押すとクラウドサービスに繋がる」と言っていますが、世の中の記事や事例の接続先の多くがAWSで(あのボタンはそもそもAWSにしか繋がらないですが)、Azureに繋いでみたという情報はそれほど多くありません。

私もこれまでボタンで遊ぶときはAWS Lambdaを使っていましたが、弊社はご存じのようにMicrosoft Azureをメインに使っていますので、Azureで使う場合はどうやるのか?というのを試した結果をまとめようと思います。

どうやって接続するか?

ボタンからSORACOMプラットフォームに送信されたデータをクラウドサービスに接続するには、以下のサービスのいずれかを使います。

それぞれのサービスの詳しい話を始めるとまた長くなりますので省略しますが、今回はBeamとFunkの2つのサービスでAzure Functionsに接続してみようと思います。

Functionsの準備

まずはFunctionsの関数アプリを準備します。今回はNode.jsの関数アプリをポータルから以下の内容で作成します。

  • ランタイム: Node.js 12 LTS
  • リージョン: 東日本
  • OS: Windows
  • プラン: サーバレス

関数アプリのデプロイが完了したら、ポータルから「関数」→「追加」と進み、HTTPトリガーの関数を作成します。Authorization levelは「Function」としておきます。

f:id:showm001:20201207110841p:plain
HTTPトリガーの関数を追加する

関数が作成されたら「コードとテスト」から現在デプロイされてるサンプルコードを確認・修正します。今回はひとまずどういう形で呼ばれてるかを確認するために「リクエストパラメータをログにダンプする」という処理だけにしてみましょう。

module.exports = async function (context, req) {
    context.log(JSON.stringify(req))
    
    context.res = {
        // status: 200, /* Defaults to 200 */
        body: "ok"
    };
}

保存したら関数の「概要」に進み、「関数のURLの取得」を押し、URLをメモしておきます。

f:id:showm001:20201204155847p:plain
関数のURLを取得

SORACOM Beamの設定

次に、SORACOM Beamの設定をします。SORACOMのユーザコンソールからログインし、メニューの「SIMグループ管理」に進みます。 そして新規グループを作成するか、既にボタンが属しているグループを選択します。

もし新規にグループを作成した場合は、「SORACOM Air for Cellular設定」から「バイナリーパーサ」をONにし、フォーマットに@buttonと書いて保存するのを忘れないようにします。「簡易位置測位機能」もONにしておきましょう。

f:id:showm001:20201204160733p:plain
SORACOM Air for Cellular設定

続いて、Beamの設定をします。「∨」を押してBeamの設定メニューを開き、「+」ボタンから「UDP → HTTP/HTTPSエントリポイント」を押します。 ボタンのデータはSORACOMプラットフォームまでUDPで届くので、SORACOM BeamでHTTPSに変換してAzure Functionsに送り届けるということになります。

f:id:showm001:20201204160907p:plain
SORACOM Beam設定(1)

プロトコルは「HTTPS」、ホスト名は先ほど取得した関数URLの(関数アプリ名).azurewebsites.netの部分、パスは関数URLの/api以下を?code=....の部分まで含めて入力します。IMSIヘッダとIMEIヘッダもONにしておきましょう。 また、「カスタムヘッダ」で、「Content-type」ヘッダを追加します。値はapplication/jsonです。

f:id:showm001:20201204161522p:plain
SORACOM Beam設定(2)

入力したら「保存」を押します。

ここまででSIMグループの設定は完了です。メニューの「SIM管理」に進み、ボタンのSIMの所属グループを今設定したグループに変更します。

実行してみる(1)

では、実行してみましょう。ボタンをポチッと押して、Azureのポータルから、作成した関数の「モニター」を見てみましょう。呼び出し自体はボタンを押してすぐに行われますが、Application Insightsのログの反映に時間がかかることがあるため、出てくるまで待ちます。

以下のように呼び出しが成功していれば無事動作しています。動作していない場合はBeamのURLの設定に間違いが無いか、バイナリーパーサの設定を忘れてないか辺りを確認してください。

f:id:showm001:20201204162821p:plain
モニター

ログの詳細を見てみましょう。リクエストをダンプした部分だけ抜き出して成形したもの(一部伏せています)が以下になります。

{
  "method": "POST",
  "url": "https://*******.azurewebsites.net/api/HttpTrigger1?code=c***********************",
  "originalUrl": "https://*******.azurewebsites.net/api/HttpTrigger1?code=**************************",
  "headers": {
    "connection": "Keep-Alive",
    "content-length": "84",
    "content-type": "application/json",
    "host": "**********.azurewebsites.net",
    "max-forwards": "9",
    "user-agent": "SORACOM Beam",
    "x-soracom-geo-position": "3?.***********;13?.***********",
    "x-soracom-geo-position-query-result": "success",
    "x-soracom-imsi": "***********",
    "x-soracom-imei": "************",
    "x-waws-unencoded-url": "/api/HttpTrigger1?code=***************",
    "client-ip": "*.*.*.*:*",
    "x-arr-log-id": "*******",
    "x-site-deployment-id": "******",
    "was-default-hostname": "*******.azurewebsites.net",
    "x-original-url": "/api/HttpTrigger1?code=******",
    "x-forwarded-for": "*.*.*.*:*",
    "x-arr-ssl": "2048|256|C=US, S=Washington, L=Redmond, O=Microsoft Corporation, OU=Microsoft IT, CN=Microsoft IT TLS CA 5|CN=*.azurewebsites.net",
    "x-forwarded-proto": "https",
    "x-appservice-proto": "https",
    "x-forwarded-tlsversion": "1.2",
    "disguised-host": "********.azurewebsites.net"
  },
  "query": {
    "code": "*******"
  },
  "params": {},
  "body": {
    "clickType": 1,
    "clickTypeName": "SINGLE",
    "batteryLevel": 1,
    "binaryParserEnabled": true
  },
  "rawBody": "{\"clickType\":1,\"clickTypeName\":\"SINGLE\",\"batteryLevel\":1,\"binaryParserEnabled\":true}"
}

req.body(HTTPリクエストボディ)に渡された情報は以下の通りとなります。

パラメータ名 説明
clickType クリック種別 1:シングルクリック/2: ダブルクリック/3: ロングクリック
clickTypeName クリック種別の名前 SINGLE / DOUBLE / LONG
batteryLevel バッテリーレベル 0.25 / 0.5 / 0.75 / 1

また、req.header(HTTPヘッダ)を見ると重要なデータとして以下のものが入っていることが分かります。

ヘッダ名 説明
x-soracom-geo-position 簡易位置測位機能で取得した位置情報 (緯度の値) ; (経度の値)
x-soracom-imsi SIMのIMSI情報 IMSIの値
x-soracom-imei SIMのIMEI情報 IMEIの値

先ほどBeamでIMSIとIMEIの値をヘッダでONにしたのでそれらと、Airの設定で簡易位置測位機能をONにしたので取得した位置情報(基地局の位置)がHTTPヘッダとして渡されています。 これらを使うと簡単に「どのボタンが、どの辺りで、どういうクリックをされたか」を取り出すことが出来ますね。

SORACOM Funkの設定

では、次にFunkでの連携をしてみましょう。

SORACOMコンソールから、先ほどのSIMグループ設定画面に遷移します。Beamからもデータが飛んでくると混乱しますので、一旦Beamは「DISABLED」に設定しておきましょう。

f:id:showm001:20201204165359p:plain
BeamをDISABLEDに変更

次に「SORACOM Funk設定」を開きます。機能を「ON」にし、サービスで「Azure Functions」を選択、送信データ形式は「JSON」にします。そして、「認証情報」で「認証情報を新規に作成する」を選択します。

認証情報IDには適当な名前を入力し、「APIトークン」に先ほど取得していた関数URLの?code=より後ろの値を入力します。

f:id:showm001:20201204170451p:plain
認証情報作成

そして「関数のURL」には関数URLの?code=より前を入力します。

f:id:showm001:20201207111601p:plain
Funk設定

これで設定は完了です。「保存」を押しましょう。

実行してみる(2)

先ほどと同じくボタンを押し、ログを確認します。今回のログはこんな感じです。

{
  "method": "POST",
  "url": "https://*****************.azurewebsites.net/api/HttpTrigger1",
  "originalUrl": "https://*************.azurewebsites.net/api/HttpTrigger1",
  "headers": {
    "connection": "Keep-Alive",
    "content-length": "84",
    "content-type": "application/json",
    "host": "**********.azurewebsites.net",
    "max-forwards": "9",
    "user-agent": "SORACOM Funk",
    "x-soracom-token": "*******",
    "x-functions-key": "******",
    "x-waws-unencoded-url": "/api/HttpTrigger1",
    "client-ip": "*.*.*.*:*",
    "x-arr-log-id": "****",
    "x-site-deployment-id": "******",
    "was-default-hostname": "******.azurewebsites.net",
    "x-original-url": "/api/HttpTrigger1",
    "x-forwarded-for": "*.*.*.*:*",
    "x-arr-ssl": "2048|256|C=US, S=Washington, L=Redmond, O=Microsoft Corporation, OU=Microsoft IT, CN=Microsoft IT TLS CA 5|CN=*.azurewebsites.net",
    "x-forwarded-proto": "https",
    "x-appservice-proto": "https",
    "x-forwarded-tlsversion": "1.2",
    "disguised-host": "*******.azurewebsites.net"
  },
  "query": {},
  "params": {},
  "body": {
    "clickType": 1,
    "clickTypeName": "SINGLE",
    "batteryLevel": 1,
    "binaryParserEnabled": true
  },
  "rawBody": "{\"clickType\":1,\"clickTypeName\":\"SINGLE\",\"batteryLevel\":1,\"binaryParserEnabled\":true}"
}

先ほどと同じように情報が取れていますが、先ほどBeamの時には含まれていたIMSI/IMEI/位置情報がヘッダに含まれていません。これらはどこに含まれてるのかというと、x-soracom-tokenヘッダの値に含まれてるのです。

上記では伏せていますが、実際に試してみると何か長い文字列が入っているのが分かると思います。これは何かというと、JWT(JSON Web Tokenというものです。 JWTについての説明は省略しますが、jwt.ioのデバッガーに貼り付けてみると以下のような内容だということが分かります(こちらも一部伏せています)。

{
  "iss": "https://soracom.io",
  "aud": "srn:soracom:OP*****:jp:Subscriber:******",
  "jti": "*******",
  "iat": ******,
  "typ": "soracom/token/v1",
  "sub": "funk.soracom.io",
  "ctx": {
    "operatorId": "OP******",
    "coverage": "jp",
    "resourceType": "Subscriber",
    "resourceId": "*******",
    "sourceProtocol": "udp",
    "srn": "srn:soracom:OP*****:jp:Subscriber:*****",
    "imsi": "**********",
    "imei": "***********",
    "locationQueryResult": "success",
    "location": {
      "lat": 3?.***************,
      "lon": 13?.****************
    }
  }
}

Node.jsでJWTを取り扱うにはjsonwebtoken等を使われるといいかと思いますが、これまた詳細は割愛します。

どっちを使うと良いの?

さて、BeamとFunk、2つのサービスで同じようにAzure Functionsを呼び出してみました。どちらでも同じように呼び出せることは分かりましたが、実際に使うときにはどちらを使うと良いのでしょうか?

連携先でAWS Lambdaを使う場合は、BeamだとAPI Gateway経由(HTTPSでの呼び出し)なのに対してFunkは直接Lambda関数が呼び出されるため、API Gatewayを使わない分だけ構成がシンプルになる/通信がインターネットに出ることなくAWS内で完結するのでよりセキュアになるといった利点があります。

それに対してAzureだとBeam/Funkどちらで行っても全体の構成は変わらず、インターネット経由でHTTPトリガーのFunctionを呼び出していることになるのであまり差が無いように見えます。

 

違う点としては、Funk経由だとSORACOM独自の情報がより多く取得できるというのがあります。SORACOMのオペレータIDやSubscriberといった情報はFunkでしか取得できません*1。 もしアプリケーションでそういった情報を使うと言うことであれば、Funkを使うのがいいと思います。

 

それから考えられるのは、「情報が改ざんされてないことをどうやって保証するか」というのがあります。位置情報やIMSI/IMEIといったセンシティブな情報がHTTPヘッダで渡されるBeamだと、そのままでは途中で改ざんされている可能性は否定できません。それに対してFunkではjwtで渡されますので、改ざんされていないことが保証されています。

ただし、これは厳密な話をした場合であり、Azure FunctionsまでHTTPSで接続している状態で、現実問題として改ざんされる可能性がどのくらいあるかというと個人的には気にするようなレベルではないと思いますし、Beamには改ざんを検知するための署名ヘッダという機能もあるのでそれを使えば改ざんは検知可能です*2

とはいえ、利用用途やセキュリティポリシーの関係でその辺りを気にしないといけない場合もあるとは思いますので、その場合はFunkを使われるかBeamの署名ヘッダを検討することになります。

 

そして、価格についてですが、特定地域向けSIM(要は国内キャリアの回線)の場合、Beamは1リクエスト辺り0.0009円、Funkは0.0018円です。FunkはBeamの倍・・・のように見えますが、Beamの利用料金のページには

エントリーポイント (Beam) へのリクエスト、Beamから転送先へのリクエスト、それぞれを個別に 1 リクエストとカウントします

とあります。つまり、今回のケースだとBeamにデータを送るので1リクエスト、そこからFunctionsを呼ぶので1リクエストでボタン1回押すごとに0.0018円かかります。結局同じ値段になりますね。    

こうやって考えますと、

  • Operator IDなども取得したいならFunk
  • Azure Functions側のコーディング量を少なくして位置情報やIMEI/IMSIを取得したいならBeam(ヘッダ見るだけでOKなので簡単)
  • IMSI/IMEI/簡易位置情報などが改ざんされてないことを保証する必要があるならFunk
  • リクエストボディの値しか参照しないなら、SORACOM側の設定項目が少ないFunkが設定は少しだけ楽

ということで、Azureの場合はどちらを選んでもあまり変わらないのかなという印象です。

なお、SORACOM Funnelを使ってAzure EventHubsに送り込むという方法もあります*3が、こちらについてはまたの機会に触れられればと思います。

まとめ

本ブログでは、SORACOM LTE-M ButtonをSORACOM Beam/SORACOM Funkを使ってAzure Functionsに接続してみました。SORACOMのサービスをAzureと組み合わせて使いたい!と思われる方の参考になれば幸いです。

*1:SORACOM Orbitで送信前にbodyに埋めるとか、受け取ったIMSI情報を使ってSORACOMのAPI叩くとか無理すればBeamでも出来そうですが

*2:ただし、検証のためのコードを実装したり鍵を共有したりする必要があるので、パッケージを使って簡単に検証できるjwtよりは手間がかかります。また、検証できるのはIMEIとIMSIの改ざんだけで位置情報は検証できません。

*3:SORACOM Funnel の Event Hubs アダプターを使用してデータを送信する