投稿日 2020-01-20

SORACOM の利用料金を定期的に Slack に通知する

みなさまこんにちは。

エンジニアの ogu です。

突然ですが、皆さんはご自身のアカウントで SORACOM に支払っている料金が月々どのくらいの金額になっているか、ご存知ですか?

私は個人で登録しているアカウントで支払っている料金をあまり気にせずにいたら、昨年末の12月の月初の支払い(11月利用分の支払い)が 3,000 円を超えていてびっくりしていろいろ見直しをしました。
Lagoon を Free プランで使っていたつもりが Maker プランになっていたのでいったん解約したり(そういえば複数のアラートを試したくて一時的に Maker プランにしてたのでした・・・すぐに戻せばいいやと思っていて忘れていました)、しばらく通信していない SIM が Active 状態のまま放置されていたので Suspend したりしました。

支払いの詳細はユーザーコンソールにログインすれば確認できますので、見たことがないという方はすぐに確認してみましょう。

SORACOM は従量課金のサービスが多いので、適切に使えば料金を抑えることができる一方で、使い方を誤ってしまうと高額な請求となってしまう可能性も無いとは言い切れません。

どんなに気をつけていても、間違いはいつでも誰の身の上にも起こり得るものです。
そこで可能であれば、何か間違いが起こって請求金額が高額になりそうなときに、そのことになるべく早く気づくことができるとよいですね。

そんなわけで、この記事のタイトルのように、定期的に利用料金を自分宛てに Push 通知するというのがその一助になるのではないかと思い、その方法を皆様にもお伝えしたいと思います。

といってもアイディアとしては単純で、AWS Lamba のような Serverless な環境から SORACOM の API を呼んで SORACOM の利用料金を取得し、それを Slack や LINE などお好きな通知先に通知する。そしてその処理を定期的に起動するように設定する。それだけです。

今回は、使い慣れた AWS Lambda を定期的に起動して、SORACOM API を呼び出して 日本とグローバルの両方の利用料金を取得し、こちらも使い慣れたツールである Slack に通知するということをやってみたいと思います。

SORACOM API を簡単に呼び出すツール – soracom-cli

このアイディアを実現するにあたって一番難しいのが、SORACOM の API を呼び出す部分かと思います。

SORACOM API はシンプルな REST API なので原理的には簡単に呼び出すことができるのですが、認証処理を行う部分で多少の手間がかかります。

そこで、soracom-cli を使うことで、より簡単に Lambda から SORACOM API を呼び出すことができないかと考えました。

soracom-cli は事前に一度 soracom configure コマンドで設定を行っておく(認証情報を覚えさせておく)と、そのような認証処理を自動的に行ってくれるようになります。

しかしここで問題が。Lambda は永続的に使えるファイルシステムがありませんので、 soracom configure を実行したとしても次回 Lambda が起動されたときには覚えさせた認証情報が残っていないので、毎回 soracom configure から実行しなければなりません。
それではせっかく soracom-cli を使うのに手間が減らない感じがします。

そこで、そのようなテンポラリな環境で実行される場合のために soracom-cli の方を改良しました!

soracom-cli バージョン 0.5.2 から、 --auth-key-id および --auth-key というオプションでユーザーの AuthKeyId と AuthKey を指定することができるようになりました。
これらのオプションを指定して実行した場合は、事前に soracom configure を実行しなくてもよくなります。

soracom-cli Layer

では、その soracom-cli を Lambda から利用するにはどうすればよいのでしょうか?

soracom-cli は Linux 用のバイナリファイルも公開されていますので、それをダウンロードしてきて自分のプログラムコードと一緒に ZIP ファイルの中に含め、それをアップロードすることで使えるようにはなるのですが、ファイルサイズが大きめなので毎回アップロードが重くなってしまいますし、アップロードできるファイルサイズの制限にもひっかかりやすくなってしまいます。

そんなお悩みを解消するのが AWS Lambda の Layer(レイヤー) 機能です。

soracom-cli をレイヤーとしてアップロードしておけば、Lambda 関数からそのレイヤーに含まれる soracom-cli を簡単に使えて、しかも soracom-cli 自体のアップロードは一回で済み、自分のプログラムコードだけをガシガシ更新していくことができるようになります。

でも、soracom-cli の新しいバージョンが出るたびに毎回レイヤーとしてアップロードしなければなりませんので、その分の手間は依然として発生してしまいます。

そんな皆様のために、この度 soracom-cli のレイヤーを SORACOM として公式に公開いたしました。

soracom-cli レイヤーの ARN を指定するだけでどの Lambda からでも soracom-cli をすぐに使うことができるようになります。

詳しい使い方の手順などは後述します。

手順

ではここからは具体的な設定手順を説明します。

