この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
こんばんは、菅野です。 Lambda関数を作っていると、外部に知られたくない文字列を使いたい事があります。 そんな時にパラメータストアを使うと、ソースに埋め込む必要があるのは「名前」だけで「秘密にしたい値」はプログラム側で一切保持しなくて済むようになります。 今回はパラメータストアに保存してあるAPIキーとRoomIDを使って、Chatworkへメッセージを送信する汎用的なLambda関数を作成してみました。
パラメータについて
今回必要なパラメータは以下の3つとなります。
- ChatworkのURL
- ChatworkのAPIキー
- ChatworkのRoomID
この中で機密性の高いものはAPIキーとRoomIDの二つなので、これらは暗号化しておきます。 URLはソースに埋め込んでも問題無いのですが、他のLambda関数でも使えるのでパラメータ化しておきます。 そうすればURLが変わった時にパラメータだけ変更すれば全てのLambda関数の修正が完了します。
パラメータの準備
それではパラメータを作成します。 例として暗号化しておきたいAPIキーを作成してみましょう。名前を付けてタイプを「安全な文字列」にするだけです。
同様にRoomIDのパラメータも作成します。
ChatworkのURLは暗号化しなくてもいいので、タイプは「文字列」でOKです。
Lambda関数用のIAMポリシーとIAMロールの準備
今回作成するLambda関数に必要な権限はパラメータストアからのパラメータ取得です。 必要な権限は以下の二つとなります。
- ssm:GetParameters
- sts:AssumeRole
このポリシーを使う、Lambda関数用のIAMロールも作ります。
Lambda関数で使うライブラリの準備
今回のLambda関数はChatworkへhttpsでPOSTする必要があるので、requestsライブラリをzipで用意しておきます。
$ mkdir requests
$ cd requests
$ pip install requests -t .
$ zip -r requests.zip *
Lambda関数の作成
- 「一から作成」を選択
- 名前を付けます
- ランタイムはPython3.6を選択
- 先ほど作成したIAMロールを選択
- コードエントリタイプは「.ZIPファイルをアップロード」を選択
- 先ほど作成したrequests.zipをアップロード
- 右上の「保存」ボタンをクリック
「lambda_function.py」ファイルが無いと注意されます。アップロードしたZIPファイルの中に含まれていないので当然です。
本来は「lambda_function.py」とライブラリをまとめてZIPファイルにしてアップロードするのですが、今回は「lambda_function.py」をマネジメントコンソール上で作成します。
- 「File」をクリック
- 「New File」をクリック
- もう一度「File」をクリック
- 「Save As」をクリック
- ファイル名に「lambda_function.py」と入力
- 一番上のフォルダを選択
- 「Save」ボタンをクリック
これで関数を作成できます。以下のコードをコピーして「lambda_function.py」に貼り付けて保存してください。
from __future__ import print_function
import json
import boto3
import requests
# 初期設定
ssm = boto3.client( 'ssm' )
chatwork_url_param_name = 'chatwork-url'
chatwork_room_param_name = ''
chatwork_key_param_name = ''
# メイン関数
def lambda_handler( event, context ):
global chatwork_room_param_name
global chatwork_key_param_name
# 渡されたパラメータストアの名前を保存
try:
chatwork_room_param_name = event[ 'room' ]
chatwork_key_param_name = event[ 'key' ]
except KeyError as e:
return {
"error": "param_exists_error",
"param_name": str( e ).replace( "'", '' )
}
# パラメータの名前から復号化したパラメータを取得
ssm_response = ssm.get_parameters(
Names = [
chatwork_url_param_name,
chatwork_room_param_name,
chatwork_key_param_name
],
WithDecryption = True
)
# パラメータを格納する配列を準備
params = {}
# 復号化したパラメータを配列に格納
for param in ssm_response[ 'Parameters' ]:
params[ param['Name'] ] = param['Value']
# パラメータストアに存在しないパラメータ名を指定されていたら終了
if len( ssm_response[ 'InvalidParameters' ] ) > 0:
return {
"error": "param_name_error",
"param_name": ', '.join( ssm_response[ 'InvalidParameters' ] )
}
# chatwork へメッセージを送信
return post_to_chatwork( event, params )
# chatwork へメッセージを送信
def post_to_chatwork( event, params ):
# 送信する情報を準備
post_params = { "body": event[ 'message' ] }
token_header = { "X-ChatWorkToken": params[ chatwork_key_param_name ] }
# chatwork へメッセージを送信
try:
https_response = requests.post(
# 引数1:URL
params[ chatwork_url_param_name ] + '/rooms/' + params[ chatwork_room_param_name ] + '/messages',
# 引数2:POST パラメータ
data=post_params,
# 引数3:ヘッダ(key)
headers=token_header
)
# 200 以外は例外を発生
https_response.raise_for_status()
except Exception as e:
return {
"error": "requests_post_error",
"message": str( e )
}
# レスポンスを辞書型に変換
https_response_dict = json.loads( https_response.text )
# 終了
return {
"error": "",
"message_id": https_response_dict[ 'message_id' ]
}
パラメータを取得する部分の説明
今回、3つのパラメータをまとめて取得しています。
# パラメータの名前から復号化したパラメータを取得
ssm_response = ssm.get_parameters(
Names = [
chatwork_url_param_name,
chatwork_room_param_name,
chatwork_key_param_name
],
WithDecryption = True
)
- Namesというリストにパラメータ名をカンマ区切りで入れて渡しています。
- APIキーとRoomIDは暗号化されていますので、復号化されたものを取得できるように「WithDecryption = True」を指定します。
テスト実行
今回作成したLambda関数はAPIキーとRoomIDのパラメータ名、Chatworkへ送る文字列をこの関数へ渡す事で「event」変数にそれらの値が入った状態で動作します。 右上の「テストイベントの選択」からテスト実行するのに必要なパラメータを作成しましょう。
「テストイベントの設定」をクリックすると以下のダイアログが表示されます。
- 任意のイベント名を入力
- パラメータに以下の内容を入力して下にある「作成」ボタンをクリック
{
"room": "chatwork-room-test",
"key": "chatwork-key-cm",
"message": "aaa\nbbb"
}
右上で今作成したテストイベント「test」を選択して「テスト」ボタンをクリックしてみます。 Chatworkへのメッセージ送信が成功して、message_idが帰ってきました。
さいごに
今更感がありますが、パラメータストアを利用してみました。 今まではLambda関数ではなく、呼び出す方のプログラムに秘密の情報を埋めていましたが、パラメータストアを使えば呼び出し側もLambda関数側もどちらにも書かなくて済みますので安心してソースコードを管理できます。 皆さんもパラメータストアを活用して安全なプログラムを作成していきましょう!
参考ページ
以下のページを参考にしました。 ありがとうございました。 Boto 3 Docs 1.7.4 documentation Systems Manager パラメータについて - AWS Systems Manager 【AWS】Lambdaでpipしたいと思ったときにすべきこと - Qiita PythonモジュールRequestsのHTTPステータスコードについて 【Python入門】JSONをパースする方法 - Qiita