データベースのスナップショットを作成したアカウントから別アカウントにコピーする
データベースのスナップショットを作成した AWS アカウントから別の AWS アカウントにコピーしたい場合、スナップショットがデフォルトの KMS キー(alias: aws/rds)で暗号化されていると共有できません。
2つの記事を読むと、別のアカウントにスナップショットを共有するためには、スナップショットをカスタマーマネージドキー(以下:CMK)で暗号化する必要があります。
- Amazon RDS の手動 DB スナップショットまたは Aurora DB クラスタースナップショットを他の AWS アカウントと共有するにはどうすればよいですか? - AWS re:Post
 - 暗号化された Amazon Aurora スナップショットを別のアカウントと共有するにはどうすればよいですか? - AWS re:Post
 
私自身スナップショットの暗号化に使用する KMS キーが共有元、共有先、どちらのアカウントに作成するのか最初わかりませんでした。
調べてみると、AWS のドキュメントには以下のようなことが書いてあります。
スナップショットの暗号化に使用された AWS KMS key を、スナップショットにアクセスできるようにするすべてのアカウントと共有します。 KMS キーポリシーに他のアカウントを追加することで、KMS キーを別の AWS アカウントと共有できます。
さらに AWSの情報センターでは、ターゲットアカウントに、ソースアカウント内のカスタム AWS KMS キーへのアクセスを許可する方法について記述があります。
つまりKMS のキーは共有元の AWS アカウントにあり、それを別アカウント(共有先)に共有することで暗号化されたスナップショットを別アカウントに共有することができるそうです。
今回は、AWS のアーキテクチャ図と一緒に説明している記事が少なく、イメージしづらかったことから自ら検証してみようと思います。
https://github.com/kntks/blog-code/tree/main/2024/02/copy-rds-snapshot
アーキテクチャ
Section titled “アーキテクチャ”AWS アカウントを2つ用意します。
Sandbox-1 がスナップショット共有元アカウントで、Sandbox-2 が共有先アカウントです。

以下の画像にあるアーキテクチャを Terraform で作成します。
これから検証する内容の簡単な手順を示します。
- Terraform で環境構築、共有元アカウントに Aurora クラスターと DB インスタンスを作成。
 - 踏み台サーバから DB に簡単なデータを Insert する
 - デフォルトキーでスナップショットを作成する
 - CMK でスナップショットのコピーを作成する
 - 共有先アカウントにスナップショットを共有する
 - 共有先アカウントでスナップショットから DB を復元する
 
1. 環境構築
Section titled “1. 環境構築”Terraform は 2024 年 1 月現在最新の 1.7.0 を使用します。
mise local terraform 1.7.0
terraform initterraform planterraform apply -auto-approveRDS のデフォルトキー
Section titled “RDS のデフォルトキー”Aurora MySQL クラスターと DB インスタンスの作成が完了すると、AWS マネージド型キーに RDS のデフォルトキー(alias: aws/rds)が作成されます。

AWS マネージドキー は、お客様のアカウントにある KMS キーであり、AWS KMS と統合されている AWS のサービスがお客様に代わって作成し、管理し、使用します。デフォルトでは、RDS AWS マネージドキー (aws/rds) が暗号化に使用されます。
RDS のデフォルトキーのキーポリシー
```json { "Version": "2012-10-17", "Id": "auto-rds-2", "Statement": [ { "Sid": "Allow access through RDS for all principals in the account that are authorized to use RDS", "Effect": "Allow", "Principal": { "AWS": "*" }, "Action": [ "kms:Encrypt", "kms:Decrypt", "kms:ReEncrypt*", "kms:GenerateDataKey*", "kms:CreateGrant", "kms:ListGrants", "kms:DescribeKey" ], "Resource": "*", "Condition": { "StringEquals": { "kms:ViaService": "rds.ap-northeast-1.amazonaws.com", "kms:CallerAccount": "891377404752" } } }, { "Sid": "Allow direct access to key metadata to the account", "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::891377404752:root" }, "Action": [ "kms:Describe*", "kms:Get*", "kms:List*", "kms:RevokeGrant" ], "Resource": "*" } ] } ```2. 踏み台サーバから DB に簡単なデータを Insert する
Section titled “2. 踏み台サーバから DB に簡単なデータを Insert する”※ 共有元アカウントでの作業
まず、Aurora MySQL のエンドポイントを確認します。
$ aws rds describe-db-cluster-endpoints --db-cluster-identifier example-db --profile sandbox-1{    "DBClusterEndpoints": [        {            "DBClusterIdentifier": "example-db",            "Endpoint": "example-db.cluster-cjmu0yqksd6t.ap-northeast-1.rds.amazonaws.com",            "Status": "available",            "EndpointType": "WRITER"        },        {            "DBClusterIdentifier": "example-db",            "Endpoint": "example-db.cluster-ro-cjmu0yqksd6t.ap-northeast-1.rds.amazonaws.com",            "Status": "available",            "EndpointType": "READER"        }    ]}データベースを作成すると AWS Secret Manager にシークレットが自動で作成されます。そこから root のパスワードを取得します

