この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
EC2 Image Builder(以下 Image Builder)のパイプラインから EC2イメージ(AMI)を作成したときに、 そのAMI IDを Systems Manager(SSM)パラメータストアに自動的に格納するような構成を作っていきます。
前回は 1アカウント内で完結するケースを書きました。
今回は「マルチアカウント編」ということで、 イメージ配布(共有)先アカウントの SSMパラメータに格納する 構成を作ってみます。
図で表すと以下のとおりです。
- Image Builderパイプライン で AMI作成・配布時に SNSトピックを通知するように設定しておきます
- このSNSトピック通知をトリガーに Lambda関数を実行します
- このLambda関数で「各アカウントのSSMパラメータストア」に AMI IDの情報を登録( PutParameter )します
構成
大枠は 前回の1アカウント編のブログ と変わりません。 共通部分は簡単に説明します。
EC2 Image Builder
パイプラインの インフラストラクチャ設定 > SNS 部分を設定しておきます。
ディストリビューション設定 部分でイメージ共有の設定を行います。
このSNS設定/ディストリビューション設定により、AMI作成・配布が完了したときに、以下のようなメッセージを 通知することができます。
{
"versionlessArn": "arn:aws:imagebuilder:ap-northeast-1:123456789012:image/sample-recipe",
"semver": 1073741827,
"arn": "arn:aws:imagebuilder:ap-northeast-1:123456789012:image/sample-recipe/0.0.1/3",
"name": "sample-recipe",
(略)
"distributionConfiguration": {
"arn": "arn:aws:imagebuilder:ap-northeast-1:123456789012:distribution-configuration/sample-al2-ami-xxxx",
"name": "sample-al2-ami-xxxx",
"dateCreated": "Mar 5, 2021 1:14:50 AM",
"dateUpdated": "Mar 9, 2021 2:56:29 AM",
"distributions": [
{
"region": "ap-northeast-1",
"amiDistributionConfiguration": {
"launchPermission": {
"userIds": [
"111111111111",
"222222222222"
]
}
}
}
],
"tags": {
"internalId": "6520c90f-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"resourceArn": "arn:aws:imagebuilder:ap-northeast-1:123456789012:distribution-configuration/sample-al2-ami-xxxx"
},
"accountId": "123456789012"
},
(略)
"outputResources": {
"amis": [
{
"region": "ap-northeast-1",
"image": "ami-01xxxxxxxxxxxxxxx",
"name": "sample-recipe 2021-03-05T01-16-05.489Z",
"accountId": "123456789012"
}
]
},
(略)
}
ハイライト部分 distributionConfiguration
が配布設定です。後述の Lambda 関数で利用します。
IAMロール
上記のように IAMロールを 2種類 作成する必要があります。
- ロールA
- Lambdaの実行アカウントに作成する
AWSLambdaBasicExecutionRole
(AWS管理ポリシー) を付与- ロールBに
AssumeRole
を行う権限を付与
- ロールB
- 配布先アカウントにそれぞれ作成する
- ロールAが
AssumeRole
できるようする(信頼ポリシー) - SSMパラメータを作成/更新する権限を付与
今回のブログではロールBの名前を LambdaExecutionRoleForSSMParam
としています。
Lambda 関数
ランタイム: Python3.7
で作成しています。
以下のようなコードを作成しました。
import json
import boto3
from boto3.session import Session
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
REGION = "ap-northeast-1"
ROLE_NAME = "LambdaExecutionRoleForSSMParam"
sts = boto3.client('sts')
def lambda_handler(event, context):
message = json.loads(event["Records"][0]["Sns"]["Message"])
process_sns_message(message)
def process_sns_message(message):
logger.info("Printing message: {}".format(message))
# state キーが無いとき、もしくは state.status が "AVAILABLE" でないときはパラメータストアに登録しない
if message.get('state') == None or message['state'].get('status') != "AVAILABLE":
return None
# レシピ名, AMI ID, 配布情報を取得
recipe_name = message['name']
ami = message['outputResources']['amis'][0]
dists = message['distributionConfiguration']['distributions']
logger.info("recipe_name={}".format(recipe_name))
logger.info("ami={}".format(ami))
logger.info("dists={}".format(dists))
# 配布先アカウントの SSMパラメータに登録
for dist in filter(lambda d: d.get('region') == REGION, dists):
for user_id in dist['amiDistributionConfiguration']['launchPermission']['userIds']:
logger.info(
"executing ssm:PutParameters to user:{}".format(user_id))
put_param(recipe_name, ami, user_id)
def put_param(recipe_name, ami, user_id):
# 一時情報の取得
remote = sts.assume_role(
RoleArn="arn:aws:iam::{}:role/{}".format(user_id, ROLE_NAME),
RoleSessionName="imagebuilder_master"
)
ssm = boto3.client(
'ssm',
aws_access_key_id=remote['Credentials']['AccessKeyId'],
aws_secret_access_key=remote['Credentials']['SecretAccessKey'],
aws_session_token=remote['Credentials']['SessionToken']
)
# SSMパラメータストアに登録
response = ssm.put_parameter(
Name="/ec2-imagebuilder/latest/{}".format(recipe_name),
Description="Latest AMI ID:{}".format(recipe_name),
Value=ami['image'],
Type='String',
Overwrite=True,
Tier='Standard'
)
logger.info(
"[put_param] ssm.put_parameter response: {}".format(response))
受け取った SNSメッセージをのステータス、AMI ID、レシピ名、配布情報を取得して、
配布先アカウントそれぞれに
SSMパラメータ ( "/ec2-imagebuilder/latest/{レシピ名}"
) に登録する処理を行います。
▼ ほか設定: SNS
Image Builder のインフラストラクチャ設定で指定したSNSトピックをトリガーとします。
▼ ほか設定: IAMロール
前述の ロールA を付与します。
AssumeRole
部分のポリシーは以下のようになります。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Resource": "arn:aws:iam::*:role/LambdaExecutionRoleForSSMParam",
"Action": "sts:AssumeRole",
}
]
}
構築
(事前準備) 配布先アカウントのIAMロール
前述の ロールB (LambdaExecutionRoleForSSMParam
) を配布先アカウントそれぞれに作成します。
以下 CloudFormation(CFn)テンプレートを作成しました。
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
MainAccountId:
Description: "Main AWS account ID to run Lambda function"
Type: String
Resources:
Role:
Type: AWS::IAM::Role
Properties:
RoleName: LambdaExecutionRoleForSSMParam
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action: "sts:AssumeRole"
Principal:
"AWS": !Sub "arn:aws:iam::${MainAccountId}:root"
Policies:
- PolicyName: policy-for-ssm-action
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: SSMPutParameter
Effect: "Allow"
Action: "ssm:PutParameter"
Resource: "*"
個別に展開、もしくは CFn StackSets 展開しておきます。
SAMで構築
SNSトピックとLambda関数(+ロールA)の部分 を AWS サーバーレスアプリケーションモデル (AWS SAM)を使って構築しました。 SAMのプロジェクト構成内容を記します。
▼ プロジェクト
sam init
で新規プロジェクトを作成します。
sam init --runtime python3.7 --name tracking-latest-images-in-imagebuilder-cross-account
▼ template.yaml
必要なリソースを記述した template.yaml
は以下のとおり。
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: "tracking the latest AMI ID in EC2 Image Builder pipeline"
Resources:
# SNS topic
SnsTopic:
Type: AWS::SNS::Topic
Properties:
TopicName: topic-for-imagebuilder
# Lambda function
Function:
Type: AWS::Serverless::Function
Properties:
Description: "Update SSM Parameter with the latest AMI ID"
CodeUri: scripts/
Handler: app.lambda_handler
Runtime: python3.7
Events:
EventBridgeRule:
Type: SNS
Properties:
Topic: !Ref SnsTopic
Policies:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
- Version: '2012-10-17'
Statement:
- Sid: STSAssumeRole
Effect: "Allow"
Action: "sts:AssumeRole"
Resource: "arn:aws:iam::*:role/LambdaExecutionRoleForSSMParam"
▼ scripts/app.py
前述のLambda関数のコードを scripts/app.py
に格納します。
▼ ビルド、デプロイ
sam build
sam deploy --guided
特にパラメータ指定していないので、ガイド通りに YES 選択でデプロイできます。
確認
事前に ImageBuilderのパイプラインの インフラストラクチャ設定 > SNS 部分 に、 作成した SNSトピックを指定しておきます。
パイプライン実行
Image Builder のパイプラインを実行します。 [使用可能] になるまで待ちます。
配布先アカウントの確認
まず、EC2の AMI画面を見てみます。Image Builder からのAMI共有を確認できました。
次に配布先のアカウントの SSMパラメータを見てみます。
AMI ID が登録されていること確認できました。
おわりに
EC2 Image Builder で作った最新AMIを「配布先アカウントの SSMパラメータストア」に 自動登録する仕組みを作ってみました。
マルチアカウント環境では AMIを他アカウントに共有する機会が多いと思います。 配布されたAMI および作成されたSSMパラメータを使って、 EC2インスタンスを作成する際の AMI ID 参照などに活用できると思います。
参考
- AWS
- DevelopersIO