Network Stack#

Let's create: vpc, private subnets, public subnets, nat, security group for alb and for ecs fargate.

AWSTemplateFormatVersion: '2010-09-09'
#------------------------------------------------------
# Mappings
#------------------------------------------------------
Mappings:
CidrMappings:
public-subnet-1:
CIDR: 10.0.0.0/24
public-subnet-2:
CIDR: 10.0.2.0/24
private-subnet-1:
CIDR: 10.0.1.0/24
private-subnet-2:
CIDR: 10.0.3.0/24
#------------------------------------------------------
# Parameters
#------------------------------------------------------
Parameters:
CidrBlock:
Type: String
Description: CidrBlock
Default: 10.0.0.0/16
InternetCidrBlock:
Type: String
Description: UserCidrBlock
Default: 0.0.0.0/0
#------------------------------------------------------
# Resources: VPC, Subnets, NAT, Routes
#------------------------------------------------------
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref CidrBlock
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-vpc
#------------------------------------------------------
# Resources: internet gateway
#------------------------------------------------------
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-ig
AttachGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
#------------------------------------------------------
# Resources: public and private subnets
#------------------------------------------------------
PublicSubnet1:
Type: AWS::EC2::Subnet
Properties:
MapPublicIpOnLaunch: false
AvailabilityZone:
Fn::Select:
- 0
- Fn::GetAZs:
Ref: AWS::Region
VpcId: !Ref VPC
CidrBlock:
Fn::FindInMap:
- CidrMappings
- public-subnet-1
- CIDR
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-public-subnet-1
PublicSubnet2:
Type: AWS::EC2::Subnet
Properties:
MapPublicIpOnLaunch: false
AvailabilityZone:
Fn::Select:
- 1
- Fn::GetAZs:
Ref: AWS::Region
VpcId: !Ref VPC
CidrBlock:
Fn::FindInMap:
- CidrMappings
- public-subnet-2
- CIDR
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-public-subnet-2
PrivateSubnet1:
Type: AWS::EC2::Subnet
Properties:
MapPublicIpOnLaunch: false
AvailabilityZone:
Fn::Select:
- 0
- Fn::GetAZs:
Ref: AWS::Region
VpcId: !Ref VPC
CidrBlock:
Fn::FindInMap:
- CidrMappings
- private-subnet-1
- CIDR
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-private-subnet-1
PrivateSubnet2:
Type: AWS::EC2::Subnet
Properties:
MapPublicIpOnLaunch: false
AvailabilityZone:
Fn::Select:
- 1
- Fn::GetAZs:
Ref: AWS::Region
VpcId: !Ref VPC
CidrBlock:
Fn::FindInMap:
- CidrMappings
- private-subnet-2
- CIDR
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-private-subnet-2
#------------------------------------------------------
# Resources: nat gateway
#------------------------------------------------------
NatGatewayEIP:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
NatGateway:
Type: AWS::EC2::NatGateway
Properties:
AllocationId:
Fn::GetAtt: [NatGatewayEIP, AllocationId]
SubnetId: !Ref PublicSubnet1
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-nat-gateway
#------------------------------------------------------
# Resources: public route table
#------------------------------------------------------
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-public-rt
RouteInternetGateway:
Type: AWS::EC2::Route
DependsOn: AttachGateway
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: !Ref InternetCidrBlock
GatewayId: !Ref InternetGateway
#------------------------------------------------------
# Resources: private route table
#------------------------------------------------------
PrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub ${AWS::StackName}-private-rt
PrivateRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable
DestinationCidrBlock: !Ref InternetCidrBlock
NatGatewayId: !Ref NatGateway
#------------------------------------------------------
# Resources: routeable subnet associations
#------------------------------------------------------
PublicSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet1
RouteTableId: !Ref PublicRouteTable
PublicSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet2
RouteTableId: !Ref PublicRouteTable
PrivateSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet1
RouteTableId: !Ref PrivateRouteTable
PrivateSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet2
RouteTableId: !Ref PrivateRouteTable
#------------------------------------------------------
# Security Group:
#------------------------------------------------------
ECSFargateSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Communication between the control plane and worker nodegroups
VpcId: !Ref VPC
GroupName: !Sub ${AWS::StackName}-ecs-fargate-sg
ECSFargateSecurityGroupIngress:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref ECSFargateSecurityGroup
IpProtocol: -1
SourceSecurityGroupId: !Ref ECSFargateSecurityGroup
SourceSecurityGroupOwnerId: !Ref AWS::AccountId
ECSFargateSecurityGroupIngressALB:
Type: AWS::EC2::SecurityGroupIngress
Properties:
IpProtocol: tcp
FromPort: 3000
ToPort: 3000
SourceSecurityGroupId: !Ref ALBSecurityGroup
GroupId: !Ref ECSFargateSecurityGroup
ALBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: ALB Security Group
VpcId: !Ref VPC
GroupName: !Sub ${AWS::StackName}-alb-sg
ALBSecurityGroupIngress:
Type: AWS::EC2::SecurityGroupIngress
Properties:
IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: !Ref InternetCidrBlock
GroupId: !Ref ALBSecurityGroup
#------------------------------------------------------
# Export:
#------------------------------------------------------
Outputs:
VPC:
Value: !Ref VPC
Export:
Name: !Sub ${AWS::StackName}-vpc
PublicSubnet1:
Value: !Ref PublicSubnet1
Export:
Name: !Sub ${AWS::StackName}-public-subnet-1
PublicSubnet2:
Value: !Ref PublicSubnet2
Export:
Name: !Sub ${AWS::StackName}-public-subnet-2
PrivateSubnet1:
Value: !Ref PrivateSubnet2
Export:
Name: !Sub ${AWS::StackName}-private-subnet-1
PrivateSubnet2:
Value: !Ref PrivateSubnet2
Export:
Name: !Sub ${AWS::StackName}-private-subnet-2
PrivateRouteTable:
Value: !Ref PrivateRouteTable
Export:
Name: !Sub ${AWS::StackName}-private-route-table
InternetGateway:
Value: !Ref InternetGateway
Export:
Name: !Sub ${AWS::StackName}-igw
ECSFargateSecurityGroup:
Value: !Ref ECSFargateSecurityGroup
Export:
Name: !Sub ${AWS::StackName}-ecs-fargate-sg
ALBSecurityGroup:
Value: !Ref ALBSecurityGroup
Export:
Name: !Sub ${AWS::StackName}-alb-sg

