Skip to content

Instantly share code, notes, and snippets.

@Femi-lawal
Last active July 7, 2022 07:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Femi-lawal/1a542fc3f3f782db615d2bb028e49cc6 to your computer and use it in GitHub Desktop.
Save Femi-lawal/1a542fc3f3f782db615d2bb028e49cc6 to your computer and use it in GitHub Desktop.
AWSTemplateFormatVersion: "2010-09-09"
Description: >
This template creates ECS Fargate Services.
############################################################
# PARAMETERS RECORDS BLOCK
############################################################
Parameters:
ImageTag:
Type: String
Default: latest
ServiceName:
Type: String
Default: rest-api
ContainerPort:
Type: Number
Default: 80
ContainerCpu:
Type: String
Default: 256
ContainerMemory:
Type: String
Default: 1GB
HealthCheckPath:
Type: String
Default: /
MinContainers:
Type: Number
Default: 1
VpcCIDR:
Description: "IP range for this VPC"
Type: "String"
Default: "10.0.0.0/16"
PublicSubnetOneCIDR:
Description: "IP range for the public subnet in the first Availability Zone"
Type: "String"
Default: "10.0.1.0/24"
PublicSubnetTwoCIDR:
Description: "IP range for the public subnet in the second Availability Zone"
Type: "String"
Default: "10.0.2.0/24"
Resources:
############################################################
# VPC & PUBLIC SUBNETS
############################################################
RestVPC:
Type: "AWS::EC2::VPC"
Properties:
CidrBlock: !Ref "VpcCIDR"
Tags:
- Key: "Name"
Value: "Rest-API-VPC"
####### Create Public Subnet #######
PublicSubnetOne:
Type: "AWS::EC2::Subnet"
Properties:
VpcId: !Ref RestVPC
CidrBlock: !Ref "PublicSubnetOneCIDR"
AvailabilityZone: !Select ["0", !GetAZs ]
MapPublicIpOnLaunch: "True"
Tags:
- Key: "Name"
Value: !Sub "${PublicSubnetOneCIDR}-PublicSubnetOne"
PublicSubnetTwo:
Type: "AWS::EC2::Subnet"
Properties:
VpcId: !Ref RestVPC
CidrBlock: !Ref "PublicSubnetTwoCIDR"
AvailabilityZone: !Select ["1", !GetAZs ]
MapPublicIpOnLaunch: "True"
Tags:
- Key: "Name"
Value: !Sub "${PublicSubnetTwoCIDR}-PublicSubnetTwo"
######## Create Public Route Table #######
PublicRouteTable1:
Type: "AWS::EC2::RouteTable"
Properties:
VpcId: !Ref RestVPC
Tags:
- Key: "Name"
Value: "PublicRoute1"
PublicRouteTable2:
Type: "AWS::EC2::RouteTable"
Properties:
VpcId: !Ref RestVPC
Tags:
- Key: "Name"
Value: "PublicRoute2"
######## Create Internet Gateway #######
InternetGateway:
Type: "AWS::EC2::InternetGateway"
Properties:
Tags:
- Key: "Name"
Value: "InternetGateway"
######## Attach Internet Gateway to VPC #######
GatewayToInternet:
Type: "AWS::EC2::VPCGatewayAttachment"
Properties:
VpcId: !Ref RestVPC
InternetGatewayId: !Ref "InternetGateway"
######## Route-out Public Route Table to Internet Gateway (Internet connection) #######
PublicRouteIGW1:
Type: "AWS::EC2::Route"
DependsOn: "GatewayToInternet"
Properties:
RouteTableId: !Ref "PublicRouteTable1"
DestinationCidrBlock: "0.0.0.0/0"
GatewayId: !Ref "InternetGateway"
PublicRouteIGW2:
Type: "AWS::EC2::Route"
DependsOn: "GatewayToInternet"
Properties:
RouteTableId: !Ref "PublicRouteTable2"
DestinationCidrBlock: "0.0.0.0/0"
GatewayId: !Ref "InternetGateway"
######## Associate Public Route Table with Public Subnet1 & Subnet2 #######
PublicSubnetOneRouteTableAssociation:
Type: "AWS::EC2::SubnetRouteTableAssociation"
Properties:
SubnetId: !Ref "PublicSubnetOne"
RouteTableId: !Ref "PublicRouteTable1"
PublicSubnetTwoRouteTableAssociation:
Type: "AWS::EC2::SubnetRouteTableAssociation"
Properties:
SubnetId: !Ref "PublicSubnetTwo"
RouteTableId: !Ref "PublicRouteTable2"
######## Create Custom Network ACL #######
PublicNetworkACL:
Type: "AWS::EC2::NetworkAcl"
Properties:
VpcId: !Ref RestVPC
Tags:
- Key: "Name"
Value: "PublicNetworkACL"
PublicInboundPublicACL:
Type: "AWS::EC2::NetworkAclEntry"
Properties:
NetworkAclId: !Ref PublicNetworkACL
RuleNumber: "100"
Protocol: "-1"
RuleAction: "allow"
Egress: "false"
CidrBlock: "0.0.0.0/0"
PublicOutboundPublicACL:
Type: "AWS::EC2::NetworkAclEntry"
Properties:
NetworkAclId: !Ref PublicNetworkACL
RuleNumber: "100"
Protocol: "-1"
RuleAction: "allow"
Egress: "true"
CidrBlock: "0.0.0.0/0"
######## Associate Public Subnet to Network ACL #######
PublicSubnetOneNetworkAclAssociation:
Type: "AWS::EC2::SubnetNetworkAclAssociation"
Properties:
SubnetId: !Ref "PublicSubnetOne"
NetworkAclId: !Ref "PublicNetworkACL"
PublicSubnetTwoNetworkAclAssociation:
Type: "AWS::EC2::SubnetNetworkAclAssociation"
Properties:
SubnetId: !Ref "PublicSubnetTwo"
NetworkAclId: !Ref "PublicNetworkACL"
############################################################
# ECS CLUSTER BLOCK
############################################################
Cluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: !Join ["-", [!Ref ServiceName, cluster]]
############################################################
# ECS TASK DEFINITION
############################################################
TaskDefinition:
Type: AWS::ECS::TaskDefinition
DependsOn: LogGroup
Properties:
# Name of the task definition. Subsequent versions of the task definition are grouped together under this name.
Family: !Join ["-", [!Ref ServiceName, TaskDefinition]]
# awsvpc is required for Fargate
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
Cpu: !Ref ContainerCpu
Memory: !Ref ContainerMemory
ExecutionRoleArn: !Ref ExecutionRole
TaskRoleArn: !Ref TaskRole
ContainerDefinitions:
- Name: !Ref ServiceName
Image:
!Join [
".",
[
!Ref AWS::AccountId,
"dkr.ecr",
!Ref AWS::Region,
!Sub "amazonaws.com/ecr-repository:${ImageTag}",
],
]
PortMappings:
- ContainerPort: !Ref ContainerPort
# Send logs to CloudWatch Logs
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-region: !Ref AWS::Region
awslogs-group: !Ref LogGroup
awslogs-stream-prefix: ecs
############################################################
# IAM ROLE
############################################################
ExecutionRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Join ["-", [!Ref ServiceName, ExecutionRole]]
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: "sts:AssumeRole"
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
TaskRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Join ["-", [!Ref ServiceName, TaskRole]]
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: "sts:AssumeRole"
Policies:
- PolicyName: "systems-manager"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- "ssmmessages:CreateControlChannel"
- "ssmmessages:CreateDataChannel"
- "ssmmessages:OpenControlChannel"
- "ssmmessages:OpenDataChannel"
Resource: "*"
- Effect: "Allow"
Action:
- "kms:Decrypt"
Resource: !Sub "arn:aws:kms:us-east-1:${AWS::AccountId}:key/${KMSKey}"
############################################################
# KMS KEY
############################################################
KMSKey:
Type: AWS::KMS::Key
Properties:
Description: Key needed to encrypt docker exec data channel
KeyPolicy:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
AWS: !Sub "arn:aws:iam::${AWS::AccountId}:root"
Action:
- "kms:*"
Resource: "*"
############################################################
# SECURITY GROUP
############################################################
ContainerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: !Join ["-", [!Ref ServiceName, ContainerSecurityGroup]]
VpcId: !Ref RestVPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: !Ref ContainerPort
ToPort: !Ref ContainerPort
SourceSecurityGroupId: !Ref LoadBalancerSecurityGroup
LoadBalancerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription:
!Join ["-", [!Ref ServiceName, LoadBalancerSecurityGroup]]
VpcId: !Ref RestVPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
############################################################
# ECS SERVICE
############################################################
Service:
Type: AWS::ECS::Service
# This dependency is needed so that the load balancer is setup correctly in time
DependsOn:
- ApplicationLoadBalancer
- ALBHTTPListener
- ListenerRule
- TargetGroup
Properties:
ServiceName: !Ref ServiceName
Cluster: !Ref Cluster
TaskDefinition: !Ref TaskDefinition
EnableExecuteCommand: true
DeploymentConfiguration:
MinimumHealthyPercent: 100
MaximumPercent: 200
DesiredCount: !Ref MinContainers
HealthCheckGracePeriodSeconds: 300
LaunchType: FARGATE
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: ENABLED
Subnets:
- !Ref PublicSubnetOne
- !Ref PublicSubnetTwo
SecurityGroups:
- !Ref ContainerSecurityGroup
LoadBalancers:
- ContainerName: !Ref ServiceName
ContainerPort: !Ref ContainerPort
TargetGroupArn: !Ref TargetGroup
############################################################
# ECS TARGET GROUP
############################################################
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
HealthCheckIntervalSeconds: 10
HealthCheckPath: !Ref HealthCheckPath
HealthCheckTimeoutSeconds: 5
UnhealthyThresholdCount: 2
HealthyThresholdCount: 2
Name: !Join ["-", [!Ref ServiceName, TargetGroup]]
Port: !Ref ContainerPort
Protocol: HTTP
TargetGroupAttributes:
- Key: deregistration_delay.timeout_seconds
Value: 60
TargetType: ip
VpcId: !Ref RestVPC
Matcher:
HttpCode: 200
############################################################
# APPLICATION LOAD BALANCER
############################################################
ApplicationLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
LoadBalancerAttributes:
- Key: idle_timeout.timeout_seconds
Value: 60
Name: !Join ["-", [!Ref ServiceName, ApplicationLoadBalancer]]
Scheme: internet-facing
SecurityGroups:
- !Ref LoadBalancerSecurityGroup
Subnets:
- !Ref PublicSubnetOne
- !Ref PublicSubnetTwo
ALBHTTPListener:
Type: "AWS::ElasticLoadBalancingV2::Listener"
Properties:
LoadBalancerArn: !Ref ApplicationLoadBalancer
DefaultActions:
- FixedResponseConfig:
StatusCode: 200
Type: fixed-response
Port: "80"
Protocol: HTTP
# ---- Applcation Load Balancer Listener Rule ---- #
ListenerRule:
Type: "AWS::ElasticLoadBalancingV2::ListenerRule"
Properties:
Actions:
- Type: forward
TargetGroupArn: !Ref TargetGroup
Conditions:
- Field: path-pattern
Values:
- "*"
ListenerArn: !Ref ALBHTTPListener
Priority: 1
############################################################
# CLOUDWATCH LOG GROUPS
############################################################
LogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Join ["", [/ecs/, !Ref ServiceName, TaskDefinition]]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment