AWS Secrets Managerのシークレットをクロスアカウントで自動バックアップしてみた
こんにちは、クラウド事業本部 コンサルティング部の荒平(@eiraces)です。
AWS Secrets Managerに格納しているシークレットのバックアップをクロスアカウントで取りたいな〜と思ったことはありますか?
AWS Backupでネイティブにサポートしているのが一番ですが、執筆時点ではその機能がありません。
今回は、「別アカウントにバックアップしたいけど、仕組みを自前で作るのはちょっと面倒だな…」と思っていたところ、aws-samples にちょうど良いソリューションがあったので試してみました。ありがたや。
AWS Secrets Manager Cross Account Backup
仕組みとしては、ソースアカウントでSecrets Managerのシークレットが作成・更新されると、EventBridgeがそのAPIコールを検知し、バックアップアカウント側のLambda関数がソースアカウントのシークレットを取得して複製 するというものです。
バックアップされたシークレットは <ACCOUNT>/<REGION>/<SOURCE_NAME> という命名規則でバックアップアカウントに保存されます。
どのアカウントのどのリージョンから来たか一目で分かるのが良いですね。
検証の構成
本エントリの簡易構成図です。以下の構成をCloudFormation (via Service Catalog)で展開します。

やってみた
GitHub リポジトリのREADMEに沿って4ステップで進めます。
ステップ1. リソースの準備
バックアップアカウントにて、今回利用するリポジトリをクローンします。
なお、後述の通り参照できるようにしておけばどのアカウントでも問題ありません。
git clone https://github.com/aws-samples/aws-secrets-manager-cross-account-backup.git
cd aws-secrets-manager-cross-account-backup
次に、scripts/ と templates/ 配下のファイルをS3バケットにアップロードします。
aws s3 cp scripts/ s3://<Bucket-Name>/ --recursive
aws s3 cp templates/ s3://<Bucket-Name>/ --recursive
このS3バケットは、ソースアカウントとバックアップアカウントの両方からアクセスできるようにバケットポリシーを設定しておく必要があります。
以下のようなポリシーを与えます(s3:GetObjectとs3:ListBucketが必要)。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCrossAccountAccess",
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::<Account-ID-Dst>:root",
"arn:aws:iam::<Account-ID-Src>:root"
]
},
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::<Bucket-Name>",
"arn:aws:s3:::<Bucket-Name>/*"
]
}
]
}
また、バックアップアカウントに実行用IAMロールをデプロイします。
aws iam create-role \
--role-name StackSetAdminRole \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "cloudformation.amazonaws.com"},
"Action": "sts:AssumeRole"
}]
}'
aws iam put-role-policy \
--role-name StackSetAdminRole \
--policy-name AssumeExecutionRole \
--policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource": "arn:aws:iam::*:role/StackSetExecutionRole"
}]
}'
続いてバックアップアカウント・ソースアカウントの両方に以下のIAMロールをデプロイします。
利用するサービスのみに権限を絞っています。 Account-ID-DstはバックアップアカウントIDを記載します。
aws iam create-role \
--role-name StackSetExecutionRole \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::<Account-ID-Dst>:root"},
"Action": "sts:AssumeRole"
}]
}'
aws iam put-role-policy \
--role-name StackSetExecutionRole \
--policy-name StackSetExecution \
--policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"cloudformation:*",
"events:*",
"iam:*",
"lambda:*",
"secretsmanager:*",
"s3:*",
"kms:*",
"sns:*",
"logs:*"
],
"Resource": "*"
}]
}'
ステップ2. ベースラインのデプロイ
バックアップアカウントにて、CloudFormationテンプレート secrets_manager_backup.baseline.template.yml をデプロイします。
Service Catalogの操作ができるIAMのARNを渡す必要があるので、aws sts get-caller-identity --query 'Arn' --output text を叩いて置き換えましょう。
aws cloudformation create-stack \
--stack-name secrets-manager-backup-baseline \
--template-url https://s3.amazonaws.com/<Bucket-Name>/templates/secrets_manager_backup.baseline.template.yml \
--capabilities CAPABILITY_NAMED_IAM \
--parameters ParameterKey=pResourceLocationBucket,ParameterValue=<Bucket-Name> \
ParameterKey=pServiceCatalogPortfolioAssociationPrincipalARN,ParameterValue=<IAM-Principal-ARN> \
ParameterKey=pResourceLocationBucketRegion,ParameterValue=ap-northeast-1 \
ParameterKey=pResourceLocationBucketKeyPrefix,ParameterValue=templates \
--region ap-northeast-1
なお、IAMプリンシパルは role, user, groupしか受け付けないようで、assumed-roleではValidateエラーになりハマったので注意が必要です。
CloudFormation画面で CREATE_COMPLETE になっていればOKです。

