Help us understand the problem. What is going on with this article?

AMI をアカウント跨ぎで(他リージョンへ)スケジュールコピーする Lambda 関数

以前、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 で以下のコードを登録します。

AMIアクセス権付与
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'] 
            ) 

環境変数等は以下のように入力します。
attr-change.png

なお、AMI の抽出対応を 5. と同様に「説明」(description)にしたい場合は 5. を参考にコードを書き替えてください。

テストしてみてうまく動作したら、必要に応じて CloudWatch イベントの cron 式で定時実行をセットします。

5. コピー先アカウントで AMI コピーを行う Lambda 関数を作成

Python 3.7 で以下のコードを登録します。

AMIコピー
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)) 

環境変数等は以下のように入力します。
attr-copy.png

※コピー対象の AMI の絞り込みには以下の項目を使います。
attr-ami.png

テストしてみてうまく動作したら、必要に応じて CloudWatch イベントの cron 式で定時実行をセットします(4. の 10 ~ 15 分程度後の時間を指定)。

その他

以前、一度にコピーできる AMI の数は 25 個だった気がするのですが、最近実行してみたところ 50 個まで同時にコピーできるようになっていました。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away