AWS EC2 Parameter Store とは
ご存知の方は読み飛ばしてください。
いわゆるVaultサーバみたいなKey-Value Storeです。シークレット情報やシステムパラメータを入れておいて、EC2やオンプレサーバ、ECSなどからパラメータ名をキーに参照できます。
格納できるパラメータの型は普通の文字列であるString, 文字列のリストの StringList, 暗号化された文字列のSecureStringの3種類です。
利用用途としては、RDSのパスワードや外部サービス(DockerHubやFacebookなど)の認証情報などをSecureStringで入れたり、本番環境や開発環境など環境ごとに異なるパラメータを格納したりがメインだと思われます。
ちなみに、AWS Simple System Manager Parameter Storeとも言います。そのうちAWS Systems Manager Parameter Storeになるかもしれません。
マネジメントコンソール上では今のところEC2のページに表示されるのでEC2 Parameter Storeとします。
パラメータのパス階層
Parameter Storeではパラメータ名をファイルシステムのパスのような階層構造にできます。
階層構造といっても物理的に階層があるわけではなく、パラメータ名をスラッシュで区切るとパスとして扱われ、指定パス配下のパラメータに対してまとめて取得、削除ができます。S3のオブジェクトキーの考え方と同じですね。
ただし、S3と違ってパラメータ名の先頭は必ずスラッシュにする必要があります。
また、これが一番重要ですが、 IAM認可もパス単位で指定 できます。
階層パラメータ名は、例えば/myapp/staging/RDS_PASSWORDみたいにサービス名 > 環境名 > 変数名 みたいな形式が一般的かと思われます。
必要に応じてサーバロール(web,apiなど)や、マルチテナントの場合はテナント名を階層として追加したりするでしょう。
パス配下のパラメータを取得する
前置きが長くなりましたが本題です。スクリプトの本体はGistにあります。このスクリプトの説明です。
使い方
# Usage: ./get_ssm_parameters.xx "パス階層"
bash +x ./get_ssm_parameters.sh "/myapp/staging"
ruby ./get_ssm_parameters.rb "/myapp/staging"
python ./get_ssm_parameters.sh "/myapp/staging"
node ./get_ssm_parameters.js "/myapp/staging"
出力例
RDS_PASSWORD="hogehoge"
SECPET_KEY_BASE="hogehagehige"
実行すると標準出力に変数名=変数値の形式で出力されます。SecureStringなパラメータは復号化されます。
変数名はパス階層の最後の値が使われます。勝手に大文字にしたりはしません。
例えば以下のようにすればこれから実行するプロセスに渡すことができます。
eval "$(bash +x ./get_ssm_parameters.sh "/myapp/staging" | sed -e 's/^/export /g')"
bundle exec rails start -p $SERVER_PORT
各スクリプトの説明
スクリプト置き場 => https://gist.github.com/sonodar/b3c80c8b9e60f4e6dcda9108c46a6089
AmazonLinuxであればrubyとpythonは入っているので好きなスクリプトを使えばいいと思います。
逆に、LambdaとかだとRuntimeが限定なのでそのLambdaのRuntimeに合わせたものを利用すればいいかと。
ちなみに、手元の環境だとpythonが最速でbashが一番遅かったです。(予想通り)
bash get_ssm_parameters.sh
シェルに関しては一行目がわりと重要です。
# !/usr/bin/env bash +x
シークレット情報を扱うシェルなのでオプション+xを付けています。これがないと、例えばElasticBeanstalkなんかだと-xオプション付きで実行されることが多いのでログファイルやCloudWatch Logsにシークレット値が平文で出力されます。
このスクリプトも標準出力に出すので、呼び出すシェルでも+xオプションを付けた方がいいです。
なお、jqコマンドがインストールされていることが前提となっています。
シェル変数展開で配列にしたりしているので、変数展開のされ方にはわりと気を遣っています。
なのでjqやawsコマンドのインタフェース変更があったらエラーになっちゃう。
他言語
特に説明は必要ないです。
node版はAPIのレスポンス処理がcallbackになるので再帰処理をしています。
Python版, Ruby版はyieldで逐次処理をしているくらいです。
Java版もありましたが誰も使わないだろうから消しました。