大きく分けて、以下のようなステップになると思います。

  1. Slack の通知先を準備する
  2. SORACOM の SAM ユーザーを作成する
  3. Lambda の関数を作成する

ステップ 1 – Slack の通知先を準備する

1-1. 必要に応じてチャンネルを作成する

既存のチャンネルに通知する場合はこのステップは省略できます。
新しいチャンネルを作りたい場合はさくっと作ってしまいましょう。たぶんここで説明するまでもないくらい簡単にチャンネルが作れると思いますので詳細は割愛します。

1-2. Slack のアプリを作成する

ワークスペースの設定で自分で自由にアプリを作れない場合は管理者の人に相談しましょう。

アプリを作るには https://api.slack.com/apps で “Create New App” ボタンをクリックします。

アプリを作ったら Incoming Webhook を作成し、その URL を控えておきます。

ステップ 2 – SORACOM の SAM ユーザーを作成する

既存の SAM ユーザーを使う場合はこのステップ 2 はまるごとスキップできます。

2-1. SORACOM ユーザーコンソールにログイン

利用料金を監視したいアカウントにログインします。

2-2. SAM ユーザーを作成

2-2-1. 右上のアカウント名のボタンから「セキュリティ」を選択します。

2-2-2. 左側のタブで「ユーザー」が選択されている状態だと思いますので「ユーザー作成」ボタンを押します。

2-2-3. 「名前」(ユーザー名)を入力して「作成」をクリックします。

2-2-4. 作成されたユーザーをクリックします。

2-2-5. 「権限設定」タブの「直接指定」欄のコード入力エリアに以下の内容を書き込みます。

{
  "statements": [
    {
      "api": [
        "Billing:getLatestBilling"
      ],
      "effect": "allow"
    }
  ]
}

ちなみに、この権限設定は getLatestBilling という API だけが呼び出せる権限になっています。
万が一このユーザーの認証情報が漏洩してしまったとしても、被害を最小限に食い止めることができます(利用料金が知られてしまうだけで、SIM を解約されてしまったり勝手に VPG を作られてしまったりということがありません)。

2-2-6. 「保存」をクリックします。

2-2-7. 「認証設定」タブを開きます。

2-2-8. 「認証キー」欄の「認証キーを生成」ボタンを押します。

2-2-9. 「認証キー ID」と「認証キー シークレット」を控え、ダイアログを閉じます。

ステップ 3 – Lambda の関数を作成する

3-1. AWS のアカウントにログインする

Lambda 関数はどのアカウントのどのリージョンに作っても構いません。ただし ap-east-1 のみ未対応ですのでそれ以外のリージョンを選択してください。

3-2. Lambda 関数を作成する

3-2-1. AWS Lambda の画面を開き、 “関数の作成” ボタンで 新しい関数を作成します。

今回は Blueprint 等は使わず「一から作成」を選択しましょう。

3-2-2. 関数名は好きな名前を設定します。

3-2-3. ランタイムは Node.js 12.x (現時点での Node.js の最新版)を選択します。

3-2-4. アクセス権限は編集不要です。

3-2-5. “関数の作成” ボタンを押します。

3-3. Lambda 関数にレイヤーを設定する

3-3-1. Designer 領域の中央付近にある Layers をクリックします。

3-3-2. Layers 領域の “レイヤーの追加” ボタンをクリックします。

3-3-3. “レイヤーバージョン ARN を提供” を選択します。

3-3-4. “レイヤーバージョン ARN” に以下の ARN を入力します。

arn:aws:lambda:ap-northeast-1:717257875195:layer:soracom-cli-052:1

ap-northeast-1 の部分は、お使いのリージョンに合わせて変更してください。
ap-east-1 以外の全リージョンに展開済みですので任意のリージョンから Layer をお使いいただけます。

また soracom-cli-052 の部分は soracom-cli のバージョン番号を含んでいます。
soracom-cli バージョン 0.5.2 の場合は soracom-cli-052 となりますが、将来 soracom-cli のバージョンが上がった場合はここの数字も増えていきます。

3-3-5. “追加” ボタンを押します。

これでレイヤーが追加されました。

3-4. Lambda 関数のコードを入力する

3-4-1. Designer 領域の中央付近にある、関数名をクリックします。

3-4-2. 関数コード領域で index.js の中身を以下の内容で置き換えます。

/*global URL*/
const execSync = require('child_process').execSync;
const https = require('https');

const soracom = (args) => {
    return execSync(`soracom --auth-key-id ${process.env.AUTH_KEY_ID} --auth-key ${process.env.AUTH_KEY} ${args}`)
}

