以前、AMIを他リージョンへスケジュールコピーするLambda関数という記事を書きましたが、それをアカウントを跨いで実行するためのものです。
ポイント
- コピー対象の AMI 全てにコピー先のアカウントに対するアクセス権を付与する
- コピー元アカウントの KMS キーを利用できるようにコピー先で作成する Lambda 関数のロールに必要なポリシーを付与する
- コピー先アカウントからはコピー元のタグは不可視なので、別項目(「説明」:description)をキーにしてコピー対象を絞り込む
なお、今回は、
- AMI のアクセス権付与はコピー元アカウントで
- AMI コピーはコピー先アカウントで
それぞれ実行するための Lambda 関数を作成します。
※前回同様、コピー元の AMI は KMS カスタマー管理キーで暗号化されているものとします(デフォルトキー=AWS マネージドキーは不可)。
作業内容
1. コピー元アカウントで KMS キーにキーユーザーを登録
AMI 暗号化のために使っている KMS カスタマー管理キーに対して、
- 4. の Lambda 関数で利用するロール(キーユーザーとして)
- 前回記事「1. ロール作成」と同じ要領で作成。面倒なら 4. の Lambda 関数作成時に合わせて作成して KMS カスタマー管理キーに追加する。↓の 2. も同様。
- コピー先アカウント(アカウント ID)
を追加します。
2. コピー先アカウントで KMS カスタマー管理キーを作成してキーユーザーを登録
カスタマー管理キーを作成し、キーユーザーとして 5. の Lambda 関数で利用するロールを追加します。
3. コピー先アカウントでポリシーを登録
コピー元アカウントの KMS カスタマー管理キーを使えるように、コピー先アカウントに以下のポリシーを登録します。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowUseOfCMK",
"Effect": "Allow",
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
],
"Resource": "arn:aws:kms:ap-northeast-1:【コピー元アカウントID】:key/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"
},
{
"Sid": "AllowUseofCMKToCreateEncryptedResources",
"Effect": "Allow",
"Action": "kms:CreateGrant",
"Resource": "arn:aws:kms:ap-northeast-1:【コピー元アカウントID】:key/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
"Condition": {
"Bool": {
"kms:GrantIsForAWSResource": true
}
}
}
]
}
※「key/」の後ろにはコピー元 AMI の暗号化に使う KMS カスタマー管理キーの ID を入力します。
登録したら、2. の KMS カスタマー管理キーのキーユーザーとして登録した Lambda 関数のロールにこのポリシーを追加で付与します。
4. コピー元アカウントで AMI アクセス権付与のための Lambda 関数を作成
Python 3.7 で以下のコードを登録します。
import boto3
import os
def lambda_handler(event, context):
region = os.environ["REGION"]
tagKey = os.environ["TAG_KEY"]
owner = os.environ["OWNER"]
share = os.environ["SHARE"]
# 変更対象のイメージを抽出
client = boto3.client('ec2', region)
images = client.describe_images(
Filters=[{'Name':'tag-key','Values':[tagKey]}],
Owners=[owner]
)
# 変更対象イメージがあれば変更実行
for image in images['Images']:
modifyKey = image['ImageId']
print("Modifying Image Attribute '{}'.".format(modifyKey))
client.modify_image_attribute(
ImageId=modifyKey,
LaunchPermission={'Add':[{'UserId':share}]}
)
for snapshot in image['BlockDeviceMappings']:
client.modify_snapshot_attribute(
CreateVolumePermission={'Add':[{'UserId':share}]},
SnapshotId=snapshot['Ebs']['SnapshotId']
)
なお、AMI の抽出対応を 5. と同様に「説明」(description)にしたい場合は 5. を参考にコードを書き替えてください。
テストしてみてうまく動作したら、必要に応じて CloudWatch イベントの cron 式で定時実行をセットします。
5. コピー先アカウントで AMI コピーを行う Lambda 関数を作成
Python 3.7 で以下のコードを登録します。
import boto3
import os
def lambda_handler(event, context):
sourceRegion = os.environ["SOURCE_REGION"]
destRegion = os.environ["DEST_REGION"]
sourceDescription = os.environ["SOURCE_DESCRIPTION"]
destTagKey = os.environ["DEST_TAG_KEY"]
owner = os.environ["OWNER"]
encryptKey = os.environ["ENCRYPT_KEY"]
# コピー対象のイメージを抽出
sourceClient = boto3.client('ec2', sourceRegion)
sourceImages = sourceClient.describe_images(
Filters=[{'Name':'description','Values':[sourceDescription]}],
Owners=[owner]
)
# 削除対象のイメージを抽出
destClient = boto3.client('ec2', destRegion)
deleteImages = destClient.describe_images(
Filters=[{'Name':'tag-key','Values':[destTagKey]}],
Owners=[owner]
)
# コピー対象イメージがあればコピー実行
for sourceImage in sourceImages['Images']:
copyKey = sourceImage['ImageId']
print("Copying Image '{}' to {}.".format(copyKey, destRegion))
new_ami = destClient.copy_image(
DryRun=False,
SourceRegion=sourceRegion,
SourceImageId=copyKey,
Name=sourceImage['Name'],
Description='',
Encrypted=True,
KmsKeyId=encryptKey
)
destClient.create_tags(Resources=[new_ami['ImageId']], Tags=[{'Key':destTagKey,'Value':''}])
# 削除対象イメージがあれば削除実行
for deleteImage in deleteImages['Images']:
deleteKey = deleteImage['ImageId']
print("Deleting Image '{}' from {}.".format(deleteKey, destRegion))
try:
destClient.deregister_image(ImageId=deleteKey)
for snapshot in deleteImage['BlockDeviceMappings']:
destClient.delete_snapshot(SnapshotId=snapshot['Ebs']['SnapshotId'], DryRun=False)
except destClient.exceptions.NoSuchEntityException:
print("Image '{}' not found.".format(deleteKey))
※コピー対象の AMI の絞り込みには以下の項目を使います。
テストしてみてうまく動作したら、必要に応じて CloudWatch イベントの cron 式で定時実行をセットします(4. の 10 ~ 15 分程度後の時間を指定)。
その他
以前、一度にコピーできる AMI の数は 25 個だった気がするのですが、最近実行してみたところ 50 個まで同時にコピーできるようになっていました。