毎日の出勤退勤時間の記録の煩わしさと しばしば付け忘れて後に「あれこの日何時に出勤(退勤)したっけ?」の苦悩から解放されるため、自分による自分のため打刻システムを構築する。
このようなものは既存のサービスだったりツールの組み合わせで出来たりするのだが、敢えて自分で作る事により楽しみながら利便性も向上させるのが本件の目的。
方式
スマホのジオフェンス機能を利用し職場エリアへの進入と退出イベントを捕捉。イベントの種類と発生時刻を記録する。
ジオフェンスと打刻API呼び出し
iPhone版IFTTTアプリを利用しLocationサービスによる指定領域への進入と退出を if にMakerサービスによるWEBリクエスト送信を then とすることで打刻APIを呼び出す。
打刻API
AWS上に構築する。
API Gatewayを利用しAPIがコールされたらSNSの打刻トピックへパブリッシュする。
API上から直接Lambdaで処理するのではなくSNSを挟むことで打刻イベントに対して複数の処理(エンドポイント)を紐付けられるようにしておく。
打刻情報の保存
打刻情報をGoogleスプレッドシートへ追記するLambdaをエンドポイントとして用意する。
本稿ではGoogleスプレッドシートへ打刻情報を書き込むLambdaを作成、SNS経由で実行できるところまで構築する。
Googleサービスアカウントの準備
AWS側からGoogleスプレッドシートへの書き込みが出来るようにするため以下を行う。
- プロジェクトの作成
- DriveとSheets APIの有効化
- サービスアカウントの発行
- G Suite側でサービスアカウントからのアクセスを認可
プロジェクトの作成
Google デベロッパー コンソール を開く。
組織ドロップダウンリスト から組織を<自分のG Suiteのドメイン>
に変更。
プロジェクトドロップダウンリスト から プロジェクトを作成
- プロジェクト名
- dakoku
作成 で実行。
APIの有効化
Googleスプレッドシートの読み書きをAPI経由で行うため対象APIを有効化する。
デベロッパーコンソールのハンバーガーメニューより API Manager を選択。
APIを有効にする から Google Drive API
と Google Sheets API
を有効にする。
サービスアカウントの作成
サービスアカウントは個々のエンドユーザではなくアプリケーションが属するアカウント。 G Suiteドメイン全体の委任を有効にすることで任意のエンドユーザに成り代わって操作することができる。
今回はAWS側からGoogleのAPIを呼び出す際にこのサービスアカウントでアクセスする。
デベロッパーコンソールのハンバーガーメニューより IAMと管理 を選択。
サービスアカウント サービスアカウントを作成 を選択してサービスアカウントを追加する。
- サービスアカウント名
dakoku-google-sheets-writer
- 新しい秘密鍵の提供
- ✓
- キーのタイプ
JSON
- G Suite ドメイン全体の委任を有効にする
- ✓
- 同意画面のサービス名
dakoku
作成 を実行。
サービスアカウントの認証情報がJSON形式でダウンロードされるので保持しておく。
G Suite側で作成したサービスアカウントを承認する
G Suiteの管理コンソールから セキュリティ もっと見る 詳細設定 認証 API クライアント アクセスを管理する
承認済み API クライアントにサービスアカウントを登録する。
- クライアント名
- 作成したサービスアカウントのクライアントID (ダウンロードしたサービスアカウント認証情報JSONのclient_idプロパティ値)を指定する。
- 1 つ以上の API の範囲
- ここにはサービスアカウントに付与するアクセス権限(OAuth 2.0のスコープ)を指定する。
Google DriveとSheetsの読み書きを許可する。https://www.googleapis.com/auth/spreadsheets
https://www.googleapis.com/auth/drive
をカンマ区切りで設定する。
承認 を実施。
SNS 打刻トピックの作成
打刻情報を送受信するためのトピックを作成する。
AWS マネージメントコンソールから SNS を開き Topics から Create new topic を実行。
- Topic name
dakoku
Lambda実行環境の準備
打刻情報をGoogleスプレッドシートへの書き込むLambda関数 dakoku-google-sheets-writer
の実行環境を整えるため以下を行う。
- ロールの作成
- S3バケットの作成
- サービスアカウントの認証情報を暗号化して管理する
ロールの作成
Lambda関数 dakoku-google-sheets-writer
を実行する為のロールを定義する。
AWS マネージメントコンソールから IAM を開き ロール から 新しいロールの作成 を実施。
- ロール名
dakoku-google-sheets-writer
次のステップ へ。
ロールタイプの選択で AWSサービスロール を指定。 AWS Lambda を 選択 する。 ポリシーのアタッチから AWSLambdaExecute を選択して 次のステップ 確認画面に ロール ARN が表示されるのでメモしておく。
S3バケットの作成
lambdaのデプロイパッケージやサービスアカウントの認証情報JSONを配置するためのバケットを作成する。
AWS マネージメントコンソールから S3 を開き バケットを作成する
- バケット名
dakoku.nijohando.jp
- リージョン
Tokyo
次へ 、プロパティ設定、アクセス許可設定はスキップし バケットを作成 を実行。
作成後、アクセス権限 タブから バケットポリシー を編集。
lambdaがバケット上のサービスアカウントの認証情報JSONにアクセスできるようにする。
{
"Version":"2012-10-17",
"Statement":[
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::xxxxxxxxxxxx:role/dakoku-google-sheets-writer"
},
"Action": [
"s3:GetObject"
],
"Resource": [
"arn:aws:s3:::dakoku.nijohando.jp/*"
]
}
]
}
サービスアカウントの認証情報を暗号化して管理する
Googleサービスアカウントの認証情報JSONをdakoku.nijohando.jp
バケットへアップロードする。
アップロードする際はSSE-KMSを有効にしてサーバサイドにて暗号化を施す。 今回は暗号化で使用するマスターキーは特に明示せずAWS管理のマスターキーを利用する。
aws s3 cp --sse aws:kms <サービスアカウントの認証情報JSON> s3://dakoku.nijohando.jp
KMSでファイルを暗号化しておくことで万が一オペミス等でバケットを公開してしまったとしても署名バージョン4を付与されたリクエスト以外は失敗するため内容の流出を防ぐことができる。
Lambdaの実装
打刻情報をGoogleスプレッドシートへの書き込むLambda関数 dakoku-google-sheets-writer を実装。
実装時のポイントは以下。
- Clojureで書く(Lambda関数ハンドラ部分のみJava)
- Amazon SNS経由で呼び出す
- REPL駆動開発を妨げないようにする
- Google API Clientライブラリは利用しない
Clojureで書く
Lambdaの対応言語のうちJava、JavaScript、C#は奇しくもClojureのホスト言語でもある。
このためClojureでLambda関数を書く際はどのホスト言語をベースにするか選択することができる。
今回Lambda(Java) + Clojureの知見を得るためにJavaを選択した。
また依存ライブラリにはホスト言語がJavaでもJavaScriptでも動くことができるcljcなライブラリが含まれる。
今回は完全に不要であり、そのままだとuberjarした際に不要な依存ライブラリを巻き込んでしまうので project.clj
に以下を定義し、
ClojureScript関連の依存を除外した。
:exclusions [[org.clojure/clojurescript]]
Amazon SNS経由で呼び出す
SNS経由で動かすためLambdaへの入力は以下の例ようなJSONとなる。
メッセージ本文はJSONではなく文字列であるため、JSONにパースし直す必要がある。
REPL駆動開発を妨げないようにする
基本的にLambdaへ依存するコードは書かず、通常のClojureの関数として処理を記述する。
またAOTコンパイルの対象となるLambda関数ハンドラはsrc/main/
配下には置かず、本番環境ビルド時にのみ対象となるsrc/prod
配下に配置する。
これはAOTコンパイルの対象とそれが依存する名前空間はtools.namespace
を利用したコードのリロードが効かなくなるため、AOTの起点となる関数ハンドラをREPL駆動開発の対象から外すことで問題を回避する。
Google API Clientライブラリは利用しない
Googleの認証、Drive、Sheets APIの呼び出しにGoogle謹製のAPI Clientライブラリしてみたが冗長で使い辛かった。
Clojureから利用する場合はライブラリでは無くRest APIを直接扱った方が簡潔に書けたのでAPI Clientライブラリは利用しない方針とした。
Lambdaデプロイパッケージの配備
Lambda関数 dakoku-google-sheets-writer
のデプロイパッケージをAWS上へ配備する。
今回はCIによる自動デプロイは行わず手動によるデプロイとする。
jarの作成
本番用ビルドプロファイル prod
を指定してデプロイパッケージを作成する。
lein with-profile prod uberjar
S3に配置
aws s3 cp target/google-sheets-writer-1.0.0-standalone.jar s3://dakoku.nijohando.jp
lambdaの登録
関数の作成
AWS マネージメントコンソールから Lambda を開き 関数の設定
設計図の選択 から ブランク関数 を選択。
トリガの設定 画面から SNS を選択。
- SNSトピック
dakoku
- トリガの有効化
- ✓
次へ
関数の設定 にて
- 名前
dakoku-google-sheets-writer
- ランタイム
Java8
- コードエントリタイプ
Amazon S3からファイルアップロード
- S3リンクのURL
https://s3-ap-northeast-1.amazonaws.com/dakoku.nijohando.jp/google-sheets-writer-1.0.0-standalone.jar
- ハンドラ
jp.nijohando.dakoku.google_sheets_writer.lambda
clojureのハイフンを含む名前空間がjavaのパッケージに変換される際にアンダースコアに変更されている点に注意- ロール
既存のロールを選択
- 既存のロール
dakoku-google-sheets-writer
- メモリ(MB)
512
- KMS キー
(デフォルト)aws/lambda
次へ 関数の作成
環境変数の設定
作成した関数が要求する環境変数を定義する。
関数を選択し、コード タブから環境変数を設定する。
- 暗号化ヘルパーを有効にする
- なし
- 環境変数: PRIVATE_KEY_JSON_URL
- サービスアカウントの認証情報JSONの場所を
s3://<バケット名>/パス
の形式で指定する。
例)s3://dakoku.nijohando.jp/dakoku-google-secret.json
- 環境変数: DELEGATED_USER_EMAIL
- Googleスプレッドシートを操作する際にサービスアカウントが成り代わるG Suiteのユーザのメールアドレスを指定する。
例)example@nijohando.jp
- 環境変数: GOOGLE_DRIVE_BASE_PATH
- 打刻情報を保存するGoogleスプレッドシートを配置するフォルダを指定する。
例)/dakoku
- 環境変数: GOOGLE_SPREADSHEET_FILE_PREFIX
- 打刻情報を保存するGoogleスプレッドシートのファイル名プレフィックスを指定する。
例)dakoku_event_
- 環境変数: ZONE
- 打刻情報の日時のタイムゾーンを指定する。
例)Asia/Tokyo
保存
テスト
AWS マネージメントコンソールから SNS を開き Topics から dakoku
を選択して Publish to topic
- Subject
test
- Message format
Raw
Messageにテスト用の打刻情報JSONを指定。
{
"subject": "nijohando",
"event" : {
"name": "office",
"modifiers": ["enter"]
}
}
MessageのJSONフォーマットは以下のように定義。
- subject
- 打刻を実施する人や物
例)自分、扉 - event.name
- 発生したイベント名
例)office(職場) - event.modifiers
- イベントの修飾子
例)enter(進入)、leave(退出)
Publish message を実行。
成功すると
- CloudWatchログにLambdaの実行ログが正常に出力
- Googleドライブ上のフォルダ
/dakoku
の下にdakoku_event_<現在の西暦>
というファイル名でGoogleスプレッドシートが作成される - スプレッドシートには月ごとのシートが作成され、以下の打刻情報が1行追加される
2017-03-09T22:16:07.857 | nijohando | office | enter
打刻システムの構築(後編)ではAPI Gateway、CloudFrontをセットアップ。実際にFTTTアプリから打刻APIを呼び出し出勤退勤時間の記録の煩わしさから本当に解放されるのかを検証する。