スタックの作成が完了すると、Service Catalogに「Secrets Manager Backup」プロダクトが登録されます。

ステップ3. Service Catalogでバックアップ対象を設定
Service Catalogから「Secrets Manager Backup」プロダクトを選択・起動します。

バックアップ対象のソースアカウントIDなど、各パラメータを指定します。
- Bucket Name
- バックアップ先のS3バケット名
- Bucket Region
- バケットのリージョン。今回は
ap-northeast-1
- バケットのリージョン。今回は
- Object Prefix
- デプロイ用のファイルが格納されているフォルダを指定。今回は直下にテンプレートを格納したため空欄
- Source Account Configuration
- ソースアカウントIDやリージョンを
- Backup Account Configuration
- バックアップアカウントIDやリージョンを指定
- CloudFormation Stackset Configuration
- Administration Role Nameは(手順1で名前を変えていないなら)
StackSetAdminRole - Execution Role Nameは(手順1で名前を変えていないなら)
StackSetExecutionRoleを指定
- Administration Role Nameは(手順1で名前を変えていないなら)

ちなみに、Service Catalog経由でのプロビジョニングが採用されているのは、バックアップ対象アカウントの追加・削除を運用者が手軽にできるようにする意図がありそうです。CloudFormationを直接触らなくて良いのはオペレーションミスの低減にも繋がりますね。
無事に製品が起動できました。ここでエラーになる場合は、IAMロールの設定ができていない可能性もあるので、見直してみてください。

ステップ4. 動作確認
ソースアカウントにてテスト用のシークレットを作成します。
aws secretsmanager create-secret \
--name "test/cross-account-backup" \
--secret-string '{"username":"admin","password":"P@ssw0rd"}' \
--tags Key=Environment,Value=test \
--region ap-northeast-1
※ シークレットにタグが付与されていないと、バックアップアカウント側のLambdaがコケるようです。
数分後、バックアップアカウント側のSecrets Managerを確認します。
<ACCOUNT_ID>/ap-northeast-1/test/cross-account-backup という名前でシークレットが作成されていれば成功です。

更新時の動作も確認
シークレットの作成だけでなく、更新時にもバックアップが走るか確認しておきます。
ソースアカウントで以下を実行してみます。
aws secretsmanager update-secret \
--secret-id "test/cross-account-backup" \
--secret-string '{"username":"admin","password":"New-P@ssw0rd!"}' \
--description "Test secret for cross-account backup" \
--region ap-northeast-1
※ 更新時はシークレット自体の Description が付与されていないと、バックアップアカウント側のLambdaがコケるようなので注意。
30秒後に確認しましたが、しっかり反映されていました。

備考
削除時の挙動
ソースアカウントでシークレットを削除した場合、バックアップ側のシークレットがどうなるかは気になるところです。
READMEには明記されていなかったので、実際に試してみます。
以下は、シークレットの削除コマンドです(forceオプション付いているので、実行はお気をつけて)
aws secretsmanager delete-secret \
--secret-id "test/cross-account-backup" \
--force-delete-without-recovery \
--region ap-northeast-1
ソースアカウント側でシークレットを削除しました。が、バックアップアカウントでは勿論連動して削除されることはありません。

料金
このソリューションの主なコストは以下の通りです。
| リソース | 概算料金 |
|---|---|
| EventBridge | カスタムイベントバスの料金(イベント100万件あたり$1.00) |
| Secrets Manager (バックアップ側) | シークレット1つあたり$0.40/月 + APIコール10,000件あたり$0.05 |
バックアップ元のシークレット数が多い環境では、バックアップ側のSecrets Manager料金がやや掛かりそうです。
他にLambda実行課金はありますが、それは無視できると思います。
おわりに
Secrets Managerのシークレットは、意外と「バックアップどうしてるの?」と聞かれると困るリソースのひとつかもしれません。
RDSやEBSのようにAWS Backupの対象にもなっていないので、こういったソリューションを使って別アカウントに退避しておくのはDR観点で有効だと思います。
aws-samplesのリポジトリはそのまま使えるものも多いので、似たユースケースでお困りの方は一度覗いてみることをオススメします。
ちなみに、 pStackSetBackupAccountReplicationRegions を値を入れると大阪リージョンにも同時バックアップできるじゃん!と思って入れたら、恐らく権限不足(ReplicateSecretToRegions)のエラーになりました。
このオプションを使う場合はaws-samplesでデプロイされるIAMを触る必要が出そうだったので、今回はスキップしています。
このエントリが誰かの助けになれば幸いです。
それでは、クラウド事業本部 コンサルティング部の荒平がお送りしました!
参考