Load Balancer#

Next let's create a ALB with a listener on port 80 and a target group.

AWSTemplateFormatVersion: '2010-09-09'
Parameters:
NetworkStackName:
Description: Stack name of the network stack
Type: String
Default: cfn-network
Resources:
LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Subnets:
- Fn::ImportValue: !Sub ${NetworkStackName}-public-subnet-1
- Fn::ImportValue: !Sub ${NetworkStackName}-public-subnet-2
SecurityGroups:
- Fn::ImportValue: !Sub ${NetworkStackName}-alb-sg
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
VpcId:
Fn::ImportValue: !Sub ${NetworkStackName}-vpc
Port: 80
Protocol: HTTP
TargetType: ip
Matcher:
HttpCode: 200-299
HealthCheckIntervalSeconds: 10
HealthCheckPath: /
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 2
Listener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref LoadBalancer
Port: 80
Protocol: HTTP
DefaultActions:
- Type: forward
TargetGroupArn: !Ref TargetGroup
# Export output
Outputs:
LoadBalancer:
Description: Load balancer
Value: !Ref LoadBalancer
Export:
Name: !Sub ${AWS::StackName}-load-balancer
TargetGroup:
Description: Target group
Value: !Ref TargetGroup
Export:
Name: !Sub ${AWS::StackName}-target-group

ECS Cluster#

Now let's create a ecs cluster.

AWSTemplateFormatVersion: '2010-09-09'
# Parameters
Parameters:
ClusterName:
Type: String
Description: 'ImageId to be used to create an EC2 instance.'
Default: 'demo'
# Create an ecs cluster
Resources:
ecscluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: !Ref ClusterName
ClusterSettings:
- Name: containerInsights
Value: enabled
CapacityProviders:
- FARGATE
- FARGATE_SPOT
DefaultCapacityProviderStrategy:
- CapacityProvider: FARGATE
Weight: 4
Base: 1
- CapacityProvider: FARGATE_SPOT
Weight: 1
Base: 0
# Export
Outputs:
ecscluster:
Value: !Ref ecscluster
Export:
Name: !Sub '${AWS::StackName}-ecscluster'

Task Definition#

Let's create a task definition with task role and task execution role, container definition, log group.

