開発と本番のアカウント間のSSMパラメータストアを比較する簡易チェックスクリプトを書いてみた

2アカウントのパラメータストアのキーが同一になっているかを確認する簡易チェックスクリプトを作成しました。
2020.07.22

こんにちは、平野です。
自作の分割キーボードがやっと手に馴染んできました(遅)。

さて、最近の開発業務で、本番環境と開発環境でAWSアカウントを分けた運用をしているのですが、 SSMパラメータストアに格納するような機微な情報は開発リポジトリの中に値を入れたくないという状況で、 本番にリリースする際に新しいパラメータを追加するのを忘れていてテストジョブが失敗してしまいました。

ということで、2アカウント間のパラメータストアを比較するようなPythonスクリプトを作成してみました。 比較すると言っても、Keyとして同一のものが存在するかをチェックするだけの簡易なものですがご紹介したいと思います。

使い方

きちんと管理するものでもないので、ちょっと下にあるソースをそのままPythonスクリプトとして保存してご利用ください。 事前にAWS CLIがインストールされている必要があります。

具体的な使い方は、コマンドラインで

python diff_params.py profile_dev profile_prd -p /HOGEHOGE/ -d 3

のように実行します。

引数として2つのAWS CLIプロファイルを指定します。

-pオプションは--prefixの短縮形で、対象とするパラメータのキーのプレフィックスを指定します。 省略時には全てのキーが対象となります。

-dオプションは--compare_depthの短縮形で、同一かどうかの比較を、/で区切られた何階層目以降を使うかを指定します。 言葉でいうとわかりづらいですが、例えば/HOGEHOGE/dev/hoge/fugaのように 開発環境か本番環境かなどがわかるようなものがキー名に入っている場合、 hogeの箇所より深い文字列だけで比較できる必要があったのでつけた機能になります。

開発環境と本番環境と、全く同じキーを使っていれば問題ないのですが、 開発環境だと思っていたら手違いで本番環境いじってました的なあるあるを防ぐ意味では、 環境での名前分けなどはやった方がいいかなと思っています。 今回はまさにそういう運用になっていたので、このオプションを作成しました。

両者に差分がない時には何も出力されず、終了コード0を返します。 差分がある場合は以下のような感じで、どちらかにだけ存在するものが一覧で表示されます。 このときは終了コードは1となります。

$ python diff_params.py profile_dev profile_prd -p /HOGEHOGE/ -d 3
profile_devだけに存在
{'aaa/bbb_1', 'aaa/ccc_1'}
profile_prdだけに存在
{'xxx/bbb_2'}

ソースコード

小さいので全文貼ります。

#!/usr/bin/env python
import sys
import json
import argparse
import subprocess
from subprocess import PIPE

p = argparse.ArgumentParser()
p.add_argument('profile_1', help='1つ目のアカウントプロファイル')
p.add_argument('profile_2', help='2つ目のアカウントプロファイル')
p.add_argument('-p', '--prefix', default='', help='比較対象パスのプレフィックス')
p.add_argument('-d',
               '--compare_depth',
               type=int,
               default=0,
               help='COMPARE_DEPTH番目より深いパスで同一比較')
args = p.parse_args()
depth = args.compare_depth


def get_names(profile, prefix):
    cmd = 'aws --profile {} ssm get-parameters-by-path --path {} --recursive'.format(
        profile, prefix)
    proc = subprocess.run(cmd, shell=True, stdout=PIPE, text=True)
    response = json.loads(proc.stdout)
    return [x['Name'] for x in response['Parameters']]


names_1 = get_names(args.profile_1, args.prefix)
names_2 = get_names(args.profile_2, args.prefix)

if depth == 0:
    compare_1 = set(names_1)
    compare_2 = set(names_2)
else:
    compare_1 = set('/'.join(n.split('/')[depth:]) for n in names_1)
    compare_2 = set('/'.join(n.split('/')[depth:]) for n in names_2)

if compare_1 == compare_2:
    pass
else:
    only_1 = compare_1 - compare_2
    if len(only_1) > 0:
        print('{}だけに存在'.format(args.profile_1))
        print(only_1)
    only_2 = compare_2 - compare_1
    if len(only_2) > 0:
        print('{}だけに存在'.format(args.profile_2))
        print(only_2)
    sys.exit(1)

工夫点

工夫というよりは苦し紛れといった方がいいかもしれませんが、 パラメータストアのキー一覧の取得をboto3ではなくサブプロセスでAWS CLIから行っています。

これは、私の環境では、基幹となる1つのIAMユーザからそれぞれ別アカウントのIAMロールへAssumeRoleをしてアクセスをしており、 またそのAssumeRoleにはMFAの入力が必要な設定になっているからです。 boto3でのアクセスだと2つのアカウントにアクセスする際にそれぞれMFAの入力が求められるのですが、 同一の一時パスワードを短時間(30秒)に複数回使用した場合、 2回目以降は弾かれてしまうという制約があります。 AWS CLIであれば1回目の認証を通った状態で、認証情報がキャッシュされるため、 2つ目のアカウントではMFAの入力が不要になり、だいぶ使い勝手が良くなりました。

まとめ

開発環境と本番環境はアカウントを分けるのがヒューマンエラーも起きにくいし、一番いい方法ではないかと思います。 そしてパラメータストアの内容は開発リポジトリには載せたくない情報を置くことがあるかと思います。 そのようなパターンで、本番環境のリリース時にパラメータストアの追加忘れなどを未然に防ぐ目的でスクリプトを作成しました。

Valueまで確認するような実装も考えたのですが、 パラメータストアにおきたいような情報はアカウントごとに異なることの方が多いと思ったので、やめました。

もしちょうどハマるようなケースがあればご使用頂ければ幸いです。