この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
はじめに
EC2 Image Builder(以下 Image Builder)のパイプラインから EC2イメージ(AMI)を作成したときに、 そのAMI IDを Systems Manager(SSM)パラメータストアに自動的に格納するような構成を試してみます。
図で表すと以下のとおりです。
- Image Builderパイプライン で AMI作成時に SNSトピックを通知するように設定しておきます
- このSNSトピック通知をトリガーに Lambda関数を実行します
- このLambda関数で SSMパラメータストアに AMI IDの情報を登録(
PutParameter
)します
なお、今回試した構成は以下 AWSブログを参考にしています。
構成
EC2 Image Builder
パイプラインやレシピの詳細説明は割愛します。
パイプラインの インフラストラクチャ設定 > SNS 部分を設定しておきます。
SNS設定により、ImageBuilder で 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",
(略)
"state": {
"status": "AVAILABLE"
},
(略)
"outputResources": {
"amis": [
{
"region": "ap-northeast-1",
"image": "ami-01xxxxxxxxxxxxxxx",
"name": "sample-recipe 2021-03-05T01-16-05.489Z",
"accountId": "123456789012"
}
]
},
(略)
}
ハイライト部分(レシピ名, ステータス, AMI ID)を後述の Lambda関数で利用します。
Lambda関数
今回は ランタイム: Python3.7
で作成しています。
以下のようなコードを作成しました。
import json
import boto3
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
ssm = boto3.client('ssm')
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]
logger.info("recipe_name={}".format(recipe_name))
logger.info("ami={}".format(ami))
# 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("Printing ssm.put_parameter response: {}".format(response))
受け取った SNSメッセージをのステータス、AMI ID、レシピ名を取得して、
SSMパラメータ ( "/ec2-imagebuilder/latest/{レシピ名}"
) に登録するシンプルな処理です。
▼ ほか設定: SNS
Image Builder のインフラストラクチャ設定で指定したSNSトピックをトリガーとします。
▼ ほか設定: IAMロール
実行ロールに必要なポリシーは以下のとおり
AWSLambdaBasicExecutionRole
(AWS管理ポリシー)- SSMパラメータに値を入れるアクション(
ssm:PutParameter
)を許可するポリシー
後者は以下のようなポリシーです。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Resource": "*",
"Action": "ssm:PutParameter"
}
]
}
SAMで構築
上記構成 (SNSトピックとLambda関数の部分)を AWS サーバーレスアプリケーションモデル (AWS SAM)を使って構築しました。 SAMのプロジェクト構成内容を記します。
プロジェクト
sam init
で新規プロジェクトを作成します。
sam init --runtime python3.7 --name tracking-latest-images-in-imagebuilder
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:
- Effect: Allow
Action:
- ssm:PutParameter
Resource: '*'
scripts/app.py
前述のLambda関数のコードを scripts/app.py
に格納します。
ビルド, デプロイ
sam build
sam deploy --guided
特にパラメータ指定していないので、ガイド通りに YES 選択でデプロイできます。
確認
1回目
まず ImageBuilderのパイプラインの インフラストラクチャ設定 > SNS
部分に、前述で作成した
SNSトピックを指定します。
そのパイプラインを実行してみます。
[使用可能]
になるまで待ちましょう。
[使用可能]
となった段階で SSMパラメータストアを確認します。
先程 ImageBuilderパイプラインで作成した AMIの IDが登録されていることを確認できました。
2回目以降
再度 同じパイプラインを実行してみます。
値が上書きされたことを確認できました。
おわりに
EC2 Image Builder で作った最新AMIをSSMパラメータストアに自動登録する仕組みを作ってみました。 CloudFormation で EC2インスタンスを作成する際の AMI ID 参照などに活用できると思います。
参考
- AWS
- Boto3
ImageBuilder の通知メッセージサンプル
{
"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",
"version": "0.0.1",
"type": "AMI",
"buildVersion": 3,
"state": {
"status": "AVAILABLE"
},
"platform": "Linux",
"imageRecipe": {
"arn": "arn:aws:imagebuilder:ap-northeast-1:123456789012:image-recipe/sample-recipe/0.0.1",
"name": "sample-recipe",
"version": "0.0.1",
"components": [
{
"componentArn": "arn:aws:imagebuilder:ap-northeast-1:123456789012:component/sample-test/0.0.1/1"
},
{
"componentArn": "arn:aws:imagebuilder:ap-northeast-1:123456789012:component/sample-build/0.0.1/1"
}
],
"platform": "Linux",
"parentImage": "arn:aws:imagebuilder:ap-northeast-1:255485124026:image/amazon-linux-2-x86/2021.2.20/2",
"blockDeviceMappings": [
{
"deviceName": "/dev/xvda",
"ebs": {
"encrypted": false,
"deleteOnTermination": true,
"volumeSize": 8,
"volumeType": "gp2"
}
}
],
"dateCreated": "Oct 27, 2020 2:34:30 AM",
"tags": {
"internalId": "52b73813-4fed-4a5a-a64d-22521554d366",
"internalId": "52xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"resourceArn": "arn:aws:imagebuilder:ap-northeast-1:123456789012:image-recipe/sample-recipe/0.0.1"
},
"accountId": "123456789012"
},
"sourcePipelineArn": "arn:aws:imagebuilder:ap-northeast-1:123456789012:image-pipeline/sample-al2-ami",
"infrastructureConfiguration": {
"arn": "arn:aws:imagebuilder:ap-northeast-1:123456789012:infrastructure-configuration/sample-infra-conf",
"name": "sample-infra-conf",
"instanceTypes": [
"t3.small"
],
"instanceProfileName": "EC2InstanceProfileForImageBuilder",
"securityGroupIds": [
"sg-xxxxxxxxxxxxxxxxx"
],
"subnetId": "subnet-xxxxxxxx",
"tags": {
"internalId": "30xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"resourceArn": "arn:aws:imagebuilder:ap-northeast-1:123456789012:infrastructure-configuration/sample-infra-conf"
},
"logging": {
"s3Logs": {}
},
"terminateInstanceOnFailure": true,
"snsTopicArn": "arn:aws:sns:ap-northeast-1:123456789012:topic-for-imagebuilder",
"dateCreated": "Mar 5, 2021 1:14:49 AM",
"accountId": "123456789012"
},
"imageTestsConfigurationDocument": {
"imageTestsEnabled": true,
"timeoutMinutes": 720
},
"distributionConfiguration": {
"arn": "arn:aws:imagebuilder:ap-northeast-1:123456789012:distribution-configuration/sample-al2-ami-6b98ab10-8065-42e0-9954-975621d6258c",
"name": "sample-al2-ami-6b98ab10-8065-42e0-9954-975621d6258c",
"dateCreated": "Mar 5, 2021 1:14:50 AM",
"distributions": [
{
"region": "ap-northeast-1",
"amiDistributionConfiguration": {}
}
],
"tags": {
"internalId": "6520c90f-86ef-4a01-9143-6fee8079d1e8",
"resourceArn": "arn:aws:imagebuilder:ap-northeast-1:123456789012:distribution-configuration/sample-al2-ami-6b98ab10-8065-42e0-9954-975621d6258c"
},
"accountId": "123456789012"
},
"dateCreated": "Mar 5, 2021 1:15:31 AM",
"outputResources": {
"amis": [
{
"region": "ap-northeast-1",
"image": "ami-013e36332e677ed78",
"image": "ami-01xxxxxxxxxxxxxxx",
"name": "sample-recipe 2021-03-05T01-16-05.489Z",
"accountId": "123456789012"
}
]
},
"buildExecutionId": "7cba5e41-3d86-4b3e-8f01-e0f8edd36371",
"testExecutionId": "0d103efb-0755-4465-b6f0-3af0ce93d407",
"distributionJobId": "40e757ba-575b-4314-8e38-21a1cba8535e",
"integrationJobId": "e8eb11b2-6a63-4aaa-ab1b-685bae925833",
"accountId": "123456789012",
"enhancedImageMetadataEnabled": false,
"tags": {
"internalId": "6041d8a9-3491-4a03-a1b6-bf92c83227a3",
"resourceArn": "arn:aws:imagebuilder:ap-northeast-1:123456789012:image/sample-recipe/0.0.1/3"
}
}