踏み台サーバのインスタンス ID を取得し、セッションを開始します。
aws ec2 describe-instances --filter "Name=tag:Name,Values=example-bastion" "Name=instance-state-name,Values=running" --query "Reservations[].Instances[].InstanceId" --output text --profile sandbox-1
aws ssm start-session --target i-064c501d998f283a6 --profile sandbox-1踏み台サーバを立ち上げる際に user_data に mysql client をインストールするスクリプトを入れているので、mysql コマンドが使えるはずです。
sh-5.2$ which mysql/usr/bin/mysql踏み台サーバから DB にアクセスします。
$ mysql -h example-db.cluster-cjmu0yqksd6t.ap-northeast-1.rds.amazonaws.com -P 3306 -u root -pEnter password:参考:Amazon Aurora DB クラスターへの接続 - AWS Documentation
共有先のアカウントでスナップショットから DB を作成したときにデータが入っているかを確認したいため、この作業では、データベースにデータを入れておきます。
Terraform では、database_name を example にしたので、use example; でデータベースを変更できます。
mysql> use example;Database changedデータを作成します。
CREATE TABLE users (`user_id` INT NOT NULL  AUTO_INCREMENT,`name` varchar(255) NOT NULL,`age` INT NOT NULL, PRIMARY KEY(`user_id`)) ENGINE=InnoDB DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_bin;
INSERT INTO users (name, age) VALUES ("test1", 10), ("test2", 20);データが入ったので、問題なさそうです。
mysql> select * from users;+---------+-------+-----+| user_id | name  | age |+---------+-------+-----+|       1 | test1 |  10 ||       2 | test2 |  20 |+---------+-------+-----+2 rows in set (0.00 sec)3. デフォルトキーでスナップショットを作成する
Section titled “3. デフォルトキーでスナップショットを作成する”※ 共有元アカウントでの作業
データベースからスナップショットを作成します。

スナップショットの取得ボタンを押します。

4. CMK でスナップショットのコピーを作成する
Section titled “4. CMK でスナップショットのコピーを作成する”これで RDSのデフォルトキーで暗号化されたスナップショットが取得できます。

Terraform であらかじめ作成していた CMK を設定します。

スナップショットをコピーのボタンを押した後、example-db-copy-1 のスナップショットが作成されました。

5. 共有先アカウントにスナップショットを共有する
Section titled “5. 共有先アカウントにスナップショットを共有する”※ 共有元アカウントでの作業
example-db-copy-1 をクリックすると以下のページに遷移します。アクションからスナップショットの共有を選択してください。

アクセス許可に Sandbox-2 の AWS アカウントID を入れ、追加ボタンを押します。
そのあと、保存ボタンを押してください。

6. 共有先アカウントでスナップショットから DB を復元する
Section titled “6. 共有先アカウントでスナップショットから DB を復元する”ここからは Sandbox-2 のアカウントでの作業です。
Sandbox-2 のアカウントに移動します。RDS のページに行った後、自分と共有のタブを確認すると共有されたスナップショットが確認できました。

このスナップショットから復元してみます。