AWSTemplateFormatVersion: '2010-09-09'
Description: ECS Task Definition
Parameters:
ImageUri:
Type: String
Description: ecr image uri
Default: <ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com/go-app
Resources:
TaskRoleBook:
Type: AWS::IAM::Role
Properties:
RoleName: 'ECSTaskRoleForBook'
AssumeRolePolicyDocument:
Statement:
- Action:
- 'sts:AssumeRole'
Effect: Allow
Principal:
Service:
- ecs-tasks.amazonaws.com
Policies:
- PolicyName: root
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- 'ssmmessages:CreateControlChannel'
- 'ssmmessages:CreateDataChannel'
- 'ssmmessages:OpenControlChannel'
- 'ssmmessages:OpenDataChannel'
Resource: '*'
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/AmazonS3FullAccess'
TaskExecutionRoleBook:
Type: AWS::IAM::Role
Properties:
RoleName: 'ECSTaskExecutionRoleForBook'
AssumeRolePolicyDocument:
Statement:
- Action:
- 'sts:AssumeRole'
Effect: Allow
Principal:
Service:
- ecs-tasks.amazonaws.com
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
BookTaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: book-service
TaskRoleArn: !GetAtt TaskRoleBook.Arn
ExecutionRoleArn: !GetAtt TaskExecutionRoleBook.Arn
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
Cpu: 256
Memory: 512
ContainerDefinitions:
- Name: book-container
Image: !Ref ImageUri
PortMappings:
- ContainerPort: 3000
Privileged: false
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: !Ref LogGroup
awslogs-region: !Ref AWS::Region
awslogs-stream-prefix: book-service
LogGroup:
Type: AWS::Logs::LogGroup
DeletionPolicy: Delete
UpdateReplacePolicy: Delete
Properties:
LogGroupName: /ecs/book-service
RetentionInDays: 7
# Export output
Outputs:
taskDefinition:
Description: Task Definition
Value: !Ref BookTaskDefinition
Export:
Name: !Sub '${AWS::StackName}-book-task-def'

Book Service#

Let's create a book service with auto scaling policy, and integrate with the ALB via the aboved created target group.

AWSTemplateFormatVersion: '2010-09-09'
Description: ECS Service
Parameters:
NetworkStackName:
Type: String
Description: Name of the network stack
Default: 'cfn-network'
ClusterStackName:
Type: String
Description: Name of the ECS cluster stack to create the service
Default: 'cfn-ecs-cluster'
ALBStackName:
Type: String
Description: Name of the ALB stack to associate with the service
Default: 'cfn-load-balancer'
TaskDefinitionStackName:
Type: String
Description: Name of the task definition stack
Default: 'cfn-task-def-book'
DesiredCount:
Type: Number
Description: Desired number of tasks
Default: 1
ContainerName:
Type: String
Description: Name of the container
Default: 'book-container'
ServiceName:
Type: String
Description: Name of the service
Default: 'book-service'
Resources:
ECSService:
Type: AWS::ECS::Service
Properties:
ServiceName: !Ref ServiceName
TaskDefinition:
Fn::ImportValue: !Sub ${TaskDefinitionStackName}-book-task-def
Cluster:
Fn::ImportValue: !Sub ${ClusterStackName}-ecscluster
LaunchType: FARGATE
DesiredCount: !Ref DesiredCount
DeploymentController:
Type: ECS
NetworkConfiguration:
AwsvpcConfiguration:
# AssignPublicIp: ENABLED
SecurityGroups:
- Fn::ImportValue: !Sub ${NetworkStackName}-ecs-fargate-sg
Subnets:
- Fn::ImportValue: !Sub ${NetworkStackName}-private-subnet-1
- Fn::ImportValue: !Sub ${NetworkStackName}-private-subnet-2
LoadBalancers:
- ContainerName: !Ref ContainerName
ContainerPort: 3000
TargetGroupArn:
Fn::ImportValue: !Sub ${ALBStackName}-target-group
HealthCheckGracePeriodSeconds: 60
ServiceScalingTarget:
Type: AWS::ApplicationAutoScaling::ScalableTarget
Properties:
MaxCapacity: 4
MinCapacity: 1
ResourceId:
Fn::Join:
- /
- - service
- Fn::ImportValue: !Sub ${ClusterStackName}-ecscluster
- !Ref ServiceName
RoleARN: !GetAtt AutoScalingRole.Arn
ScalableDimension: ecs:service:DesiredCount
ServiceNamespace: ecs
ServiceScalingPolicy:
Type: AWS::ApplicationAutoScaling::ScalingPolicy
Properties:
PolicyName: AverageCPUUtilizationPolicy
PolicyType: TargetTrackingScaling
ScalingTargetId: !Ref ServiceScalingTarget
TargetTrackingScalingPolicyConfiguration:
TargetValue: 80.0
ScaleInCooldown: 60
ScaleOutCooldown: 60
PredefinedMetricSpecification:
PredefinedMetricType: ECSServiceAverageCPUUtilization
AutoScalingRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ecs.application-autoscaling.amazonaws.com
Action: 'sts:AssumeRole'
Policies:
- PolicyName: MyAWSApplicationAutoscalingECSServicePolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ecs:DescribeServices
- ecs:UpdateService
- cloudwatch:PutMetricAlarm
- cloudwatch:PutMetricAlarm
- cloudwatch:PutMetricAlarm
Resource: '*'

