本記事は 春のスキルアップ応援フェア2026 4/26付の記事です。 |

- 2つのCloudFormationでVPC/Transit Gateway/Network Firewall/AWS Private CA等の基盤をデプロイ
- シェルスクリプトでAWS CLIを使用してNetwork Firewall Proxyリソースを作成
- 別のCloudFormationスタックでNetwork Firewall Proxy Endpointをデプロイ
Step Functionsとは — どういうときにはまるのか
- CloudFormationだけでは完結しないデプロイ。例えばCloudFormation未対応のリソースをAPI/CLIで作成するステップを含むもの
- ステップ間でデータの受け渡しが必要なもの(前のCloudFormationスタックのOutputsを次のステップに渡す等)
- 長時間かかるステップを含むもの(CloudFormationスタック作成やリソースの作成待ちなど、数十分を要する場合)
上記それぞれを個別に考えると、CloudFormationのスタックをデプロイするAWS CLIコマンドと個別のAWSリソースをデプロイするAWS CLIコマンドを羅列すればよくない?とか、シェル変数に入れて受け渡すだけでしょう?とか、待っている数十分の間にシェルスクリプトの実行環境が障害起こす可能性がどれだけあるというの?となりますが、こうした細かい考慮事項を自分で管理しようと考えると意外に面倒なものです。Step Functionsを使えば各ステップの実行状態がコンソールで視覚的に確認できるため、どこで何が起きたかが一目瞭然ですし、問題が起きた場合のエラーハンドリングも簡単に実装できます。
シェルスクリプトの実行方法
phases:
build:
commands:
- echo "=== Getting VPC Stack Outputs ==="
- echo "VPC Stack Name ${VPC_STACK_NAME}"
- echo "S3 Bucket ${S3_BUCKET}"
- SUBNET_ID=$(aws cloudformation describe-stacks --stack-name ${VPC_STACK_NAME} --query "Stacks[0].Outputs[?OutputKey=='SubnetId'].OutputValue" --output text --no-cli-pager)
- echo "Subnet ID ${SUBNET_ID}"
- SECURITY_GROUP_ID=$(aws cloudformation describe-stacks --stack-name ${VPC_STACK_NAME} --query "Stacks[0].Outputs[?OutputKey=='SecurityGroupId'].OutputValue" --output text --no-cli-pager)
- echo "Security Group ID ${SECURITY_GROUP_ID}"
- echo "=== Creating EC2 Instance ==="
- INSTANCE_ID=$(aws ec2 run-instances --image-id resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 --instance-type t3.micro --subnet-id ${SUBNET_ID} --security-group-ids ${SECURITY_GROUP_ID} --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=sfn-poc-test},{Key=Cost,Value=XXX}]' --query 'Instances[0].InstanceId' --output text --no-cli-pager)
- echo "Instance ID ${INSTANCE_ID}"
今回作ったもの
Step Functionsの実装パターン解説
CloudFormation SDK統合 + ポーリングループ
Step FunctionsにはCloudFormation用の最適化統合(.sync パターン、つまり完了まで自動待機してくれる統合)が存在しません。そのため、CloudFormation APIはAWS SDK統合で呼び出し、スタック完了待ちは自前のポーリングループで実装する必要があります。
具体的には、CreateStack → Wait → DescribeStacks → Choice(完了判定)のループを組みます。ASLの該当部分を抜粋します(一部、読みやすさを優先して修正・省略しています)。
"CreateVpcStack": {
"Type": "Task",
"Resource": "arn:aws:states:::aws-sdk:cloudformation:createStack",
"Parameters": {
"StackName.$": "$.vpcStackName",
"TemplateURL.$": "$.vpcTemplateUrl",
"RoleARN": "arn:aws:iam::...:role/CloudFormation-role",
"Tags": [{"Key": "Cost", "Value": "XXX"}]
},
"ResultPath": "$.createVpcResult",
"Next": "WaitForVpcStack"
},
"WaitForVpcStack": {
"Type": "Wait",
"Seconds": 15,
"Next": "CheckVpcStack"
},
"CheckVpcStack": {
"Type": "Task",
"Resource": "arn:aws:states:::aws-sdk:cloudformation:describeStacks",
"Parameters": {
"StackName.$": "$.vpcStackName"
},
"ResultPath": "$.describeVpcResult",
"Next": "IsVpcStackComplete"
},
"IsVpcStackComplete": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.describeVpcResult.Stacks[0].StackStatus",
"StringEquals": "CREATE_COMPLETE",
"Next": "ExtractVpcOutputs"
},
{
"Variable": "$.describeVpcResult.Stacks[0].StackStatus",
"StringMatches": "*FAILED*",
"Next": "DeploymentFailed"
},
{
"Variable": "$.describeVpcResult.Stacks[0].StackStatus",
"StringMatches": "*ROLLBACK*",
"Next": "DeploymentFailed"
}
],
"Default": "WaitForVpcStack"
}
ポイントは以下の通りです。
- CloudFormationテンプレートはS3にアップロードして TemplateURL で参照する必要がある(SDK統合ではローカルファイル参照不可)
- DescribeStacks の結果を ResultPath で保持し、Choiceステートで StackStatus を判定
- CREATE_COMPLETE でもなく FAILED/ROLLBACK でもない場合(CREATE_IN_PROGRESS 等)は Default でWaitに戻る
- ポーリング間隔はスタックの規模に応じて調整(今回は15秒)
CodeBuild最適化統合(.sync)
CodeBuildにはStep Functionsとの最適化統合があり、startBuild.sync と書くだけでビルド完了まで自動待機してくれます。CloudFormationのポーリングループと比べると非常にシンプルです(一部、読みやすさを優先して修正・省略しています)。
"RunCreateEC2Build": {
"Type": "Task",
"Resource": "arn:aws:states:::codebuild:startBuild.sync",
"Parameters": {
"ProjectName": "sfn-poc-orchestrator-create-ec2",
"EnvironmentVariablesOverride": [
{
"Name": "VPC_STACK_NAME",
"Value.$": "$.vpcStackName",
"Type": "PLAINTEXT"
},
{
"Name": "S3_BUCKET",
"Value.$": "$.s3BucketName",
"Type": "PLAINTEXT"
}
]
},
"ResultPath": "$.buildResult",
"Next": "ReadEC2Result"
}
EnvironmentVariablesOverride で、前のステップで取得した値をCodeBuildの環境変数として渡しています。CodeBuild内のbuildspec.ymlでは、この環境変数を使ってAWS CLIコマンドを実行します。
CodeBuildの出力値(今回はEC2のInstanceId)を後続ステートに渡すには、CodeBuild内でS3にJSONファイルを書き出し、後続のS3 GetObject SDK統合で読み取る方式を採用しました(以下、一部、読みやすさを優先して省略しています)。
"ReadEC2Result": {
"Type": "Task",
"Resource": "arn:aws:states:::aws-sdk:s3:getObject",
"Parameters": {
"Bucket.$": "$.s3BucketName",
"Key": "results/create-ec2/result.json"
},
"ResultSelector": {
"body.$": "States.StringToJson($.Body)"
},
"ResultPath": "$.ec2Result",
"Next"
States.StringToJson 組み込み関数で、S3から読んだ文字列をJSONオブジェクトに変換しています。これにより、後続のCloudFormationスタック作成時に $.ec2Result.body.instanceId のようにドット記法でInstanceIdを参照できます。
フェーズ間のデータ受け渡し
Step Functionsのデータフローを理解する上で重要なのが ResultPath と ResultSelector です。
- ResultPath: タスクの出力をステート入力JSONのどこに格納するかを指定する。 “ResultPath”: “$.buildResult” とすると、元の入力JSONに buildResult キーが追加された形で出力されます。元の入力データが失われないのがポイントです。
- ResultSelector: タスクの出力から必要な部分だけを抽出する。S3 GetObjectの巨大なレスポンスから Body だけを取り出す、といった用途に使います。
今回の構成では、データが以下のように流れます。
CloudFormation Stack A 作成完了 ↓ ExtractVpcOutputs (Passステート) でスタック名等を整形 ↓ CodeBuild 環境変数 (VPC_STACK_NAME, S3_BUCKET) を受け取り ↓ buildspec.yml内でVPCスタックのOutputsを問い合わせてSubnetId, SecurityGroupIdを取得 ↓ EC2インスタンスを作成 ↓ result.json を S3 に書き出し ↓ S3 GetObject + States.StringToJson で InstanceId を取得 ↓ CloudFormation Stack B Parameters (InstanceId)
この一連の流れが、Step Functionsの宣言的な定義だけで見通しよく書けるのはなかなか利便性高いです。
まとめ
試した結果、3つの検証ポイントはすべて問題なく動作しました。検証ポイントに対する結果と所感は以下の通りです。
- CloudFormation SDK統合 + ポーリングループ: 自前で組む必要はあるが、パターンさえ覚えれば難しくない
- CodeBuild .sync 統合: .sync サフィックスをつけるだけで完了待ちしてくれるので非常に楽
- フェーズ間のデータ受け渡し: ResultPath/ResultSelector/States.StringToJson の組み合わせで柔軟に対応できる
今回で基本構造が確認できたので、次の記事ではNetwork Firewall Proxyのデプロイ自動化に進みます。
デプロイ手順とソースコード
本記事で実施した内容を実際に試してみたい方のために、デプロイ手順とソースコードを掲載します。
しかしあれですね、デプロイを楽にするためのStep Functions等のスタック(Orchestratorスタック)作成を個別に実施する必要がありそこは手順に基づく手作業になるので、結局デプロイの手順の数はそんなに削減されないという・・・。
1. Orchestratorスタックのデプロイ
まず、Step Functionsステートマシン、CodeBuildプロジェクト、S3バケット等を含むOrchestratorスタックをデプロイします。
aws cloudformation create-stack \ --stack-name sfn-poc-orchestrator \ --template-body file://cfn-sfn-poc-orchestrator.yaml \ --capabilities CAPABILITY_IAM \ --region ap-northeast-1
2. CloudFormationテンプレートとbuildspecのS3アップロード
Orchestratorスタックが作成したS3バケットに、VPC/Alarm用のCloudFormationテンプレートとbuildspecをアップロードします。
# S3バケット名を取得
BUCKET=$(aws cloudformation describe-stacks \
--stack-name sfn-poc-orchestrator \
--query "Stacks[0].Outputs[?OutputKey=='ArtifactBucketName'].OutputValue" \
--output text \
--region ap-northeast-1)
# CloudFormationテンプレートをアップロード
aws s3 cp cfn-sfn-poc-vpc.yaml s3://${BUCKET}/cfn-templates/cfn-sfn-poc-vpc.yaml
aws s3 cp cfn-sfn-poc-alarm.yaml s3://${BUCKET}/cfn-templates/cfn-sfn-poc-alarm.yaml
# buildspecをアップロード
aws s3 cp buildspec.yml s3://${BUCKET}/buildspec/sfn-poc-create-ec2/buildspec.yml
### 3. ステートマシンの実行
# ステートマシンARNを取得
STATE_MACHINE_ARN=$(aws cloudformation describe-stacks \
--stack-name sfn-poc-orchestrator \
--query "Stacks[0].Outputs[?OutputKey=='StateMachineArn'].OutputValue" \
--output text \
--region ap-northeast-1)
# ステートマシンを実行
aws stepfunctions start-execution \
--state-machine-arn ${STATE_MACHINE_ARN} \
--input "{
\"vpcStackName\": \"sfn-poc-vpc\",
\"alarmStackName\": \"sfn-poc-alarm\",
\"vpcTemplateUrl\": \"https://s3.ap-northeast-1.amazonaws.com/${BUCKET}/cfn-templates/cfn-sfn-poc-vpc.yaml\",
\"alarmTemplateUrl\": \"https://s3.ap-northeast-1.amazonaws.com/${BUCKET}/cfn-templates/cfn-sfn-poc-alarm.yaml\",
\"s3BucketName\": \"${BUCKET}\",
\"region\": \"ap-northeast-1\"
}" \
--region ap-northeast-1
Step Functionsコンソールで実行状況を確認できます。全ステップが成功すると、VPC、EC2インスタンス、CloudWatch Alarmが作成されます。
ソースコード
cfn-sfn-poc-orchestrator.yaml(Orchestrator: Step Functions + CodeBuild + S3)
AWSTemplateFormatVersion: '2010-09-09'
Description: Step Functions PoC - Orchestrator (State Machine + CodeBuild + S3)
Parameters:
EnvironmentName:
Type: String
Default: dev
Resources:
# ========================================
# S3 Bucket
# ========================================
ArtifactBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub '${AWS::StackName}-artifacts-${AWS::AccountId}-${AWS::Region}'
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
VersioningConfiguration:
Status: Enabled
Tags:
- Key: Cost
Value: XXX
# ========================================
# CodeBuild IAM Role
# ========================================
CodeBuildRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: codebuild.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: CodeBuildPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ec2:RunInstances
- ec2:DescribeInstances
- ec2:DescribeInstanceStatus
- ec2:CreateTags
Resource: '*'
- Effect: Allow
Action:
- cloudformation:DescribeStacks
Resource: '*'
- Effect: Allow
Action:
- s3:PutObject
- s3:GetObject
- s3:GetBucketLocation
- s3:ListBucket
Resource:
- !Sub '${ArtifactBucket.Arn}'
- !Sub '${ArtifactBucket.Arn}/*'
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: '*'
- Effect: Allow
Action:
- ssm:GetParameters
Resource: !Sub 'arn:aws:ssm:${AWS::Region}::parameter/aws/service/ami-amazon-linux-latest/*'
Tags:
- Key: Cost
Value: XXX
# ========================================
# CodeBuild Project
# ========================================
CreateEC2Project:
Type: AWS::CodeBuild::Project
Properties:
Name: !Sub '${AWS::StackName}-create-ec2'
ServiceRole: !GetAtt CodeBuildRole.Arn
Artifacts:
Type: NO_ARTIFACTS
Environment:
Type: LINUX_CONTAINER
ComputeType: BUILD_GENERAL1_SMALL
Image: aws/codebuild/amazonlinux2-x86_64-standard:5.0
EnvironmentVariables:
- Name: S3_BUCKET
Value: !Ref ArtifactBucket
- Name: SUBNET_ID
Value: placeholder
- Name: SECURITY_GROUP_ID
Value: placeholder
Source:
Type: S3
Location: !Sub '${ArtifactBucket}/buildspec/sfn-poc-create-ec2/'
TimeoutInMinutes: 15
Tags:
- Key: Cost
Value: XXX
# ========================================
# CloudFormation Execution Role
# ========================================
CloudFormationRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: cloudformation.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AdministratorAccess
Tags:
- Key: Cost
Value: XXX
# ========================================
# Step Functions IAM Role
# ========================================
StepFunctionsRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: states.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: StepFunctionsPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- cloudformation:CreateStack
- cloudformation:DescribeStacks
Resource: '*'
- Effect: Allow
Action:
- s3:GetObject
- s3:PutObject
Resource:
- !Sub '${ArtifactBucket.Arn}/*'
- Effect: Allow
Action:
- codebuild:StartBuild
- codebuild:StopBuild
- codebuild:BatchGetBuilds
Resource:
- !GetAtt CreateEC2Project.Arn
- Effect: Allow
Action:
- iam:PassRole
Resource:
- !GetAtt CloudFormationRole.Arn
Condition:
StringEquals:
iam:PassedToService: cloudformation.amazonaws.com
- Effect: Allow
Action:
- events:PutTargets
- events:PutRule
- events:DescribeRule
Resource: '*'
Tags:
- Key: Cost
Value: XXX
# ========================================
# Step Functions State Machine
# ========================================
DeploymentStateMachine:
Type: AWS::StepFunctions::StateMachine
Properties:
StateMachineName: !Sub '${AWS::StackName}-deployment'
RoleArn: !GetAtt StepFunctionsRole.Arn
DefinitionString: !Sub |
{
"Comment": "SFN PoC: CloudFormation(VPC) -> CodeBuild(EC2) -> CloudFormation(Alarm)",
"StartAt": "CreateVpcStack",
"States": {
"CreateVpcStack": {
"Type": "Task",
"Resource": "arn:aws:states:::aws-sdk:cloudformation:createStack",
"Parameters": {
"StackName.$": "$.vpcStackName",
"TemplateURL.$": "$.vpcTemplateUrl",
"RoleARN": "${CloudFormationRole.Arn}",
"Tags": [{"Key": "Cost", "Value": "XXX"}]
},
"ResultPath": "$.createVpcResult",
"Next": "WaitForVpcStack",
"Catch": [{
"ErrorEquals": ["States.ALL"],
"Next": "DeploymentFailed",
"ResultPath": "$.error"
}]
},
"WaitForVpcStack": {
"Type": "Wait",
"Seconds": 15,
"Next": "CheckVpcStack"
},
"CheckVpcStack": {
"Type": "Task",
"Resource": "arn:aws:states:::aws-sdk:cloudformation:describeStacks",
"Parameters": {
"StackName.$": "$.vpcStackName"
},
"ResultPath": "$.describeVpcResult",
"Next": "IsVpcStackComplete",
"Catch": [{
"ErrorEquals": ["States.ALL"],
"Next": "DeploymentFailed",
"ResultPath": "$.error"
}]
},
"IsVpcStackComplete": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.describeVpcResult.Stacks[0].StackStatus",
"StringEquals": "CREATE_COMPLETE",
"Next": "ExtractVpcOutputs"
},
{
"Variable": "$.describeVpcResult.Stacks[0].StackStatus",
"StringMatches": "*FAILED*",
"Next": "DeploymentFailed"
},
{
"Variable": "$.describeVpcResult.Stacks[0].StackStatus",
"StringMatches": "*ROLLBACK*",
"Next": "DeploymentFailed"
}
],
"Default": "WaitForVpcStack"
},
"ExtractVpcOutputs": {
"Type": "Pass",
"Parameters": {
"vpcStackName.$": "$.vpcStackName",
"alarmStackName.$": "$.alarmStackName",
"alarmTemplateUrl.$": "$.alarmTemplateUrl",
"s3BucketName.$": "$.s3BucketName"
},
"Next": "RunCreateEC2Build"
},
"RunCreateEC2Build": {
"Type": "Task",
"Resource": "arn:aws:states:::codebuild:startBuild.sync",
"Parameters": {
"ProjectName": "${CreateEC2Project}",
"EnvironmentVariablesOverride": [
{
"Name": "VPC_STACK_NAME",
"Value.$": "$.vpcStackName",
"Type": "PLAINTEXT"
},
{
"Name": "S3_BUCKET",
"Value.$": "$.s3BucketName",
"Type": "PLAINTEXT"
}
]
},
"ResultPath": "$.buildResult",
"Next": "ReadEC2Result",
"Catch": [{
"ErrorEquals": ["States.ALL"],
"Next": "DeploymentFailed",
"ResultPath": "$.error"
}]
},
"ReadEC2Result": {
"Type": "Task",
"Resource": "arn:aws:states:::aws-sdk:s3:getObject",
"Parameters": {
"Bucket.$": "$.s3BucketName",
"Key": "results/create-ec2/result.json"
},
"ResultSelector": {
"body.$": "States.StringToJson($.Body)"
},
"ResultPath": "$.ec2Result",
"Next": "CreateAlarmStack",
"Catch": [{
"ErrorEquals": ["States.ALL"],
"Next": "DeploymentFailed",
"ResultPath": "$.error"
}]
},
"CreateAlarmStack": {
"Type": "Task",
"Resource": "arn:aws:states:::aws-sdk:cloudformation:createStack",
"Parameters": {
"StackName.$": "$.alarmStackName",
"TemplateURL.$": "$.alarmTemplateUrl",
"RoleARN": "${CloudFormationRole.Arn}",
"Parameters": [
{
"ParameterKey": "InstanceId",
"ParameterValue.$": "$.ec2Result.body.instanceId"
}
],
"Tags": [{"Key": "Cost", "Value": "XXX"}]
},
"ResultPath": "$.createAlarmResult",
"Next": "WaitForAlarmStack",
"Catch": [{
"ErrorEquals": ["States.ALL"],
"Next": "DeploymentFailed",
"ResultPath": "$.error"
}]
},
"WaitForAlarmStack": {
"Type": "Wait",
"Seconds": 15,
"Next": "CheckAlarmStack"
},
"CheckAlarmStack": {
"Type": "Task",
"Resource": "arn:aws:states:::aws-sdk:cloudformation:describeStacks",
"Parameters": {
"StackName.$": "$.alarmStackName"
},
"ResultPath": "$.describeAlarmResult",
"Next": "IsAlarmStackComplete",
"Catch": [{
"ErrorEquals": ["States.ALL"],
"Next": "DeploymentFailed",
"ResultPath": "$.error"
}]
},
"IsAlarmStackComplete": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.describeAlarmResult.Stacks[0].StackStatus",
"StringEquals": "CREATE_COMPLETE",
"Next": "DeploymentSucceeded"
},
{
"Variable": "$.describeAlarmResult.Stacks[0].StackStatus",
"StringMatches": "*FAILED*",
"Next": "DeploymentFailed"
},
{
"Variable": "$.describeAlarmResult.Stacks[0].StackStatus",
"StringMatches": "*ROLLBACK*",
"Next": "DeploymentFailed"
}
],
"Default": "WaitForAlarmStack"
},
"DeploymentSucceeded": {
"Type": "Succeed"
},
"DeploymentFailed": {
"Type": "Fail",
"Error": "DeploymentError",
"Cause": "One or more deployment steps failed"
}
}
}
Tags:
- Key: Cost
Value: XXX
Outputs:
StateMachineArn:
Value: !Ref DeploymentStateMachine
ArtifactBucketName:
Value: !Ref ArtifactBucket
CodeBuildProjectName:
Value: !Ref CreateEC2Project
cfn-sfn-poc-vpc.yaml(Stack A: VPC基盤)
AWSTemplateFormatVersion: '2010-09-09'
Description: Step Functions PoC - VPC Base Infrastructure (Stack A)
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.99.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-vpc'
- Key: Cost
Value: XXX
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-igw'
- Key: Cost
Value: XXX
VPCGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: 10.99.1.0/24
MapPublicIpOnLaunch: true
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-public-subnet'
- Key: Cost
Value: XXX
RouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-rt'
- Key: Cost
Value: XXX
DefaultRoute:
Type: AWS::EC2::Route
DependsOn: VPCGatewayAttachment
Properties:
RouteTableId: !Ref RouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
SubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet
RouteTableId: !Ref RouteTable
SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: SFN PoC Security Group - outbound only
VpcId: !Ref VPC
SecurityGroupEgress:
- IpProtocol: -1
CidrIp: 0.0.0.0/0
Tags:
- Key: Name
Value: !Sub '${AWS::StackName}-sg'
- Key: Cost
Value: XXX
Outputs:
VpcId:
Value: !Ref VPC
SubnetId:
Value: !Ref PublicSubnet
SecurityGroupId:
Value: !Ref SecurityGroup
cfn-sfn-poc-alarm.yaml(Stack B: CloudWatch Alarm)
AWSTemplateFormatVersion: '2010-09-09'
Description: Step Functions PoC - CloudWatch Alarm (Stack B)
Parameters:
InstanceId:
Type: String
Description: EC2 Instance ID to monitor
Resources:
CPUAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmName: !Sub '${AWS::StackName}-cpu-alarm'
AlarmDescription: CPU utilization alarm for PoC test
Namespace: AWS/EC2
MetricName: CPUUtilization
Dimensions:
- Name: InstanceId
Value: !Ref InstanceId
Statistic: Average
Period: 300
EvaluationPeriods: 1
Threshold: 80
ComparisonOperator: GreaterThanThreshold
TreatMissingData: notBreaching
Outputs:
AlarmArn:
Value: !GetAtt CPUAlarm.Arn
AlarmName:
Value: !Ref CPUAlarm
buildspec.yml(CodeBuild: EC2インスタンス作成)
version: 0.2
phases:
build:
commands:
- echo "=== Getting VPC Stack Outputs ==="
- echo "VPC Stack Name ${VPC_STACK_NAME}"
- echo "S3 Bucket ${S3_BUCKET}"
- SUBNET_ID=$(aws cloudformation describe-stacks --stack-name ${VPC_STACK_NAME} --query "Stacks[0].Outputs[?OutputKey=='SubnetId'].OutputValue" --output text --no-cli-pager)
- echo "Subnet ID ${SUBNET_ID}"
- SECURITY_GROUP_ID=$(aws cloudformation describe-stacks --stack-name ${VPC_STACK_NAME} --query "Stacks[0].Outputs[?OutputKey=='SecurityGroupId'].OutputValue" --output text --no-cli-pager)
- echo "Security Group ID ${SECURITY_GROUP_ID}"
- echo "=== Creating EC2 Instance ==="
- INSTANCE_ID=$(aws ec2 run-instances --image-id resolve:ssm:/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 --instance-type t3.micro --subnet-id ${SUBNET_ID} --security-group-ids ${SECURITY_GROUP_ID} --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=sfn-poc-test},{Key=Cost,Value=XXX}]' --query 'Instances[0].InstanceId' --output text --no-cli-pager)
- echo "Instance ID ${INSTANCE_ID}"
- echo "=== Waiting for instance to be running ==="
- aws ec2 wait instance-running --instance-ids ${INSTANCE_ID}
- echo "Instance is running"
- echo "=== Writing result to S3 ==="
- echo "{\"instanceId\":\"${INSTANCE_ID}\"}" > /tmp/result.json
- cat /tmp/result.json
- aws s3 cp /tmp/result.json s3://${S3_BUCKET}/results/create-ec2/result.json --no-cli-pager
- echo "=== Done ==="