スナップショットを復元のページが長いため、変更点だけまとめました。
| 設定 | 値 | 
|---|---|
| DBインスタンス識別子 | example-db-2 | 
| インスタンスの設定 | 値 | 
|---|---|
| バースト可能クラス | (ラジオボタンにチェックを入れる)db.t3.medium | 
| 接続 | 値 | 補足 | 
|---|---|---|
| VPC | example-vpc | Terraaform で作成済 | 
| パブリックアクセス | なし | |
| VPC セキュリティグループ | 既存の選択 | |
| 既存の VPC セキュリティグループ | example-db-2 | Terraform で作成済 | 
| アベイラビリティーゾーン | ap-northeast-1 | 
Sandbox-1の時と同じ要領で DB に接続します。パスワードは Sandbox-1 で DB に接続した時と同じです。
aws rds describe-db-cluster-endpoints --db-cluster-identifier example-db-2-cluster --profile sandbox-2
aws ec2 describe-instances --filter "Name=tag:Name,Values=example-bastion" "Name=instance-state-name,Values=running" --query "Reservations[].Instances[].InstanceId" --output text --profile sandbox-2
aws ssm start-session --target i-006b7a2e209096de9 --profile sandbox-2
mysql -h example-db-2-cluster.cluster-c9ygy4kamm38.ap-northeast-1.rds.amazonaws.com -P 3306 -u root -pSandbox-2 のアカウントに作成した DB でもデータを復元することができました。
mysql> use example;
Database changedmysql> select * from users;+---------+-------+-----+| user_id | name  | age |+---------+-------+-----+|       1 | test1 |  10 ||       2 | test2 |  20 |+---------+-------+-----+2 rows in set (0.00 sec)Sandbox-2 では、DB を手動で作成したので、先に DB を AWS マネジメントコンソールから削除します。
そのあと、Terraform からすべてのリソースを削除します。
terraform destroySandbox-1 アカウントからスナップショットを取得し、共有することで Sandbox-2 アカウントで復元することができました。
Sandbox-2 でスナップショットを復元した後のデータベースパスワードは、Sandbox-1 のときと同じですが、Sandbox-2 側の AWS Secret Manager にシークレットが作成されるわけではありません。自分で設定する必要があります。
今回の検証では、操作する IAM が Administrator だったため、Sandbox-2 側の IAM について何もしませんでした。ポリシーや PermissionSet の設計をしている組織では、共有先の AWS アカウントでスナップショットを復元するときの権限には注意したほうがよさそうです。
トラブルシューティング
Section titled “トラブルシューティング”updating policy: MalformedPolicyDocumentException: The new key policy will not allow you to update the key policy in the future
Section titled “updating policy: MalformedPolicyDocumentException: The new key policy will not allow you to update the key policy in the future”とりあえず、KMS を作成しようと以下のような Terraform を作成したところ、 terraform apply のタイミングでエラーになりました。
resource "aws_kms_key" "snapshot" {  count                   = var.create_customer_managed_key ? 1 : 0  description             = "to encrypt the database snapshot"  deletion_window_in_days = 10}
resource "aws_kms_key_policy" "this" {  count  = var.create_customer_managed_key ? 1 : 0  key_id = aws_kms_key.snapshot[0].id  policy = data.aws_iam_policy_document.this.json}
data "aws_iam_policy_document" "this" {  statement {    actions = [      "kms:Encrypt",      "kms:Decrypt",      "kms:ReEncrypt*",      "kms:GenerateDataKey*",      "kms:DescribeKey",    ]    resources = ["*"]    principals {      type = "AWS"      identifiers = [ "*" ]    }  }}エラー文を読んでみると、ポリシーの更新中: MalformedPolicyDocumentException: 新しいキーポリシーでは将来キーポリシーを更新できません。と書かれています。
以下の記事では、権限が足りないため発生している事象であることが説明されています。
セーフティチェックの 1 つが、キーポリシーのプリンシパルに CreateKey API と PutKeyPolicy API を実行するために必要な許可があることを確認します。このチェックにより、AWS KMS キーが管理不能になる可能性がなくなります。つまり、キーポリシーを変更したり、キーを削除したりできなくなります。
エラーを解決するために強めの権限をつけてみると解消しました。
data "aws_iam_policy_document" "this" {  # 以下の権限を付与する  statement {    principals {      type        = "AWS"      identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"]    }    actions = [      "kms:Create*",      "kms:Describe*",      "kms:Enable*",      "kms:List*",      "kms:Put*",      "kms:Update*",      "kms:Revoke*",      "kms:Disable*",      "kms:Get*",      "kms:Delete*",      "kms:ScheduleKeyDeletion",      "kms:CancelKeyDeletion"    ]    resources = ["*"]  }  省略}Aurora DBクラスターに接続できない
Section titled “Aurora DBクラスターに接続できない”パスワードを入力したにもかかわらず、MySQL がスタートしない場合は、DB に関連している Security Group のルールが間違っている可能性があります。
sh-5.2$ mysql -h example-db.cluster-cjmu0yqksd6t.ap-northeast-1.rds.amazonaws.com -P 3306 -u root -pEnter password:今回は、EC2 インスタンスに関連していたサブネットからの通信を Security Group が許可していませんでした。