Code Pipeline#

Finally let's create a code pipeline with standard ECS deployment which is simple rolling update. It consists of codebuild, ecr, and codedeploy. Please take note the imagedefinitions.json

AWSTemplateFormatVersion: '2010-09-09'
Resources:
# IAM role for codepipeline
RoleForCodePipeline:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service: codepipeline.amazonaws.com
Policies:
- PolicyName: CodePipelinePolicyDemo
PolicyDocument: |
{
"Statement": [
{
"Action": [
"iam:PassRole"
],
"Resource": "*",
"Effect": "Allow",
"Condition": {
"StringEqualsIfExists": {
"iam:PassedToService": [
"cloudformation.amazonaws.com",
"elasticbeanstalk.amazonaws.com",
"ec2.amazonaws.com",
"ecs-tasks.amazonaws.com"
]
}
}
},
{
"Action": [
"codecommit:CancelUploadArchive",
"codecommit:GetBranch",
"codecommit:GetCommit",
"codecommit:GetRepository",
"codecommit:GetUploadArchiveStatus",
"codecommit:UploadArchive"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"codedeploy:CreateDeployment",
"codedeploy:GetApplication",
"codedeploy:GetApplicationRevision",
"codedeploy:GetDeployment",
"codedeploy:GetDeploymentConfig",
"codedeploy:RegisterApplicationRevision"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"codestar-connections:UseConnection"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"elasticbeanstalk:*",
"ec2:*",
"elasticloadbalancing:*",
"autoscaling:*",
"cloudwatch:*",
"s3:*",
"sns:*",
"cloudformation:*",
"rds:*",
"sqs:*",
"ecs:*"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"lambda:InvokeFunction",
"lambda:ListFunctions"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"opsworks:CreateDeployment",
"opsworks:DescribeApps",
"opsworks:DescribeCommands",
"opsworks:DescribeDeployments",
"opsworks:DescribeInstances",
"opsworks:DescribeStacks",
"opsworks:UpdateApp",
"opsworks:UpdateStack"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"cloudformation:CreateStack",
"cloudformation:DeleteStack",
"cloudformation:DescribeStacks",
"cloudformation:UpdateStack",
"cloudformation:CreateChangeSet",
"cloudformation:DeleteChangeSet",
"cloudformation:DescribeChangeSet",
"cloudformation:ExecuteChangeSet",
"cloudformation:SetStackPolicy",
"cloudformation:ValidateTemplate"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"codebuild:BatchGetBuilds",
"codebuild:StartBuild",
"codebuild:BatchGetBuildBatches",
"codebuild:StartBuildBatch"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Effect": "Allow",
"Action": [
"devicefarm:ListProjects",
"devicefarm:ListDevicePools",
"devicefarm:GetRun",
"devicefarm:GetUpload",
"devicefarm:CreateUpload",
"devicefarm:ScheduleRun"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"servicecatalog:ListProvisioningArtifacts",
"servicecatalog:CreateProvisioningArtifact",
"servicecatalog:DescribeProvisioningArtifact",
"servicecatalog:DeleteProvisioningArtifact",
"servicecatalog:UpdateProduct"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"cloudformation:ValidateTemplate"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ecr:DescribeImages"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"states:DescribeExecution",
"states:DescribeStateMachine",
"states:StartExecution"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"appconfig:StartDeployment",
"appconfig:StopDeployment",
"appconfig:GetDeployment"
],
"Resource": "*"
}
],
"Version": "2012-10-17"
}
# IAM role for codebuild
RoleForCodeBuild:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Statement:
- Action: sts:AssumeRole
Effect: Allow
Principal:
Service: codebuild.amazonaws.com
Policies:
- PolicyName: CodeBuildPolicyDemo
PolicyDocument: |
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Resource": [
"arn:aws:logs:<REGION>:<ACCOUNT_ID>:log-group:/aws/codebuild/debug",
"arn:aws:logs:<REGION>:<ACCOUNT_ID>:log-group:/aws/codebuild/debug:*"
],
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
]
},
{
"Effect": "Allow",
"Resource": [
"arn:aws:s3:::codepipeline-<REGION>-*"
],
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:GetObjectVersion",
"s3:GetBucketAcl",
"s3:GetBucketLocation"
]
},
{
"Effect": "Allow",
"Resource": [
"arn:aws:s3:::codebuild-demo-23062023",
"arn:aws:s3:::codebuild-demo-23062023/*",
"arn:aws:s3:::codepipeline-<REGION>-499144044954/*"
],
"Action": [
"s3:PutObject",
"s3:GetBucketAcl",
"s3:GetBucketLocation"
]
},
{
"Effect": "Allow",
"Action": [
"codebuild:CreateReportGroup",
"codebuild:CreateReport",
"codebuild:UpdateReport",
"codebuild:BatchPutTestCases",
"codebuild:BatchPutCodeCoverages"
],
"Resource": [
"arn:aws:codebuild:<REGION>:<ACCOUNT_ID>:report-group/debug-*"
]
}
]
}
# Codebuild project
CodeBuildProject:
Type: AWS::CodeBuild::Project
Properties:
Name: demo
ServiceRole: !Ref RoleForCodeBuild
Artifacts:
Type: S3
Location: codebuild-demo-23062023
Packaging: NONE
Environment:
Type: LINUX_CONTAINER
Image: aws/codebuild/amazonlinux2-x86_64-standard:5.0
ComputeType: BUILD_GENERAL1_SMALL
PrivilegedMode: false
LogsConfig:
CloudWatchLogs:
Status: ENABLED
GroupName: /aws/codebuild/debug
Source:
Type: NO_SOURCE
BuildSpec: |
version: 0.2
phases:
build:
commands:
- printf '[{"name":"%s","imageUri":"%s"}]' book-container <ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com/go-app:latest > imagedefinitions.json
- cat imagedefinitions.json
artifacts:
files:
- imagedefinitions.json
name: demo
Cache:
Type: NO_CACHE
Tags:
- Key: name
Value: demo
# CodePipeline
CodePipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
Name: demo-pipeline
RoleArn:
Fn::GetAtt:
- RoleForCodePipeline
- Arn
ArtifactStore:
Type: S3
Location: codepipeline-<REGION>-499144044954
Stages:
- Name: Source
Actions:
- Name: ECRSource
ActionTypeId:
Owner: AWS
Category: Source
Version: '1'
Provider: ECR
Configuration:
RepositoryName: go-app
ImageTag: latest
OutputArtifacts:
- Name: ecr-demo
- Name: Build
Actions:
- Name: CodeBuild
ActionTypeId:
Owner: AWS
Category: Build
Version: '1'
Provider: CodeBuild
RunOrder: 1
Configuration:
ProjectName: !Ref CodeBuildProject
InputArtifacts:
- Name: ecr-demo
OutputArtifacts:
- Name: demo
- Name: Deploy
Actions:
- Name: Deploy
ActionTypeId:
Owner: AWS
Category: Deploy
Version: '1'
Provider: ECS
Configuration:
ClusterName: demo
ServiceName: book-service
FileName: imagedefinitions.json
InputArtifacts:
- Name: demo

Here is the imagedefinitions.json

[
{
"name": "book-container",
"imageUri": "<ACCOUNT_ID>.dkr.ecr.<REGION>.amazonaws.com/go-app:latest"
}
]

And here is the build_spec.yaml

artifacts:
files:
- imagedefinitions.json
- imageDetail.json
version: '0.2'
phases:
install:
commands:
- export ACCOUNT_ID=<ACCOUNT_ID>
- export REGION=<REGION>
- export REPO_NAME=go-app
- echo ${ACCOUNT_ID} ${REGION} ${REPO_NAME}
pre_build:
commands:
- export TAG_NAME=$(date +%s)
build:
commands:
post_build:
commands:
- printf '[{"name":"%s","imageUri":"%s"}]' book-container ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/${REPO_NAME}:latest > imagedefinitions.json
- printf '{"ImageURI":"%s"}' ${ACCOUNT_ID}.dkr.ecr.${REGION}.amazonaws.com/${REPO_NAME}:latest > imageDetail.json
- cat imagedefinitions.json
- cat imageDetail.json