Bitbucket PipelinesをトリガーにSSMドキュメントをキックする仕組みを実装したので手順をメモに残す。
実装した仕組み
Bitbucket Cloudのリポジトリで管理しているCFn Templateファイルを使ってAWS上で一時サーバのEC2を繰り返し作成/削除したいという要件があったため、CFnでのEC2作成の処理に関して以下の自動化の仕組みを実装した。
この仕組みを使うと、BitbucketのCommit IDへのタグ作成の1アクションだけでS3へのファイル転送からEC2作成までの一連の処理を自動で実行できる。
Bitbucket CloudからS3へCFn Templateファイルを転送
CFn Templateファイルを配置しているBitbucket CloudをAWSと連携するために、Bitbucket PipelinesというBitbucket Cloudで利用可能なパイプラインの機能を利用して、S3バケットへBitbucket Cloud上のファイルを転送する実装としている。
Bitbucket Pipelinesではパイプラインのトリガーとして、ブランチの更新とCommit IDへのタグ作成のどちらかを指定可能で、今回はタグ作成をトリガーとすることにした。
参考までに、ビルドを実現するAWS CodeBuildではBitbucketとの連携をサポートしており、Bitbucket WebhookをトリガーにCodeBuildのビルドプロジェクトを起動し、ビルドプロジェクトにてBitbucketから取得したファイルをS3に配置するという実装も可能。
BitbucketとCodeBuildを連携する場合、CodeBuildからBitbucketへの接続を許可する必要があるが、セキュリティポリシー上外部からBitbucketへの接続が不可であったために今回は不採用とした。
(参考) CodeBuild の Bitbucket プルリクエストとウェブフックフィルタのサンプル
S3の更新をトリガーにCFnでEC2を作成
BitbucketによるS3へのファイル転送をCloudTrailにてログ記録し、それをEventBridgeで検知、EventBridgeからSSMドキュメントを起動、SSMドキュメント内でS3へ転送したCFn Templateを利用してEC2を作成する実装としている。
SSMドキュメントの代わりに、CodePipelineからCloudFormationを呼び出してEC2を作成することも検討したが、一時サーバー上で実行する処理を既にSSMドキュメント化していたため、運用作業のインターフェースを一本化する目的でEC2作成自体もSSMドキュメントによる実行という方針に決まった。
前提
- AWSのアカウントを作成していること
- Bitbucket Cloudのリポジトリを作成し、以下のEC2インスタンス作成用のCFn Template「ec2-cfn.yml」をトップディレクトリ直下に配置済みであること
Resources:
MyInstance:
Type: AWS::EC2::Instance
Properties:
AvailabilityZone: ap-northeast-1a #EC2インスタンスを起動するアベイラビリティーゾーンを指定
ImageId: ami-06cd52961ce9f0d85 #EC2インスタンスのAMI IDを指定。ここで指定しているのはAmazon Linux AMI
Tags:
- Key: Name
Value: automation-test #EC2インスタンスの名前を指定
実装の手順
1. S3バケット作成
Bitbucket Pipelinesから送られたCFn Templateファイルを保管するためのS3バケットを作成する。
1-1. S3のコンソール > バケット にて「バケットの作成」をクリックする。
1-2. 以下を指定して「バケットを作成」をクリックする。
2. IAM定義追加
2-1. Bitbucket Pipelines用のIAMユーザーとIAMポリシー作成
Bitbucket PipelinesからS3バケットにCFn Templateファイルを配置するためのIAMユーザーとIAMポリシーを追加する。
2-1-1. IAMポリシー作成
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::<手順1で作成したS3バケット名に置き換え>/*",
"arn:aws:s3:::<手順1で作成したS3バケット名に置き換え>"
]
}
]
}
3. 「名前」に「S3_<バケット名>_Access」を指定し、「ポリシーの作成」をクリックする
2-1-2. IAMユーザー作成
- IAMのコンソール > ユーザー にて「ユーザーを追加」をクリックする
「ユーザー」に「bitbucket-pipeline」を指定し「アクセスの種類」で「プログラムによるアクセス」を選択し、た上で、「次のステップ:アクセス権限」をクリックする
「アクセス許可の設定」で「既存のポリシーを直接アタッチ」をクリックし、「ポリシーのフィルタ」で手順2-1-1で作成したポリシー「S3_<バケット名>_Access」を検索して指定した上で、「次のステップ:タグ」をクリックする
作成したIAMユーザーのアクセスキーIDとシークレットアクセスキーを記録する。記録した情報は手順3のBitbucket Pipelines作成時に利用する
2-2. Systems Manager用のサービスロールとIAMポリシー作成
EC2インスタンスのCFn Stack作成用のSSMドキュメントをSystems Managerに実行させるためのサービスロールとIAMポリシーを追加する。
2-2-1. IAMポリシー作成
ポリシーの内容には以下を指定し、ポリシーの名前は「SSMServiceAccess」を指定する。あとは手順2-1-1のIAMポリシー作成と同様。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"cloudformation:CreateStack",
"cloudformation:DescribeStacks",
"ec2:CreateTags",
"ec2:DescribeInstances",
"ec2:RunInstances",
"ec2:TerminateInstances",
"s3:GetObject"
],
"Resource": "*"
}
]
}
2-2-2. サービスロール作成
「ユースケースの選択」でサービスから「Systems Manager」、「Systems Manager Allows SSM to call AWS services on your behalf」を選択した後に「次のステップ:アクセス権限」をクリックする
「ポリシーのフィルタ」で手順2-2-1で作成した「SSMServiceAccess」を検索して指定した上で、「次のステップ:タグ」をクリックする
3. Bitbucket Pipelines作成
Bitbucketリポジトリで管理するCFn Templateファイルを手順1で作成したS3バケットに配置するためにBitbucket Pipelinesを作成する。
3-1. 以下のbitbucket-pipelines.ymlファイルをリポジトリのトップディレクトリ直下に追加する
bitbucket-pipelines.ymlはBitbucket Pipelinesの挙動を指定するためのファイル。詳細は以下を参照。
https://support.atlassian.com/bitbucket-cloud/docs/configure-bitbucket-pipelinesyml/
image: python:3.8
pipelines:
tags:
automation_test*: #ここで指定した名前通りにCommit IDにタグを追加すると、Bitbucket Pipelinesが稼働する
- step:
caches:
- pip
script:
- DIR_NAME=build_$(date "+%Y%m%d%H%M%S")
- BUCKET_NAME=<S3バケット名> #手順1で作成したS3バケットの名前に置き換える
- OBJECT_KEY=test #CFn Templateファイルを配置したいS3バケットのフォルダを指定する。予めフォルダを作成しておく必要はない
- mkdir -p ./${DIR_NAME}/${OBJECT_KEY}
- cp -p ./ec2-cfn.yml ./${DIR_NAME}/${OBJECT_KEY}
- pipe: atlassian/aws-s3-deploy:0.4.5
variables:
S3_BUCKET: '${BUCKET_NAME}'
LOCAL_PATH: './${DIR_NAME}'
3.2 Bitbucketのコンソール > Deployments にて「Add variables」をクリックする
3.3 Add Variables > Repositoryにて以下の変数を追加する
Name | Value |
---|---|
AWS_ACCESS_KEY_ID | 手順2-1-1の6で記録したアクセスキーID |
AWS_SECRET_ACCESS_KEY | 手順2-1-1の6で記録したシークレットアクセスキー |
AWS_DEFAULT_REGION | ap-north-east1 (S3バケット作成時のリージョンと合わせる) |
4. SSMドキュメント作成
4-1. Systems Managerのコンソール > ドキュメント にて「自己所有」をクリックし、「オートメーションを作成する」をクリックする
4-2. 「名前」に「Create-EC2」と入力し、「エディター」をクリックした上で、「編集」をクリックする
4-3. ポップアップの「OK」をクリックする
4-4. 「ドキュメントビルダー」に以下のymlを修正して入力し、「オートメーションを作成する」をクリックする
description: Launch EC2 Instance by creating CloudFormation stack
schemaVersion: '0.3'
assumeRole: '{{ AutomationAssumeRole }}'
parameters:
AutomationAssumeRole:
type: String
description: (Required) The ARN of the role that allows Automation to perform the actions on your behalf.
default: 'arn:aws:iam::{{global:ACCOUNT_ID}}:role/SSMServiceRole'
mainSteps:
- name: CreateStackForEC2
action: 'aws:createStack'
inputs:
TemplateURL: 'https://nkrk-automation-test.s3-ap-northeast-1.amazonaws.com/test/ec2-cfn.yml' #CFn Templateファイル配置先のS3のURLに置き換え
StackName: 'ec2-test'
詳細はSSMドキュメントでCloudFormation Stackを作成してみた参照。
5. Event Bridge
5-1. EventBridgeのコンソール > ルール にて、「ルールを作成」をクリックする。
5-2. 以下を指定して、「作成」をクリックする。
- 名前:「InvokeSSMAutomation_Create-EC2」を入力
- パターンを定義:「イベントパターン」を選択
- イベント一致パターン:「カスタムパターン」を選択し、イベントパターンに以下のJSONを修正して入力し、「保存」をクリック
{
"detail-type": [
"AWS API Call via CloudTrail"
],
"source": [
"aws.s3"
],
"detail": {
"eventSource": [
"s3.amazonaws.com"
],
"requestParameters": {
"bucketName": [
"<手順1で作成したS3バケット名に置き換え>"
],
"key": [
"<Bitbucketから受け取るCFn TemplateファイルのS3内のパスを指定。手順3-1のbitbucket-pipelines.ymlの指定内容と一致させる必要あり>"
]
},
"eventName": [
"PutObject",
"CompleteMultipartUpload",
"CopyObject"
]
}
}
- ターゲット:「SSM Automation」を選択
- ドキュメント:手順4で作成したSSMドキュメント「Create-EC2」を指定
- ロール:「この特定のリソースに対して新しいロールを作成する」を選択し、名前を「EventBridgeServiceRole」に変更
6. CloudTrail
6-1.「証跡の作成」をクリックする
6-2.設定を何も変更せずに「証跡の作成」をクリックする
6-3. 作成した証跡「management-events」をクリックする
6-4. 「データイベント」の「編集」をクリックする
6-5. 以下の内容を設定して、「変更の保存」をクリックする
- 「イベントタイプ」の「データイベント」にチェックを入れる
- 「現在および将来のすべてのS3バケット」の読み取りと書き込みのチェックを外す
- 「バケット/プレフィックス」に「xxxx-automation-test/test/ec2-cfn.yml」と入力し、読み取りのチェックを外す