const slack = (message) => {
  return new Promise((resolve, reject) => {
    let urlObj;
    try {
      urlObj = new URL(process.env.SLACK_WEBHOOK_URL);
    } catch (err) {
      reject(err);
    }
    let opts = {
        method: 'POST',
        hostname: urlObj.hostname,
        port: urlObj.port,
        path: urlObj.pathname,
        headers: {'Content-Type':'application/json'},
    }

    const req = https.request(opts, (res) => {
      if (res.statusCode === 200) {
          resolve(res);
      } else {
          reject(res);
      }
    });

    req.on('error', (err) => {
        reject(err);
    });

    req.write(JSON.stringify({text: message}));
    req.end();
  });
}

exports.handler = async (event) => {
    const jpBillStr = soracom(`bills get-latest --coverage-type jp`).toString();
    const jpBillObj = JSON.parse(jpBillStr);
    await slack(`Japan coverage: ${jpBillObj.amount} JPY`);

    const gBillStr = soracom(`bills get-latest --coverage-type g`).toString();
    const gBillObj = JSON.parse(gBillStr);
    await slack(`Global coverage: ${gBillObj.amount} USD`);

    const response = {
        statusCode: 200,
        body: `Japan: ${jpBillObj.amount} JPY, Global: ${gBillObj.amount} USD`,
    };
    return response;
};

このコードの解説を少しだけしますと、5〜7行目で soracom という名前の関数を定義しています。環境変数に指定される AUTH_KEY_ID と AUTH_KEY を引数に指定して soracom コマンドを実行するものです。

9〜40行目は Slack にメッセージを送信するためのコードです。あまり本質的ではない部分ですが、コードの大部分を占めています。

42 行目から Lambda 関数の本体です。

43 行目で soracom コマンドを使って日本カバレッジの最新の利用料金の情報を取得しています。
44 行目でその情報(JSON)をパースして、45行目で Slack に通知しています。

47〜49行目はグローバルカバレッジに対して同様のことを行っています。

3-5. 環境変数を設定

環境変数領域で以下の3つのキーで環境変数を設定します。

  • AUTH_KEY_ID
  • AUTH_KEY
  • SLACK_WEBHOOK_URL

AUTH_KEY_IDAUTH_KEY に指定する値は 2-2-9 で控えておいたものを使用します。
SLACK_WEBHOOK_URL に指定する値は 1-2 で控えておいた URL を使用します。

ここまでの変更を保存するため、画面右上の “保存” ボタンを押します。

3-6. テスト

この時点で一旦テストを行ってみましょう。

3-6-1. 画面右上のほうに “テスト” というボタンがあるので押します。

3-6-2. 初回はダイアログが出てくると思いますので “イベント名” に適当な名前をつけて “作成” ボタンを押します。

今回作った関数は、このイベントの内容は使用しませんので JSON 部分はそのままでいじらなくても大丈夫です。

3-6-3. テストイベントができたらもう一度 “テスト” ボタンを押します。

しばらく待って、Slack に以下のような通知がきたでしょうか?

Slack への利用料金の通知

エラーがあると Lambda の画面に表示されますのでデバッグしましょう。

3-7. トリガーを設定

あとはこれを定期的に実行するだけです。

3-7-1. Designer 領域の左下にある “トリガーを追加” ボタンを押します。

3-7-2. “トリガーを選択” ドロップダウンリストから CloudWatch Events を選択します。

3-7-3. ルールを作成もしくは選択します。

既存のルールの中にちょうどいいものがあればそれを選択します。なければドロップダウンの中から “新規ルールの作成” を選択して新しいルールを作成します。

“ルール名” は適当なものをつけます。

“ルールタイプ” は “スケジュール式” を選びます。

“スケジュール式” の欄には、例にあるような rate(1 day) (1日1回、ただし1日のうちでいつ実行されるかは AWS が決める)や cron() 形式で指定します。

“トリガーの有効化” はチェックがついた状態にしておきます。

“追加” を押します。

これで定期実行されるようになったはずです。

まとめ

SORACOM のサービスは基本的には使った分しかお金がかかりませんので、ちょっと試してみる分には無料枠の中で収まったりしますし、もしお金がかかったとしても数円〜数百円程度のものが多いです。

私たちが開発し提供している機能をお客様にどんどん気軽に試してみていただきたいなと日頃から思っておりますが、今回ご紹介したような仕組みを用意していただくことで、お客様がより気軽に新しいことに挑戦していけるようになるといいなと思います。

さて、来る 2020年2月18日に、Technology Camp 2020 というイベントが開催されます。

https://www.technology-camp.soracom.jp/

私は A2 セッションで SORACOM API の使いこなしについてお話させていただきますので、興味のある方はぜひお越しいただければと思います。

実際にお客様が SORACOM API を使いこなされている事例を交えつつ、今回ご紹介した soracom-cli レイヤーの話だけでなく、API にまつわるディープな話をさせていただく予定です。

ogu