この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
AWS CloudFormationでループ処理させたいな
こんにちは、のんピ(@non____97)です。
AWS CloudFormationを使って同じようなリソースを作る際にループ処理があれば良いなぁと思ったことはありますか? 私はあります。
ループ処理はマクロやAWS CDKを使えば対応可能ですが、以下のような要件がある場合は難しいです。
- AWS CloudFormationもしくは、AWS SAMを使う必要がある
- ループして作成されたリソース毎にパラメーターが異なる
ループ処理をするマクロの例
そこで、完全な力技ですが、シェルスクリプトを使って、入力されたパラメーターの数に応じてCloudFormationテンプレートファイルを動的に変化させてみました。
構成の紹介
ベースとなる環境は以下の記事で紹介したAWS Step FunctionsのCI/CD環境です。
CodeCommit上のリポジトリに変更が発生すると、CodeBuildでAWS SAMを使ってステートマシンやEventBridgeルールなどの各種リソースを作成しています。
CodeBuildでは以下の処理を行なっています。
- yqやAWS SAM CLIなどをインストールする
- 事前にS3バケットにアップロードしていた
pre_build_command.sh
とbuild_command.sh
をダウンロードする pre_build_command.sh
を実行する- リポジトリ上の設定ファイルからCron式やイベントパターン、タグなどの情報を読み込む
- 事前にS3バケットにアップロードしていた
sam-template.yml
をダウンロードする
build_command.sh
を実行する- 必要があればAssume Roleをし、一時認証情報を環境変数にセットする
- AWS SAM CLIで設定ファイルに記入されたリソースをデプロイする
yqはjqのYAML版のようなものです。yqはPython製とGo製の2つがあります。
Python製のyq
Go製のyq
今回はPython製のyqを使用しています。yqの操作方法については上述のリポジトリや以下記事をご覧ください。
Python製のyqの場合、基本的な操作はjqと同じなのでjqのマニュアルもオススメです。
変更前の各種ファイル
sam-template.yml
が使用するCloudFormationのテンプレートファイルです。このファイルは698行もあります。
CloudFormationのテンプレートファイルでは、EventBridgeルールに設定するCron式やイベントパターン、イベントバスをそれぞれ最大5つずつ設定できるようにしています。CloudFormationではループ処理ができないので、それぞれ5つ分のリソースを定義しており、かなりのボリュームがあります。
Cron式で制御するEventBridgeルールの定義
ScheduledRule1:
Condition: ExistsCron1
Type: AWS::Events::Rule
Properties:
Description: ScheduledRule
ScheduleExpression: !Sub ${Cron1}
State: ENABLED
Targets:
- Arn: !Ref StateMachine
Id: !GetAtt StateMachine.Name
RoleArn: !GetAtt ExecuteStateMachineRole.Arn
ScheduledRule2:
Condition: ExistsCron2
Type: AWS::Events::Rule
Properties:
Description: ScheduledRule
ScheduleExpression: !Sub ${Cron2}
State: ENABLED
Targets:
- Arn: !Ref StateMachine
Id: !GetAtt StateMachine.Name
RoleArn: !GetAtt ExecuteStateMachineRole.Arn
ScheduledRule3:
Condition: ExistsCron3
Type: AWS::Events::Rule
Properties:
Description: ScheduledRule
ScheduleExpression: !Sub ${Cron3}
State: ENABLED
Targets:
- Arn: !Ref StateMachine
Id: !GetAtt StateMachine.Name
RoleArn: !GetAtt ExecuteStateMachineRole.Arn
ScheduledRule4:
Condition: ExistsCron4
Type: AWS::Events::Rule
Properties:
Description: ScheduledRule
ScheduleExpression: !Sub ${Cron4}
State: ENABLED
Targets:
- Arn: !Ref StateMachine
Id: !GetAtt StateMachine.Name
RoleArn: !GetAtt ExecuteStateMachineRole.Arn
ScheduledRule5:
Condition: ExistsCron5
Type: AWS::Events::Rule
Properties:
Description: ScheduledRule
ScheduleExpression: !Sub ${Cron5}
State: ENABLED
Targets:
- Arn: !Ref StateMachine
Id: !GetAtt StateMachine.Name
RoleArn: !GetAtt ExecuteStateMachineRole.Arn
また、pre_build_command.sh
とbuild_command.sh
は以下の通りです。※ 長いので折り畳みます。
pre_build_command.sh
とbuild_command.sh
pre_build_command.sh
#!/bin/bash
# -x to display the command to be executed
set -x
bucket_name="$1"
sam_file_name="$2"
repository_path="$3"
deployment_destination_account_iam_role_arn=$(yq -r ".Settings.deployment_destination_account_iam_role_arn" ${repository_path}StateMachineSettings.yml)
echo deployment_destination_account_iam_role_arn : ${deployment_destination_account_iam_role_arn}
i=0
IFS=$'\n'; for cron in $(yq -rc ".Settings.event_bridge_rule[].cron? | select(.!=null)" ${repository_path}StateMachineSettings.yml); do
cron_array[$((i++))]=$(echo ${cron})
done
echo "${cron_array[@]}"
i=0
IFS=$'\n'; for event_pattern in $(yq -rc ".Settings.event_bridge_rule[].event_pattern? | select(.!=null)" ${repository_path}StateMachineSettings.yml); do
event_pattern_array[$((i++))]=$(echo ${event_pattern})
done
echo "${event_pattern_array[@]}"
i=0
IFS=$'\n'; for event_bus_arn in $(yq -rc ".Settings.event_bridge_rule[].event_bus_arn? | select(.!=null)" ${repository_path}StateMachineSettings.yml); do
event_bus_arn_array[$((i++))]=$(echo ${event_bus_arn})
done
echo "${event_bus_arn_array[@]}"
i=0
IFS=$'\n'; for target_event_bus_arn in $(yq -rc ".Settings.target_event_bus_arn[]" ${repository_path}StateMachineSettings.yml); do
target_event_bus_arn_array[$((i++))]=$(echo ${target_event_bus_arn})
done
echo "${target_event_bus_arn_array[@]}"
xray_tracing=$(yq -r ".Settings.xray_tracing" ${repository_path}StateMachineSettings.yml)
echo xray_tracing : ${xray_tracing}
iam_policy_document=$(yq -r ".Settings.iam_policy_document" ${repository_path}StateMachineSettings.yml)
echo iam_policy_document : ${iam_policy_document}
IFS=$'\n'; for tag in $(yq -rc ".Settings.tags[]" ${repository_path}StateMachineSettings.yml); do
key=$(echo ${tag} | jq -r .Key)
value=$(echo ${tag} | jq -r .Value)
tags_list+=$(echo "'${key}'"="'${value}' ")
done
echo tags_list : ${tags_list}
# Download the AWS SAM template file from the S3 bucket
aws s3 cp s3://${bucket_name}/${sam_file_name} ${sam_file_name}
cat StateMachineWorkFlow.asl.json
# Move the necessary files to the AWS SAM directory
mkdir -p sam-sfn/state_machine
cp -p ${repository_path}StateMachineWorkFlow.asl.json ./sam-sfn/state_machine/StateMachineWorkFlow.asl.json
cp -p ${sam_file_name} ./sam-sfn/${sam_file_name}
_cron_array=$(IFS=';'; echo "${cron_array[*]}")
_event_pattern_array=$(IFS=';'; echo "${event_pattern_array[*]}")
_event_bus_arn_array=$(IFS=';'; echo "${event_bus_arn_array[*]}")
_target_event_bus_arn_array=$(IFS=';'; echo "${target_event_bus_arn_array[*]}")
build_command.sh
#!/bin/bash
# -x to display the command to be executed
set -x
bucket_name="$1"
sam_file_name="$2"
state_machine_name="$3"
stack_unique_id="$4"
echo deployment_destination_account_iam_role_arn : ${deployment_destination_account_iam_role_arn}
IFS=';'; cron_array=(${_cron_array}); unset IFS
IFS=';'; event_pattern_array=(${_event_pattern_array}); unset IFS
IFS=';'; event_bus_arn_array=(${_event_bus_arn_array}); unset IFS
IFS=';'; target_event_bus_arn_array=(${_target_event_bus_arn_array}); unset IFS
echo "${cron_array[@]}"
echo "${event_pattern_array[@]}"
echo "${event_bus_arn_array[@]}"
echo "${target_event_bus_arn_array[@]}"
echo xray_tracing : ${xray_tracing}
echo iam_policy_document : ${iam_policy_document}
echo tags_list : ${tags_list}
cd sam-sfn
ls -l ./state_machine/StateMachineWorkFlow.asl.json
if [[ "${deployment_destination_account_iam_role_arn}" != null ]]; then
before=$(aws sts get-caller-identity | jq -r .Arn)
output=$(aws sts assume-role --role-arn ${deployment_destination_account_iam_role_arn} --role-session-name sam-deploy-session)
AWS_ACCESS_KEY_ID=$(echo ${output} | jq -r .Credentials.AccessKeyId)
AWS_SECRET_ACCESS_KEY=$(echo ${output} | jq -r .Credentials.SecretAccessKey)
AWS_SESSION_TOKEN=$(echo ${output} | jq -r .Credentials.SessionToken)
after=$(aws sts get-caller-identity | jq -r .Arn)
fi
AWS_ACCOUNT=$(aws sts get-caller-identity | jq -r .Account)
if [[ -s ./state_machine/StateMachineWorkFlow.asl.json ]]; then
sam build \
--template-file ${sam_file_name}
sam package \
--template-file ${sam_file_name} \
--s3-bucket ${bucket_name} \
--s3-prefix ${state_machine_name}_${AWS_ACCOUNT} \
--output-template-file output.yml
deploy_command=(sam deploy \
--template-file output.yml \
--s3-bucket ${bucket_name} \
--s3-prefix ${state_machine_name}_${AWS_ACCOUNT} \
--stack-name ${state_machine_name} \
--capabilities CAPABILITY_IAM \
--no-fail-on-empty-changeset \
--parameter-overrides \
StateMachineName=${state_machine_name} \
StackUniqueId=${stack_unique_id} \
Cron1="'${cron_array[0]}'" \
Cron2="'${cron_array[1]}'" \
Cron3="'${cron_array[2]}'" \
Cron4="'${cron_array[3]}'" \
Cron5="'${cron_array[4]}'" \
EventPattern1="'${event_pattern_array[0]}'" \
EventPattern2="'${event_pattern_array[1]}'" \
EventPattern3="'${event_pattern_array[2]}'" \
EventPattern4="'${event_pattern_array[3]}'" \
EventPattern5="'${event_pattern_array[4]}'" \
EventBusArn1="'${event_bus_arn_array[0]}'" \
EventBusArn2="'${event_bus_arn_array[1]}'" \
EventBusArn3="'${event_bus_arn_array[2]}'" \
EventBusArn4="'${event_bus_arn_array[3]}'" \
EventBusArn5="'${event_bus_arn_array[4]}'" \
TargetEventBusArn1="'${target_event_bus_arn_array[0]}'" \
TargetEventBusArn2="'${target_event_bus_arn_array[1]}'" \
TargetEventBusArn3="'${target_event_bus_arn_array[2]}'" \
TargetEventBusArn4="'${target_event_bus_arn_array[3]}'" \
TargetEventBusArn5="'${target_event_bus_arn_array[4]}'" \
XRayTracing=${xray_tracing} \
IamPolicyDocument="'${iam_policy_document}'")
if [[ -n "${tags_list}" ]]; then
"${deploy_command[@]}" \
--tags "'${tags_list}'"
else
"${deploy_command[@]}"
fi
aws cloudformation describe-stacks --stack-name ${state_machine_name}
else
# If the ASL file is empty, delete the stack
yes | \
sam delete \
--stack-name ${state_machine_name}
fi
このようなテンプレートファイルとシェルスクリプトだと、各リソースを5つ以上設定できるようにしたい場合にテンプレートファイルもシェルスクリプトも変更する必要があるなどメンテナンスも大変です。
これを次章以降、設定ファイルに入力されたパラメーターの数に応じて、CloudFormationテンプレートファイルを動的に変化するように変更します。
シェルスクリプト魔改造してみた
CloudFormationテンプレートファイル
シェルスクリプトを魔改造して、入力されたパラメーターの数に応じてCloudFormationテンプレートファイルを動的に変化するようにします。
まず、テンプレートファイルから修正します。
テンプレートファイルないには繰り返し定義されているものと、そうでないものとの2種類があります。
これを区別するために、ループ処理の対象にIndex
から始まる区切り文字を付与します。
例) 入力されたCron式の数によって動的に変化するもの : CronIndexCron
,ScheduledRuleIndexCron
また、yqではYAMLのタグを保持できません。そのため、ループ処理させたい箇所の組み込み関数は!Ref
や!Sub
など短縮記法ではなく、Ref:
やFn::Sub:
で定義します。
行数は234行とかなり減りました。
実際のテンプレートファイルは以下の通りです。
sam-template.yml
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: >
sfn-sam
Sample SAM Template for sfn-sam
Parameters:
StateMachineName:
Description: Please type the Step Functions State Machine Name.
Type: String
Default: sfn-sam-state-machine
StackUniqueId:
Description: Please type the Stack unique ID.
Type: String
Default: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
CronIndexCron:
Description: Please type the Cron.
Type: String
Default: "null"
EventPatternIndexEventPattern:
Description: Please type the EventPattern.
Type: String
Default: "null"
EventBusArnIndexEventBusArn:
Description: Please type the Event Bus ARN.
Type: String
Default: "null"
TargetEventBusArnIndexTargetEventBusArn:
Description: Please type the target event bus arn after execution.
Type: String
Default: "null"
XRayTracing:
Description: Please type the AWS X-Ray trace to enable or not.
Type: String
Default: false
AllowedValues:
- true
- false
IamPolicyDocument:
Description: Please type the IAM Policy Document.
Type: String
Conditions:
IsEnabledXRayTracing: !Equals
- !Sub ${XRayTracing}
- true
ExistsIamPolicyDocument: !Not
- !Equals
- !Sub ${IamPolicyDocument}
- "null"
Resources:
StateMachine:
Type: AWS::Serverless::StateMachine
Properties:
Name: !Sub ${StateMachineName}
DefinitionUri: state_machine/StateMachineWorkFlow.asl.json
Role: !GetAtt StateMachineRole.Arn
Logging:
Level: ALL
IncludeExecutionData: True
Destinations:
- CloudWatchLogsLogGroup:
LogGroupArn: !GetAtt StateMachineLogGroup.Arn
Tracing:
Enabled: !Sub ${XRayTracing}
StateMachineLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName : !Sub '/aws/vendedlogs/states/${StateMachineName}-${StackUniqueId}-Logs'
RetentionInDays: 731
StateMachineRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- states.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyDocument: {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogDelivery",
"logs:GetLogDelivery",
"logs:UpdateLogDelivery",
"logs:DeleteLogDelivery",
"logs:ListLogDeliveries",
"logs:PutResourcePolicy",
"logs:DescribeResourcePolicies",
"logs:DescribeLogGroups"
],
"Resource": "*"
}
]
}
PolicyName: CloudWatchLogsDeliveryFullAccessPolicy
- !If
- IsEnabledXRayTracing
- PolicyDocument: {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"xray:PutTraceSegments",
"xray:PutTelemetryRecords",
"xray:GetSamplingRules",
"xray:GetSamplingTargets"
],
"Resource": [
"*"
]
}
]
}
PolicyName: XRayAccessPolicy
- !Ref AWS::NoValue
- !If
- ExistsIamPolicyDocument
- PolicyDocument: !Sub ${IamPolicyDocument}
PolicyName: IamPolicyForExecuteStateMachine
- !Ref AWS::NoValue
ScheduledRuleIndexCron:
Type: AWS::Events::Rule
Properties:
Description: ScheduledRule
ScheduleExpression:
Fn::Sub: ${CronIndexCron}
State: ENABLED
Targets:
- Arn:
Ref: StateMachine
Id:
Fn::GetAtt: StateMachine.Name
RoleArn:
Fn::GetAtt: ExecuteStateMachineRole.Arn
EventPatternRuleIndexEventPattern:
Type: AWS::Events::Rule
Properties:
Description: EventPatternRule
EventPattern:
Fn::Sub: ${EventPatternIndexEventPattern}
EventBusName:
Fn::Sub: ${EventBusArnIndexEventBusArn}
State: ENABLED
Targets:
- Arn:
Ref: StateMachine
Id:
Fn::GetAtt: StateMachine.Name
RoleArn:
Fn::GetAtt: ExecuteStateMachineRole.Arn
ExecuteStateMachineRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- events.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: states_StartExecution
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- states:StartExecution
Resource:
- !Ref StateMachine
TargetEventBusArnRuleIndexTargetEventBusArn:
Type: AWS::Events::Rule
Properties:
Description: TargetEventBusArnRule
EventPattern: {
"source": ["aws.states"],
"detail-type": ["Step Functions Execution Status Change"],
"detail": {
"status": ["SUCCEEDED"],
"stateMachineArn": [Ref: StateMachine]
}
}
State: ENABLED
Targets:
- Arn:
Ref: TargetEventBusArnIndexTargetEventBusArn
Id: TargetEventBusArnIndexTargetEventBusArn
RoleArn:
Fn::GetAtt: InvokeEventBusRoleIndexTargetEventBusArn.Arn
InvokeEventBusRoleIndexTargetEventBusArn:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- events.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: InvokeEventBus
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- events:PutEvents
Resource:
- Ref: TargetEventBusArnIndexTargetEventBusArn
シェルスクリプト
build_command.sh
CloudFormationのテンプレートファイルの修正が終わったら、次はbuild_command.sh
の修正です。
build_command.sh
はsam build
やsam deploy
などAWS SAM CLIを実行しています。
sam deploy
時にテンプレートファイルに渡すパラメーターの数が動的に変化する必要があります。
そのため、CloudFormationのテンプレートファイルと同様に、ループ処理の対象にIndex
から始まる区切り文字を付与します。
実際のコードは以下の通りです。
build_command.sh
#!/bin/bash
# -x to display the command to be executed
set -x
bucket_name="$1"
sam_file_name="$2"
state_machine_name="$3"
stack_unique_id="$4"
# Check variables
echo deployment_destination_account_iam_role_arn : ${deployment_destination_account_iam_role_arn}
IFS=';'; cron_array=(${tmp_cron_array}); unset IFS
IFS=';'; event_pattern_array=(${tmp_event_pattern_array}); unset IFS
IFS=';'; event_bus_arn_array=(${tmp_event_bus_arn_array}); unset IFS
IFS=';'; target_event_bus_arn_array=(${tmp_target_event_bus_arn_array}); unset IFS
echo "${cron_array[@]}"
echo "${event_pattern_array[@]}"
echo "${event_bus_arn_array[@]}"
echo "${target_event_bus_arn_array[@]}"
echo xray_tracing : ${xray_tracing}
echo iam_policy_document : ${iam_policy_document}
echo tags_list : ${tags_list}
# Change to the directory where AWS SAM CLI is to be executed
cd sam-sfn
# Check that the State Machine workflow exists
ls -l ./state_machine/StateMachineWorkFlow.asl.json
# Assume Role if the deployment destination is a different AWS account
if [[ "${deployment_destination_account_iam_role_arn}" != null ]]; then
before=$(aws sts get-caller-identity | jq -r .Arn)
output=$(aws sts assume-role --role-arn ${deployment_destination_account_iam_role_arn} --role-session-name sam-deploy-session)
AWS_ACCESS_KEY_ID=$(echo ${output} | jq -r .Credentials.AccessKeyId)
AWS_SECRET_ACCESS_KEY=$(echo ${output} | jq -r .Credentials.SecretAccessKey)
AWS_SESSION_TOKEN=$(echo ${output} | jq -r .Credentials.SessionToken)
after=$(aws sts get-caller-identity | jq -r .Arn)
fi
AWS_ACCOUNT=$(aws sts get-caller-identity | jq -r .Account)
# If the "StateMachineWorkFlow.asl.json" is not empty, then Deploying resources with AWS SAM CLI
if [[ -s ./state_machine/StateMachineWorkFlow.asl.json ]]; then
sam build \
--template-file ${sam_file_name}
sam package \
--template-file ${sam_file_name} \
--s3-bucket ${bucket_name} \
--s3-prefix ${state_machine_name}_${AWS_ACCOUNT} \
--output-template-file output.yml
deploy_command=(sam deploy \
--template-file output.yml \
--s3-bucket ${bucket_name} \
--s3-prefix ${state_machine_name}_${AWS_ACCOUNT} \
--stack-name ${state_machine_name} \
--capabilities CAPABILITY_IAM \
--no-fail-on-empty-changeset \
--parameter-overrides \
StateMachineName=${state_machine_name} \
StackUniqueId=${stack_unique_id} \
CronIndexCron="'${cron_array[IndexCron]}'" \
EventPatternIndexEventPattern="'${event_pattern_array[IndexEventPattern]}'" \
EventBusArnIndexEventBusArn="'${event_bus_arn_array[IndexEventBusArn]}'" \
TargetEventBusArnIndexTargetEventBusArn="'${target_event_bus_arn_array[IndexTargetEventBusArn]}'" \
XRayTracing=${xray_tracing} \
IamPolicyDocument="'${iam_policy_document}'")
# If tags are specified, add an option to specify tags
if [[ -n "${tags_list}" ]]; then
"${deploy_command[@]}" \
--tags "'${tags_list}'"
else
"${deploy_command[@]}"
fi
# After deployment, the stack is displayed
aws cloudformation describe-stacks --stack-name ${state_machine_name}
else
# If the "StateMachineWorkFlow.asl.json" is empty, delete the stack
yes | \
sam delete \
--stack-name ${state_machine_name}
fi
pre_build_command.sh
最後にpre_build_command.sh
を修正します。
pre_build_command.sh
内で行なっている、リソース毎の処理の流れは以下の通りです。
- テンプレートファイル上で繰り返し定義したい内容を変数に保存する
- テンプレートファイル上の繰り返し定義したい内容を削除する
- 設定ファイルに入力されたパラメーターの数分、次の処理を繰り返す
build_dcommand.sh
上のIndex
から始まる区切り文字を含む行をコピーして、次の行に貼り付ける- 変数に保存していた繰り返し定義したい内容をテンプレートファイルに追加する。その際、
Index
から始まる区切り文字をループした回数に置換する。
- 例) 区切り文字が
IndexCron
でループ回数が0
回の場合は、IndexCron
を0
に置換する。
- 配列に設定ファイルに入力されたパラメーターを保存する
build_dcommand.sh
上のIndex
から始まる区切り文字を含む行を削除する
これを各リソース毎におこないます。
ここではsed
とyq
が大活躍します。
sedはパターンスペースとホールドスペースの動きを理解することが必要です。その際、ITmediaさんの以下記事が非常に参考になりました。
実際のコードは以下の通りです。
pre_build_command.sh
#!/bin/bash
# -x to display the command to be executed
set -x
bucket_name="$1"
sam_file_name="$2"
repository_path="$3"
# Download the AWS SAM template file from the S3 bucket
aws s3 cp s3://${bucket_name}/${sam_file_name} ${sam_file_name}
# Display State Machine workflow
cat StateMachineWorkFlow.asl.json
# Move the necessary files to the AWS SAM directory
mkdir -p sam-sfn/state_machine
cp -p ${repository_path}StateMachineWorkFlow.asl.json ./sam-sfn/state_machine/StateMachineWorkFlow.asl.json
cp -p ${sam_file_name} ./sam-sfn/${sam_file_name}
# Get the ARN of the IAM role to be used when deploying
deployment_destination_account_iam_role_arn=$(yq -r ".Settings.deployment_destination_account_iam_role_arn" ${repository_path}StateMachineSettings.yml)
echo deployment_destination_account_iam_role_arn : ${deployment_destination_account_iam_role_arn}
# Get the Cron expression of the EventBridge rule associated with the State Machine
# Template delimiter
delimiter=IndexCron
# Get the template part of Parameters and Resources
template_parameter=$(yq -r ".Parameters.Cron${delimiter}" ./sam-sfn/${sam_file_name})
template_resource=$(yq -r ".Resources.ScheduledRule${delimiter}" ./sam-sfn/${sam_file_name})
# Remove the template parts of Parameters and Resources from the template file
cat ./sam-sfn/${sam_file_name} | \
yq 'del(.Parameters.Cron'${delimiter}')' -Y | \
yq 'del(.Resources.ScheduledRule'${delimiter}')' -Y | \
tee ./sam-sfn/tmp_${sam_file_name}
mv ./sam-sfn/tmp_${sam_file_name} ./sam-sfn/${sam_file_name}
i=0
IFS=$'\n'; for cron in $(yq -rc ".Settings.event_bridge_rule[].cron? | select(.!=null)" ${repository_path}StateMachineSettings.yml); do
# Copy and paste the line with the delimiter on "build_command.sh" to the next line
sed -i -e "/${delimiter}/{ h; s/${delimiter}/${i}/g; G; }" build_command.sh
# Add Parameters and Resources to the template file
cat ./sam-sfn/${sam_file_name} | \
yq --argjson object "${template_parameter}" '.Parameters += {Cron'${i}': $object}' -Y | \
yq --argjson object "${template_resource}" '.Resources += {ScheduledRule'${i}': $object}' -Y | \
sed -e 's/Cron'${delimiter}'/Cron'${i}'/' | \
tee ./sam-sfn/tmp_${sam_file_name}
mv ./sam-sfn/tmp_${sam_file_name} ./sam-sfn/${sam_file_name}
# Store values entered in the configuration file as an array
cron_array[$((i++))]=$(echo ${cron})
done
# Delete lines with delimiters on "build_command.sh
sed -i "/${delimiter}/d" build_command.sh
# Check values in an array
echo "${cron_array[@]}"
# Get the ARN of the Event Bus of the EventBridge rule event pattern associated with the State Machine
# Template delimiter
delimiter=IndexEventBusArn
i=0
IFS=$'\n'; for event_bus_arn in $(yq -rc ".Settings.event_bridge_rule[].event_bus_arn? | select(.!=null)" ${repository_path}StateMachineSettings.yml); do
# Copy and paste the line with the delimiter on "build_command.sh" to the next line
sed -i -e "/${delimiter}/{ h; s/${delimiter}/${i}/g; G; }" build_command.sh
# Store values entered in the configuration file as an array
event_bus_arn_array[$((i++))]=$(echo ${event_bus_arn})
done
# Delete lines with delimiters on "build_command.sh
sed -i "/${delimiter}/d" build_command.sh
# Check values in an array
echo "${event_bus_arn_array[@]}"
# Get the event pattern of the EventBridge rule associated with the State Machine
# Template delimiter
delimiter_event_pattern=IndexEventPattern
delimiter_event_bus_arn=IndexEventBusArn
# Get the template part of Parameters and Resources
template_parameter_event_pattern=$(yq -r ".Parameters.EventPattern${delimiter_event_pattern}" ./sam-sfn/${sam_file_name})
template_parameter_event_bus_arn=$(yq -r ".Parameters.EventBusArn${delimiter_event_bus_arn}" ./sam-sfn/${sam_file_name})
template_resource_event_pattern=$(yq -r ".Resources.EventPatternRule${delimiter_event_pattern}" ./sam-sfn/${sam_file_name})
# Remove the template parts of Parameters and Resources from the template file
cat ./sam-sfn/${sam_file_name} | \
yq 'del(.Parameters.EventPattern'${delimiter_event_pattern}')' -Y | \
yq 'del(.Parameters.EventBusArn'${delimiter_event_bus_arn}')' -Y | \
yq 'del(.Resources.EventPatternRule'${delimiter_event_pattern}')' -Y | \
tee ./sam-sfn/tmp_${sam_file_name}
mv ./sam-sfn/tmp_${sam_file_name} ./sam-sfn/${sam_file_name}
i=0
IFS=$'\n'; for event_pattern in $(yq -rc ".Settings.event_bridge_rule[].event_pattern? | select(.!=null)" ${repository_path}StateMachineSettings.yml); do
# Copy and paste the line with the delimiter on "build_command.sh" to the next line
sed -i -e "/${delimiter_event_pattern}/{ h; s/${delimiter_event_pattern}/${i}/g; G; }" build_command.sh
# Add Parameters and Resources to the template file
cat ./sam-sfn/${sam_file_name} | \
yq --argjson object "${template_parameter_event_pattern}" '.Parameters += {EventPattern'${i}': $object}' -Y | \
yq --argjson object "${template_parameter_event_bus_arn}" '.Parameters += {EventBusArn'${i}': $object}' -Y | \
yq --argjson object "${template_resource_event_pattern}" '.Resources += {EventPatternRule'${i}': $object}' -Y | \
sed -e 's/EventPattern'${delimiter_event_pattern}'/EventPattern'${i}'/' | \
sed -e 's/EventBusArn'${delimiter_event_bus_arn}'/EventBusArn'${i}'/' | \
tee ./sam-sfn/tmp_${sam_file_name}
mv ./sam-sfn/tmp_${sam_file_name} ./sam-sfn/${sam_file_name}
# Store values entered in the configuration file as an array
event_pattern_array[$((i++))]=$(echo ${event_pattern})
done
# Delete lines with delimiters on "build_command.sh
sed -i "/${delimiter_event_pattern}/d" build_command.sh
# Check values in an array
echo "${event_pattern_array[@]}"
# Get the ARN of the Event Bus that puts an event when the State Machine finishes successfully
# Template delimiter
delimiter=IndexTargetEventBusArn
# Get the template part of Parameters and Resources
template_parameter_target_event_bus_arn=$(yq -r ".Parameters.TargetEventBusArn${delimiter}" ./sam-sfn/${sam_file_name})
template_resource_target_event_bus_arn_rule=$(yq -r ".Resources.TargetEventBusArnRule${delimiter}" ./sam-sfn/${sam_file_name})
template_resource_target_event_bus_arn_role=$(yq -r ".Resources.InvokeEventBusRole${delimiter}" ./sam-sfn/${sam_file_name})
# Remove the template parts of Parameters and Resources from the template file
cat ./sam-sfn/${sam_file_name} | \
yq 'del(.Parameters.TargetEventBusArn'${delimiter}')' -Y | \
yq 'del(.Resources.TargetEventBusArnRule'${delimiter}')' -Y | \
yq 'del(.Resources.InvokeEventBusRole'${delimiter}')' -Y | \
tee ./sam-sfn/tmp_${sam_file_name}
mv ./sam-sfn/tmp_${sam_file_name} ./sam-sfn/${sam_file_name}
i=0
IFS=$'\n'; for target_event_bus_arn in $(yq -rc ".Settings.target_event_bus_arn[]" ${repository_path}StateMachineSettings.yml); do
# Copy and paste the line with the delimiter on "build_command.sh" to the next line
sed -i -e "/${delimiter}/{ h; s/${delimiter}/${i}/g; G; }" build_command.sh
# Add Parameters and Resources to the template file
cat ./sam-sfn/${sam_file_name} | \
yq --argjson object "${template_parameter_target_event_bus_arn}" '.Parameters += {TargetEventBusArn'${i}': $object}' -Y | \
yq --argjson object "${template_resource_target_event_bus_arn_rule}" '.Resources += {TargetEventBusArnRule'${i}': $object}' -Y | \
yq --argjson object "${template_resource_target_event_bus_arn_role}" '.Resources += {InvokeEventBusRole'${i}': $object}' -Y | \
sed -e 's/TargetEventBusArn'${delimiter}'/TargetEventBusArn'${i}'/g' | \
sed -e 's/InvokeEventBusRole'${delimiter}'/InvokeEventBusRole'${i}'/g' | \
tee ./sam-sfn/tmp_${sam_file_name}
mv ./sam-sfn/tmp_${sam_file_name} ./sam-sfn/${sam_file_name}
# Store values entered in the configuration file as an array
target_event_bus_arn_array[$((i++))]=$(echo ${target_event_bus_arn})
done
# Delete lines with delimiters on "build_command.sh
sed -i "/${delimiter}/d" build_command.sh
# Check values in an array
echo "${target_event_bus_arn_array[@]}"
# Get whether State Machine enables X-Ray tracing
xray_tracing=$(yq -r ".Settings.xray_tracing" ${repository_path}StateMachineSettings.yml)
echo xray_tracing : ${xray_tracing}
# Get the IAM Policy document to attach to the IAM Role associated with the State Machine
iam_policy_document=$(yq -r ".Settings.iam_policy_document" ${repository_path}StateMachineSettings.yml)
echo iam_policy_document : ${iam_policy_document}
# Get tags to attach to resources created by CloudFormation
IFS=$'\n'; for tag in $(yq -rc ".Settings.tags[]" ${repository_path}StateMachineSettings.yml); do
key=$(echo ${tag} | jq -r .Key)
value=$(echo ${tag} | jq -r .Value)
tags_list+=$(echo "'${key}'"="'${value}' ")
done
echo tags_list : ${tags_list}
# Change arrays temporarily to strings
tmp_cron_array=$(IFS=';'; echo "${cron_array[*]}")
tmp_event_pattern_array=$(IFS=';'; echo "${event_pattern_array[*]}")
tmp_event_bus_arn_array=$(IFS=';'; echo "${event_bus_arn_array[*]}")
tmp_target_event_bus_arn_array=$(IFS=';'; echo "${target_event_bus_arn_array[*]}")
デプロイしてみた
それでは実際に修正したテンプレートファイル、シェルスクリプトを使ってデプロイしてみます。
設定ファイル(StateMachineSettings.yml
)は以下のように入力しました。
Settings:
deployment_destination_account_iam_role_arn:
event_bridge_rule:
- cron: cron(15 11 * * ? *)
- cron: cron(15 23 * * ? *)
- event_pattern: {
"source": ["aws.states"],
"detail-type": ["Step Functions Execution Status Change"],
"detail": {
"status": ["SUCCEEDED"],
"stateMachineArn": ["arn:aws:states:us-east-1:<AWSアカウントID>:stateMachine:nonpi"]
}
}
event_bus_arn: arn:aws:events:us-east-1:<AWSアカウントID>:event-bus/default
- event_pattern: {
"source": ["aws.states"],
"detail-type": ["Step Functions Execution Status Change"],
"detail": {
"status": ["SUCCEEDED"],
"stateMachineArn": ["arn:aws:states:us-east-1:<AWSアカウントID>:stateMachine:aaaaaaaaaaaaabbbbbccccdddefg"]
}
}
event_bus_arn: arn:aws:events:us-east-1:<AWSアカウントID>:event-bus/default
target_event_bus_arn:
- arn:aws:events:us-east-1:<AWSアカウントID>:event-bus/StateMachineEventBus
- arn:aws:events:us-east-2:<AWSアカウントID>:event-bus/default
xray_tracing: true
iam_policy_document: {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:stopInstances"
],
"Resource": [
"arn:aws:ec2:us-east-1:<AWSアカウントID>:instance/i-0a3b8c7357a46b183",
"arn:aws:ec2:us-east-1:<AWSアカウントID>:instance/i-0a3b8c7357a46dddd",
"arn:aws:ec2:us-east-1:<AWSアカウントID>:instance/i-0cb6e117c54a102f4"
]
}
]
}
tags:
- Key: System Name
Value: System 2
- Key: Environment
Value: production
- Key: Auther
Value: non-97
- Key: Corporate
Value: Classmethod
コミットしてしばらくすると、スタックが作成されていました。作成されたスタックの情報をマネージメントコンソールから確認すると、設定ファイルに入力した通りのタグが付与されていることが確認できました。
次に、リソース
タブをクリックすると、設定ファイルに入力した通りのリソースが作成されていました。
続いて、テンプレート
タブをクリックして、このスタックのテンプレートファイルを確認します。
Cron式を2つ入力したので、Cron0
やCron1
、ScheduledRule0
、ScheduledRule1
と入力されたCron式の数に応じてテンプレートファイルが変化していることが確認できます。
CloudFormationのテンプレートファイル
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: 'sfn-sam
Sample SAM Template for sfn-sam
'
Parameters:
StateMachineName:
Description: Please type the Step Functions State Machine Name.
Type: String
Default: sfn-sam-state-machine
StackUniqueId:
Description: Please type the Stack unique ID.
Type: String
Default: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
XRayTracing:
Description: Please type the AWS X-Ray trace to enable or not.
Type: String
Default: false
AllowedValues:
- true
- false
IamPolicyDocument:
Description: Please type the IAM Policy Document.
Type: String
Cron0:
Description: Please type the Cron.
Type: String
Default: 'null'
Cron1:
Description: Please type the Cron.
Type: String
Default: 'null'
EventPattern0:
Description: Please type the EventPattern.
Type: String
Default: 'null'
EventBusArn0:
Description: Please type the Event Bus ARN.
Type: String
Default: 'null'
EventPattern1:
Description: Please type the EventPattern.
Type: String
Default: 'null'
EventBusArn1:
Description: Please type the Event Bus ARN.
Type: String
Default: 'null'
TargetEventBusArn0:
Description: Please type the target event bus arn after execution.
Type: String
Default: 'null'
TargetEventBusArn1:
Description: Please type the target event bus arn after execution.
Type: String
Default: 'null'
Conditions:
IsEnabledXRayTracing:
Fn::Equals:
- Fn::Sub: ${XRayTracing}
- true
ExistsIamPolicyDocument:
Fn::Not:
- Fn::Equals:
- Fn::Sub: ${IamPolicyDocument}
- 'null'
Resources:
StateMachine:
Type: AWS::Serverless::StateMachine
Properties:
Name:
Fn::Sub: ${StateMachineName}
DefinitionUri:
Bucket: artifactbucketstack-artifactbucket7410c9ef-mpyyu6apfoxt
Key: StateMachineTest002_984900217833/b1d331f56a2e20e252e7d04171aecc16
Role:
Fn::GetAtt:
- StateMachineRole
- Arn
Logging:
Level: ALL
IncludeExecutionData: true
Destinations:
- CloudWatchLogsLogGroup:
LogGroupArn:
Fn::GetAtt:
- StateMachineLogGroup
- Arn
Tracing:
Enabled:
Fn::Sub: ${XRayTracing}
StateMachineLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName:
Fn::Sub: /aws/vendedlogs/states/${StateMachineName}-${StackUniqueId}-Logs
RetentionInDays: 731
StateMachineRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- states.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogDelivery
- logs:GetLogDelivery
- logs:UpdateLogDelivery
- logs:DeleteLogDelivery
- logs:ListLogDeliveries
- logs:PutResourcePolicy
- logs:DescribeResourcePolicies
- logs:DescribeLogGroups
Resource: '*'
PolicyName: CloudWatchLogsDeliveryFullAccessPolicy
- Fn::If:
- IsEnabledXRayTracing
- PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- xray:PutTraceSegments
- xray:PutTelemetryRecords
- xray:GetSamplingRules
- xray:GetSamplingTargets
Resource:
- '*'
PolicyName: XRayAccessPolicy
- Ref: AWS::NoValue
- Fn::If:
- ExistsIamPolicyDocument
- PolicyDocument:
Fn::Sub: ${IamPolicyDocument}
PolicyName: IamPolicyForExecuteStateMachine
- Ref: AWS::NoValue
ExecuteStateMachineRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- events.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: states_StartExecution
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- states:StartExecution
Resource:
- Ref: StateMachine
ScheduledRule0:
Type: AWS::Events::Rule
Properties:
Description: ScheduledRule
ScheduleExpression:
Fn::Sub: ${Cron0}
State: ENABLED
Targets:
- Arn:
Ref: StateMachine
Id:
Fn::GetAtt: StateMachine.Name
RoleArn:
Fn::GetAtt: ExecuteStateMachineRole.Arn
ScheduledRule1:
Type: AWS::Events::Rule
Properties:
Description: ScheduledRule
ScheduleExpression:
Fn::Sub: ${Cron1}
State: ENABLED
Targets:
- Arn:
Ref: StateMachine
Id:
Fn::GetAtt: StateMachine.Name
RoleArn:
Fn::GetAtt: ExecuteStateMachineRole.Arn
EventPatternRule0:
Type: AWS::Events::Rule
Properties:
Description: EventPatternRule
EventPattern:
Fn::Sub: ${EventPattern0}
EventBusName:
Fn::Sub: ${EventBusArn0}
State: ENABLED
Targets:
- Arn:
Ref: StateMachine
Id:
Fn::GetAtt: StateMachine.Name
RoleArn:
Fn::GetAtt: ExecuteStateMachineRole.Arn
EventPatternRule1:
Type: AWS::Events::Rule
Properties:
Description: EventPatternRule
EventPattern:
Fn::Sub: ${EventPattern1}
EventBusName:
Fn::Sub: ${EventBusArn1}
State: ENABLED
Targets:
- Arn:
Ref: StateMachine
Id:
Fn::GetAtt: StateMachine.Name
RoleArn:
Fn::GetAtt: ExecuteStateMachineRole.Arn
TargetEventBusArnRule0:
Type: AWS::Events::Rule
Properties:
Description: TargetEventBusArnRule
EventPattern:
source:
- aws.states
detail-type:
- Step Functions Execution Status Change
detail:
status:
- SUCCEEDED
stateMachineArn:
- Ref: StateMachine
State: ENABLED
Targets:
- Arn:
Ref: TargetEventBusArn0
Id: TargetEventBusArn0
RoleArn:
Fn::GetAtt: InvokeEventBusRole0.Arn
InvokeEventBusRole0:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- events.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: InvokeEventBus
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- events:PutEvents
Resource:
- Ref: TargetEventBusArn0
TargetEventBusArnRule1:
Type: AWS::Events::Rule
Properties:
Description: TargetEventBusArnRule
EventPattern:
source:
- aws.states
detail-type:
- Step Functions Execution Status Change
detail:
status:
- SUCCEEDED
stateMachineArn:
- Ref: StateMachine
State: ENABLED
Targets:
- Arn:
Ref: TargetEventBusArn1
Id: TargetEventBusArn1
RoleArn:
Fn::GetAtt: InvokeEventBusRole1.Arn
InvokeEventBusRole1:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- events.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: InvokeEventBus
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- events:PutEvents
Resource:
- Ref: TargetEventBusArn1
設定ファイルを編集して、もう一度デプロイしてみます。
cron(11 20 * * ? *)
というCron式のEventBridgeルールを追加で作成するように、設定ファイル(StateMachineSettings.yml
)を更新しました。
StateMachineSettings.yml
Settings:
deployment_destination_account_iam_role_arn:
event_bridge_rule:
- cron: cron(15 11 * * ? *)
- cron: cron(15 23 * * ? *)
- cron: cron(11 20 * * ? *)
- event_pattern: {
"source": ["aws.states"],
"detail-type": ["Step Functions Execution Status Change"],
"detail": {
"status": ["SUCCEEDED"],
"stateMachineArn": ["arn:aws:states:us-east-1:984900217833:stateMachine:nonpi"]
}
}
event_bus_arn: arn:aws:events:us-east-1:984900217833:event-bus/default
- event_pattern: {
"source": ["aws.states"],
"detail-type": ["Step Functions Execution Status Change"],
"detail": {
"status": ["SUCCEEDED"],
"stateMachineArn": ["arn:aws:states:us-east-1:984900217833:stateMachine:aaaaaaaaaaaaabbbbbccccdddefg"]
}
}
event_bus_arn: arn:aws:events:us-east-1:984900217833:event-bus/default
target_event_bus_arn:
- arn:aws:events:us-east-1:984900217833:event-bus/StateMachineEventBus
- arn:aws:events:us-east-2:984900217833:event-bus/default
xray_tracing: true
iam_policy_document: {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:stopInstances"
],
"Resource": [
"arn:aws:ec2:us-east-1:984900217833:instance/i-0a3b8c7357a46b183",
"arn:aws:ec2:us-east-1:984900217833:instance/i-0a3b8c7357a46dddd",
"arn:aws:ec2:us-east-1:984900217833:instance/i-0cb6e117c54a102f4"
]
}
]
}
tags:
- Key: System Name
Value: System 2
- Key: Environment
Value: production
- Key: Auther
Value: non-97
- Key: Corporate
Value: Classmethod
コミットしてしばらくすると、スタックが更新されていました。更新されたスタックのリソース一覧をマネージメントコンソールから確認すると、EventBridgeルールが追加されていることが確認できました。
次に、パラメータ
タブをクリックすると、設定ファイルに入力した通りのパラメーターが指定されていました。
続いて、テンプレート
タブをクリックして、このスタックのテンプレートファイルを確認します。
Cron式を3つ入力したので、Cron0
やCron1
、Cron2
、ScheduledRule0
、ScheduledRule1
、ScheduledRule2
と入力されたCron式の数に応じてテンプレートファイルが変化していることが確認できます。
リソース追加後のCloudFormationのテンプレートファイル
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: 'sfn-sam
Sample SAM Template for sfn-sam
'
Parameters:
StateMachineName:
Description: Please type the Step Functions State Machine Name.
Type: String
Default: sfn-sam-state-machine
StackUniqueId:
Description: Please type the Stack unique ID.
Type: String
Default: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
XRayTracing:
Description: Please type the AWS X-Ray trace to enable or not.
Type: String
Default: false
AllowedValues:
- true
- false
IamPolicyDocument:
Description: Please type the IAM Policy Document.
Type: String
Cron0:
Description: Please type the Cron.
Type: String
Default: 'null'
Cron1:
Description: Please type the Cron.
Type: String
Default: 'null'
Cron2:
Description: Please type the Cron.
Type: String
Default: 'null'
EventPattern0:
Description: Please type the EventPattern.
Type: String
Default: 'null'
EventBusArn0:
Description: Please type the Event Bus ARN.
Type: String
Default: 'null'
EventPattern1:
Description: Please type the EventPattern.
Type: String
Default: 'null'
EventBusArn1:
Description: Please type the Event Bus ARN.
Type: String
Default: 'null'
TargetEventBusArn0:
Description: Please type the target event bus arn after execution.
Type: String
Default: 'null'
TargetEventBusArn1:
Description: Please type the target event bus arn after execution.
Type: String
Default: 'null'
Conditions:
IsEnabledXRayTracing:
Fn::Equals:
- Fn::Sub: ${XRayTracing}
- true
ExistsIamPolicyDocument:
Fn::Not:
- Fn::Equals:
- Fn::Sub: ${IamPolicyDocument}
- 'null'
Resources:
StateMachine:
Type: AWS::Serverless::StateMachine
Properties:
Name:
Fn::Sub: ${StateMachineName}
DefinitionUri:
Bucket: artifactbucketstack-artifactbucket7410c9ef-mpyyu6apfoxt
Key: StateMachineTest002_984900217833/b1d331f56a2e20e252e7d04171aecc16
Role:
Fn::GetAtt:
- StateMachineRole
- Arn
Logging:
Level: ALL
IncludeExecutionData: true
Destinations:
- CloudWatchLogsLogGroup:
LogGroupArn:
Fn::GetAtt:
- StateMachineLogGroup
- Arn
Tracing:
Enabled:
Fn::Sub: ${XRayTracing}
StateMachineLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName:
Fn::Sub: /aws/vendedlogs/states/${StateMachineName}-${StackUniqueId}-Logs
RetentionInDays: 731
StateMachineRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- states.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogDelivery
- logs:GetLogDelivery
- logs:UpdateLogDelivery
- logs:DeleteLogDelivery
- logs:ListLogDeliveries
- logs:PutResourcePolicy
- logs:DescribeResourcePolicies
- logs:DescribeLogGroups
Resource: '*'
PolicyName: CloudWatchLogsDeliveryFullAccessPolicy
- Fn::If:
- IsEnabledXRayTracing
- PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- xray:PutTraceSegments
- xray:PutTelemetryRecords
- xray:GetSamplingRules
- xray:GetSamplingTargets
Resource:
- '*'
PolicyName: XRayAccessPolicy
- Ref: AWS::NoValue
- Fn::If:
- ExistsIamPolicyDocument
- PolicyDocument:
Fn::Sub: ${IamPolicyDocument}
PolicyName: IamPolicyForExecuteStateMachine
- Ref: AWS::NoValue
ExecuteStateMachineRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- events.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: states_StartExecution
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- states:StartExecution
Resource:
- Ref: StateMachine
ScheduledRule0:
Type: AWS::Events::Rule
Properties:
Description: ScheduledRule
ScheduleExpression:
Fn::Sub: ${Cron0}
State: ENABLED
Targets:
- Arn:
Ref: StateMachine
Id:
Fn::GetAtt: StateMachine.Name
RoleArn:
Fn::GetAtt: ExecuteStateMachineRole.Arn
ScheduledRule1:
Type: AWS::Events::Rule
Properties:
Description: ScheduledRule
ScheduleExpression:
Fn::Sub: ${Cron1}
State: ENABLED
Targets:
- Arn:
Ref: StateMachine
Id:
Fn::GetAtt: StateMachine.Name
RoleArn:
Fn::GetAtt: ExecuteStateMachineRole.Arn
ScheduledRule2:
Type: AWS::Events::Rule
Properties:
Description: ScheduledRule
ScheduleExpression:
Fn::Sub: ${Cron2}
State: ENABLED
Targets:
- Arn:
Ref: StateMachine
Id:
Fn::GetAtt: StateMachine.Name
RoleArn:
Fn::GetAtt: ExecuteStateMachineRole.Arn
EventPatternRule0:
Type: AWS::Events::Rule
Properties:
Description: EventPatternRule
EventPattern:
Fn::Sub: ${EventPattern0}
EventBusName:
Fn::Sub: ${EventBusArn0}
State: ENABLED
Targets:
- Arn:
Ref: StateMachine
Id:
Fn::GetAtt: StateMachine.Name
RoleArn:
Fn::GetAtt: ExecuteStateMachineRole.Arn
EventPatternRule1:
Type: AWS::Events::Rule
Properties:
Description: EventPatternRule
EventPattern:
Fn::Sub: ${EventPattern1}
EventBusName:
Fn::Sub: ${EventBusArn1}
State: ENABLED
Targets:
- Arn:
Ref: StateMachine
Id:
Fn::GetAtt: StateMachine.Name
RoleArn:
Fn::GetAtt: ExecuteStateMachineRole.Arn
TargetEventBusArnRule0:
Type: AWS::Events::Rule
Properties:
Description: TargetEventBusArnRule
EventPattern:
source:
- aws.states
detail-type:
- Step Functions Execution Status Change
detail:
status:
- SUCCEEDED
stateMachineArn:
- Ref: StateMachine
State: ENABLED
Targets:
- Arn:
Ref: TargetEventBusArn0
Id: TargetEventBusArn0
RoleArn:
Fn::GetAtt: InvokeEventBusRole0.Arn
InvokeEventBusRole0:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- events.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: InvokeEventBus
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- events:PutEvents
Resource:
- Ref: TargetEventBusArn0
TargetEventBusArnRule1:
Type: AWS::Events::Rule
Properties:
Description: TargetEventBusArnRule
EventPattern:
source:
- aws.states
detail-type:
- Step Functions Execution Status Change
detail:
status:
- SUCCEEDED
stateMachineArn:
- Ref: StateMachine
State: ENABLED
Targets:
- Arn:
Ref: TargetEventBusArn1
Id: TargetEventBusArn1
RoleArn:
Fn::GetAtt: InvokeEventBusRole1.Arn
InvokeEventBusRole1:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- events.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: InvokeEventBus
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- events:PutEvents
Resource:
- Ref: TargetEventBusArn1
それシェルスクリプトで出来るかも
シェルスクリプトを使って、入力されたパラメーターの数に応じてCloudFormationテンプレートファイルを動的に変化させてみました。
かなりのパワープレイですが、AWS CDKやマクロを使わずにAWS CloudFomationでループ処理を実装したい場合は、このようにテンプレートファイルを直接編集するスクリプトを作ることになるのかなと思います。
そのような場面に備えてスクリプトを書く力を鍛えておくことが重要そうですね。
今回シェルスクリプトを書くにあたっては、GoogleのShell Style Guideを参考にしました。
この記事が誰かの助けになれば幸いです。
以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!