AWS ELB: 503 Service Temporarily Unavailable - amazon-web-services

I am trying to deploy microservices to AWS ECS following this example repo. Here, a load balancer is utilized for networking between different docker services. I have adjusted the cloudformation templates to my needs and have only deployed one service yet, the webserver. I want public access to the web interface and add other services this service is talking to subsequently. However, I currently struggle to get the webserver going using the URL of the load balancer. If I go directly to the EC2 instance using the public IP, I get access to the interface. However, if I go to the load balancer DNS, I get 503 Service Temporarily Unavailable. I have checked the AWS docs, but the webservice target group shows a registered target (the EC2 instance) with status healthy. What am I missing?
Parameters:
EnvironmentName:
Description: An environment name that will be prefixed to resource names
Type: String
VPC:
Type: AWS::EC2::VPC::Id
Description: Choose which VPC the Application Load Balancer should be deployed to
Subnets:
Description: Choose which subnets the Application Load Balancer should be deployed to
Type: AWS::EC2::Subnet::Id
PublicSubnet:
Description: Choose which public subnet the EC2 instance should be deployed to
Type: AWS::EC2::Subnet::Id
Resources:
LoadBalancerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub ${EnvironmentName}-loadbalancer
GroupDescription: Access to the load balancer that sits in front of ECS
VpcId: !Ref VPC
SecurityGroupIngress:
# Allow access from anywhere to our ECS services
- CidrIp: 0.0.0.0/0
IpProtocol: -1
Tags:
- Key: Name
Value: !Sub ${EnvironmentName}-LoadBalancers
LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: !Ref EnvironmentName
Subnets:
- !Ref Subnets
SecurityGroups:
- !Ref LoadBalancerSecurityGroup
Tags:
- Key: Name
Value: !Ref EnvironmentName
LoadBalancerListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref LoadBalancer
Port: 80
Protocol: HTTP
DefaultActions:
- Type: forward
TargetGroupArn: !Ref DefaultTargetGroup
DefaultTargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: !Sub ${EnvironmentName}-default
VpcId: !Ref VPC
Port: 80
Protocol: HTTP
ECSHostSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub ${EnvironmentName}-ecs-hosts
GroupDescription: Access to the ECS hosts and the tasks/containers that run on them
VpcId: !Ref VPC
SecurityGroupIngress:
# Only allow inbound access to ECS from the ELB
- SourceSecurityGroupId: !Ref LoadBalancerSecurityGroup
IpProtocol: -1
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
Tags:
- Key: Name
Value: !Sub ${EnvironmentName}-ECS-Hosts
ECSCluster:
Type: AWS::ECS::Cluster
Properties:
ClusterName: !Ref EnvironmentName
ECSRole:
Type: AWS::IAM::Role
Properties:
Path: /
RoleName: !Sub ${EnvironmentName}-ecs-role
AssumeRolePolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: 'sts:AssumeRole'
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role'
- 'arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM'
- 'arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy'
ECSInstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Path: /
Roles:
- !Ref ECSRole
EC2Webserver:
Type: AWS::EC2::Instance
Properties:
AvailabilityZone: eu-central-1a
ImageId: !Ref ECSAMI
InstanceType: !Ref InstanceType
IamInstanceProfile: !Ref ECSInstanceProfile
UserData:
Fn::Base64:
!Sub |
#!/bin/bash
# Add to cluster:
echo ECS_CLUSTER=${ECSCluster} >> /etc/ecs/ecs.config
echo ECS_ENABLE_AWSLOGS_EXECUTIONROLE_OVERRIDE=true >> /etc/ecs/ecs.config
SecurityGroupIds:
- !Ref ECSHostSecurityGroup
SubnetId: !Ref PublicSubnet
Tags:
- Key: Name
Value: !Sub ${EnvironmentName}
Service:
Type: AWS::ECS::Service
DependsOn: ListenerRule
Properties:
Cluster: !Ref Cluster
Role: !Ref ServiceRole
DesiredCount: !Ref DesiredCount
TaskDefinition: !Ref TaskDefinitionWebserver
LoadBalancers:
- ContainerName: !Sub ${EnvironmentName}-webserver
ContainerPort: 8080
TargetGroupArn: !Ref TargetGroup
TaskDefinitionWebserver:
Type: AWS::ECS::TaskDefinition
Properties:
Family: !Sub ${EnvironmentName}-webserver
ContainerDefinitions:
- Name: !Sub ${EnvironmentName}-webserver
Essential: true
Image: !Ref Image
Memory: 512
PortMappings:
- ContainerPort: 8080
HostPort: 80
TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name: !Sub ${EnvironmentName}-webserver
VpcId: !Ref VPC
Port: 80
Protocol: HTTP
Matcher:
HttpCode: 200-299
HealthCheckIntervalSeconds: 30
HealthCheckPath: /health
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 10
HealthyThresholdCount: 5
ListenerRule:
Type: AWS::ElasticLoadBalancingV2::ListenerRule
Properties:
ListenerArn: !Ref LoadBalancerListener
Priority: 1
Conditions:
- Field: path-pattern
Values:
- /
Actions:
- TargetGroupArn: !Ref TargetGroup
Type: forward
ServiceRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ecs-service-${AWS::StackName}
Path: /
AssumeRolePolicyDocument: |
{
"Statement": [{
"Effect": "Allow",
"Principal": { "Service": [ "ecs.amazonaws.com" ]},
"Action": [ "sts:AssumeRole" ]
}]
}
Policies:
- PolicyName: !Sub ecs-service-${AWS::StackName}
PolicyDocument:
{
"Version": "2012-10-17",
"Statement":
[
{
"Effect": "Allow",
"Action":
[
"ec2:AuthorizeSecurityGroupIngress",
"ec2:Describe*",
"elasticloadbalancing:DeregisterInstancesFromLoadBalancer",
"elasticloadbalancing:Describe*",
"elasticloadbalancing:RegisterInstancesWithLoadBalancer",
"elasticloadbalancing:DeregisterTargets",
"elasticloadbalancing:DescribeTargetGroups",
"elasticloadbalancing:DescribeTargetHealth",
"elasticloadbalancing:RegisterTargets"
],
"Resource": "*"
}
]
}
Outputs:
WebsiteServiceUrl:
Description: The URL endpoint for the website service
Value: !Join ["", [!GetAtt LoadBalancer.DNSName, "/"]]

Thanks everyone! I finally figured it out, what I had to do was adjusting the path as my service redirects on /. So I only changed the listener rule using a wildcard:
ListenerRule:
Type: AWS::ElasticLoadBalancingV2::ListenerRule
Properties:
ListenerArn: !Ref LoadBalancerListener
Priority: 1
Conditions:
- Field: path-pattern
Values:
- [/*]
Actions:
- TargetGroupArn: !Ref TargetGroup
Type: forward

SG egress rules
It looks the Security Groups (SG) has no egress rule defined. Both ALB and EC2.
LoadBalancerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub ${EnvironmentName}-loadbalancer
GroupDescription: Access to the load balancer that sits in front of ECS
VpcId: !Ref VPC
SecurityGroupIngress:
# Allow access from anywhere to our ECS services
- CidrIp: 0.0.0.0/0
IpProtocol: -1
I believe when you look at the outbound rules of the SG in the EC2 console, there would be no rule. If this is true, then the cause would be that traffic can come in to port 80 of ALB, but it cannot go out from ALB,
This is my theory. So please add a egress rule to verify?
LoadBalancerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub ${EnvironmentName}-loadbalancer
GroupDescription: Access to the load balancer that sits in front of ECS
VpcId: !Ref VPC
SecurityGroupIngress:
# Allow access from anywhere to our ECS services
- CidrIp: 0.0.0.0/0
IpProtocol: -1
SecurityGroupEgress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0 # <--- Maybe better change to VPC CIDR or ECS/EC2 subnet CIDR rather than any IP.
Regarding EC2, because SG is stateful, traffic can go through port 80 to reach the port 8080 of the docker container, and the response can go back through the SG because the SG knows it is the response of the incoming connection.
Whereas for ALB, the incoming connection from the Internet is terminated at ALB port 80, then a new outbound connection needs to be established to EC2 instance(s) port 80, hence needs an egress rule defined, if I am correct.
ECSHostSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: !Sub ${EnvironmentName}-ecs-hosts
GroupDescription: Access to the ECS hosts and the tasks/containers that run on them
VpcId: !Ref VPC
SecurityGroupIngress:
# Only allow inbound access to ECS from the ELB
- SourceSecurityGroupId: !Ref LoadBalancerSecurityGroup
IpProtocol: -1
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
However, if the EC2 instances need to install packages or create an outbound connection, then the EC2 SG needs egress rules too.
IAM
Regarding the IAM role for the ECS service, there are pre-defined AWS managed role, so I suppose better to use them?
AWS::ECS::Service
"Role": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2008-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "ecs.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
},
"ManagedPolicyArns": ["arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceRole"]
}
}
As AWS has introduced Service Linked Role now, it should be even better to use it.
Service-Linked Role for Amazon ECS
Prior to the introduction of a service-linked role for Amazon ECS, you were required to create an IAM role for your Amazon ECS services which granted Amazon ECS the permission it needed. This role is no longer required, however it is available if needed. For more information, see Legacy IAM Roles for Amazon ECS.
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/aws-service-role/ecs.amazonaws.com/AWSServiceRoleForECS"

Related

AWS HTTP API Gateway with custom link to private ALB

I have the following setup
Custom Domain api.foo.co.uk-> API Mapping to stage v1 -> HTTP API path ANY /{proxy+} -> Private VPC Link -> ALB Fargate
If I hit the Custom Domain api.foo.co.uk I get a 503 "message": "Service Unavailable"
If I hit the API direct p3dqjsdfszlv7.execute-api.eu-west-1.amazonaws.com/v1/ i get the same
In the CW for the API i see the following:
{
"auth_status":"-",
"aws_endpoint":"-",
"cognito_auth_provider":"-",
"cognito_auth_type":"-",
"cognito_identity_id":"-",
"cognito_identity_pool_id":"-",
"domain_name":"api.foo.co.uk",
"domain_prefix":"api",
"err_msg":"Service Unavailable",
"err_response":"INTEGRATION_NETWORK_FAILURE",
"err_string":" "Service Unavailable"",
"http_method":"GET",
"integration_error":"-",
"integration_error_msg":"-",
"integration_int_status":"200",
"integration_status":"-",
"path":"/v1/",
"principa_ord_id":"-",
"protocol":"HTTP/1.1",
"request_id":"cSJJ2h7BjoEEJ-g=",
"route_key":"ANY /{proxy+}",
"source_ip":"22.22.103.68",
"stage":"v1",
"status":"503",
"time":"16/Mar/2021:14:08:24 +0000",
"user":"-",
"user_agent":"insomnia/2021.1.0"}
Anyone have any idea what the issue might be ? I have looked and can not find anything on the error message err_response":"INTEGRATION_NETWORK_FAILURE" or what the cause might be.
I have also enabled the access logs on the ALB but they are blank so I am assuming its not getting as far as the ALB
I had the same problem of INTEGRATION_NETWORK_FAILURE. I managed to find a more informative error message by including all of the $context.integration* variables in the access logs (see https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-logging-variables.html). In my case there was an error message that said "Request failed due to a network error communicating with the endpoint".
I can't tell what was the exact cause or what made it go away, but I can share some CloudFormation snippets of the final working setup:
ALB setup - listening for HTTP requests on port 80:
ApplicationLoadBalancerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: !Sub "${EnvironmentName} ALB security group"
GroupName: !Sub "${EnvironmentName}-load-balancer-sg"
VpcId:
'Fn::ImportValue': !Sub "${EnvironmentName}:VPC"
SecurityGroupIngress:
- CidrIp: "0.0.0.0/0"
IpProtocol: "tcp"
FromPort: 80
ToPort: 80
SecurityGroupEgress:
- CidrIp: "0.0.0.0/0"
IpProtocol: "-1"
SharedApplicationLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: !Sub "${EnvironmentName}-shared-lb"
Scheme: "internal"
Type: "application"
Subnets:
- 'Fn::ImportValue': !Sub "${EnvironmentName}:${LBSubnetType}1"
- 'Fn::ImportValue': !Sub "${EnvironmentName}:${LBSubnetType}2"
SecurityGroups:
- !Ref ApplicationLoadBalancerSecurityGroup
IpAddressType: "ipv4"
SharedApplicationLoadBalancerListener:
Type: AWS::ElasticLoadBalancingV2::Listener
DependsOn:
- SharedApplicationLoadBalancer
Properties:
LoadBalancerArn: !Ref SharedApplicationLoadBalancer
Protocol: "HTTP"
Port: 80
DefaultActions:
- Type: fixed-response
FixedResponseConfig:
StatusCode: 404
MessageBody: Shared ALB has no such route
ContentType: text/plain
Per-service ALB target group:
SharedAlbServiceXTargetGroup:
Type: "AWS::ElasticLoadBalancingV2::TargetGroup"
Properties:
Name: !Sub "${EnvironmentName}-alb-${ServiceName}-tg"
HealthCheckIntervalSeconds: 30
HealthCheckPath: "/ping"
HealthCheckProtocol: "HTTP"
HealthyThresholdCount: 2
Port: 8080
Protocol: "HTTP"
UnhealthyThresholdCount: 2
VpcId:
"Fn::ImportValue": !Sub "${EnvironmentName}:VPC"
TargetType: "ip"
SharedAlbServiceXListenerRule:
Type: "AWS::ElasticLoadBalancingV2::ListenerRule"
Properties:
Actions:
- Type: "forward"
TargetGroupArn: !Ref SharedAlbServiceXTargetGroup
Conditions:
- Field: "host-header"
HostHeaderConfig:
Values:
- !Ref HttpApiCustomDomain
ListenerArn: !Ref SharedApplicationLoadBalancerListener
Priority: !Ref SharedAlbListenerRulePriority
VPC link:
PrivateApiGatewayVpcLinkSecurityGroup:
Condition: PrivateAccess
Type: "AWS::EC2::SecurityGroup"
Properties:
VpcId:
'Fn::ImportValue': !Sub "${EnvironmentName}:VPC"
GroupName: !Sub "${EnvironmentName}-apigw-vpclink"
GroupDescription: !Sub "SG for API Gateway private VPC link in ${EnvironmentName} environment"
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
PrivateApiGatewayVpcLink:
Condition: PrivateAccess
Type: "AWS::ApiGatewayV2::VpcLink"
Properties:
Name: !Sub "${EnvironmentName}-api-gateway-vpclink"
SecurityGroupIds:
- !Ref PrivateApiGatewayVpcLinkSecurityGroup
SubnetIds:
- "Fn::ImportValue": !Sub "${EnvironmentName}:PrivateSubnet1"
- "Fn::ImportValue": !Sub "${EnvironmentName}:PrivateSubnet2"
API Gateway Integration
HttpApiIntegration:
Type: "AWS::ApiGatewayV2::Integration"
Properties:
ApiId: !Ref HttpApi
Description: !Sub "Private ALB Integration for ${ServiceName} in ${EnvironmentName} env"
IntegrationType: "HTTP_PROXY"
IntegrationMethod: "ANY"
ConnectionType: "VPC_LINK"
ConnectionId: !Ref PrivateApiGatewayVpcLink
IntegrationUri: !Ref SharedApplicationLoadBalancerListener
PayloadFormatVersion: "1.0"

Ec2 and Elastic load Balancer

Please consider this scenario:
I want to deploy a CloudFormation stack with the basic required resources for an Elastic Load Balancer to route traffic to the EC2.
I have stripped bellow template from a template that has a VPC, InternetGateway, VPCGatewayAttachment, RouteTable, Route, SubnetRouteTableAssociation, attached to it.
I did so because I thought AWS will provision a default VPC for me.
Mappings:
NetworkToSubnet:
"10.0.0.0":
PubSubnetZoneA: "10.0.10.0/24"
PrivSubnetZoneA: "10.0.20.0/24"
PubSubnetZoneB: "10.0.30.0/24"
PrivSubnetZoneB: "10.0.40.0/24"
Resources:
# Internet accessable subnet in the first availability zone
PubSubnetZoneA:
Type: 'AWS::EC2::Subnet'
Properties:
AvailabilityZone:
Fn::Select:
- '0'
- Fn::GetAZs:
Ref: 'AWS::Region'
CidrBlock:
Fn::FindInMap:
- NetworkToSubnet
- '10.0.0.0'
- PubSubnetZoneA
MapPublicIpOnLaunch: 'True'
Tags:
- Key: 'Name'
Value:
'Fn::Join': [ ':', [ 'Public', 'Zone A', !Ref 'AWS::StackName' ] ]
# Non-internet accessable subnet in the first availability zone
PrivSubnetZoneA:
Type: 'AWS::EC2::Subnet'
Properties:
AvailabilityZone:
Fn::Select:
- '0'
- Fn::GetAZs:
Ref: 'AWS::Region'
CidrBlock:
Fn::FindInMap:
- NetworkToSubnet
- '10.0.0.0'
- PrivSubnetZoneA
MapPublicIpOnLaunch: 'False'
Tags:
- Key: 'Name'
Value:
'Fn::Join': [ ':', [ 'Private', 'Zone A', !Ref 'AWS::StackName' ] ]
# Internet accessable subnet in the second availability zone
PubSubnetZoneB:
Type: 'AWS::EC2::Subnet'
Properties:
AvailabilityZone:
Fn::Select:
- '1'
- Fn::GetAZs:
Ref: 'AWS::Region'
CidrBlock:
Fn::FindInMap:
- NetworkToSubnet
- '10.0.0.0'
- PubSubnetZoneB
MapPublicIpOnLaunch: 'True'
Tags:
- Key: 'Name'
Value:
'Fn::Join': [ ':', [ 'Public', 'Zone B', !Ref 'AWS::StackName' ] ]
# Non-internet accessable subnet in the second availability zone
PrivSubnetZoneB:
Type: 'AWS::EC2::Subnet'
Properties:
AvailabilityZone:
Fn::Select:
- '1'
- Fn::GetAZs:
Ref: 'AWS::Region'
CidrBlock:
Fn::FindInMap:
- NetworkToSubnet
- '10.0.0.0'
- PrivSubnetZoneB
MapPublicIpOnLaunch: 'False'
Tags:
- Key: 'Name'
Value:
'Fn::Join': [ ':', [ 'Private', 'Zone B', !Ref 'AWS::StackName' ] ]
# EC2 Security Group Allowing Port 22 and 80 from anywhere
EC2SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: 'SSH and Port 80'
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 80
ToPort: 80
SourceSecurityGroupId:
Ref: ELBSecurityGroup
# ELB Security Group allowing Port 80 from anywhere
ELBSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: 'SSH and Port 80'
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
# Linux Instance with Apache running on Port 80
AmazonLinuxInstance:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-0ffd774e02309201f
InstanceInitiatedShutdownBehavior: stop
InstanceType: t2.nano
Monitoring: 'true'
NetworkInterfaces:
- AssociatePublicIpAddress: 'true'
DeviceIndex: '0'
GroupSet:
- !Ref EC2SecurityGroup
Tenancy: default
UserData:
Fn::Base64: !Sub |
#!/bin/bash -xe
cd /tmp
yum update -y
yum install -y httpd24
echo "Healthy" > /var/www/html/index.html
service httpd start
/opt/aws/bin/cfn-signal \
-e $? \
--stack ${AWS::StackName} \
--resource AmazonLinuxInstance \
--region ${AWS::Region}
# Target Group
EC2TargetGroup:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
HealthCheckIntervalSeconds: 30
HealthCheckProtocol: HTTP
HealthCheckTimeoutSeconds: 15
HealthyThresholdCount: 5
Matcher:
HttpCode: '200'
Name: EC2TargetGroup
Port: 80
Protocol: HTTP
TargetGroupAttributes:
- Key: deregistration_delay.timeout_seconds
Value: '20'
Targets:
- Id:
Ref: AmazonLinuxInstance
Port: 80
UnhealthyThresholdCount: 3
Tags:
- Key: Name
Value: EC2TargetGroup
- Key: Port
Value: 80
#ELB (ALB)
ALBListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
DefaultActions:
- Type: forward
TargetGroupArn:
Ref: EC2TargetGroup
LoadBalancerArn:
Ref: ApplicationLoadBalancer
Port: 80
Protocol: HTTP
ApplicationLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Scheme: internet-facing # or internal
Outputs:
ALBHostName:
Description: 'Application Load Balancer Hostname'
Value:
!GetAtt ApplicationLoadBalancer.DNSName
EC2Instance:
Description: 'EC2 Instance'
Value:
Ref: AmazonLinuxInstance
EC2TargetGroup:
Description: 'EC2 Target Group'
Value:
Ref: EC2TargetGroup
ApplicationLoadBalancer:
Description: 'Application Load Balancer'
Value:
Ref: ApplicationLoadBalancer
When provisioning the stack provided I get the following errors:
The following resource(s) failed to create: [PubSubnetZoneB, PrivSubnetZoneA,
PrivSubnetZoneB, ApplicationLoadBalancer, ELBSecurityGroup, PubSubnetZoneA]. Rollback
requested by user.
At least two subnets in two different Availability Zones must be specified (Service:
AmazonElasticLoadBalancing; Status Code: 400; Error Code: ValidationError; Request ID: 000-
0000-0000-0000-0000; Proxy: null)
What are the minimum required resources for an ELB to have, so it can direct traffic to an EC2?
The error is saying At least two subnets in two different Availability Zones must be specified.
From AWS::ElasticLoadBalancingV2::LoadBalancer - AWS CloudFormation:
Subnets
The IDs of the subnets. You can specify only one subnet per Availability Zone. You must specify either subnets or subnet mappings.
[Application Load Balancers] You must specify subnets from at least two Availability Zones. When you specify subnets for an existing Application Load Balancer, they replace the previously enabled subnets.
Thus, you need to add Subnets to your Load Balancer definition. It must be at least two subnets, each in different Availability Zones.

Fargate service not respecting security groups

I am unable to connect from a Fargate container to an RDS instance when its ingress is limited through security groups. I can connect with lambdas though.
The container has no issue hitting SQS, or the internet. Only has issues hitting the RDS endpoint.
Here is an excerpt from the template, where the database ingress is open. Fargate can connect without issue.
Service:
Type: AWS::ECS::Service
Properties:
ServiceName: !Ref ServiceName
Cluster: !Ref Cluster
TaskDefinition: !Ref TaskDefinition
PlatformVersion: 1.3.0
DeploymentConfiguration:
MinimumHealthyPercent: 100
MaximumPercent: 200
DesiredCount: 0
LaunchType: FARGATE
NetworkConfiguration:
AwsvpcConfiguration:
AssignPublicIp: DISABLED
Subnets:
- !Ref PrivateSubnet1
SecurityGroups:
- !Ref DatabaseAccessSecurityGroup
DatabaseInstance:
Type: AWS::RDS::DBInstance
Properties:
Engine: mysql
EngineVersion: 8.0.16
AvailabilityZone: !GetAtt PrivateSubnet1.AvailabilityZone
PubliclyAccessible: false
...
VPCSecurityGroups:
- !Ref DatabaseSecurityGroup
DatabaseSubnetGroup:
Type: AWS::RDS::DBSubnetGroup
Properties:
DBSubnetGroupDescription: Cloudformation managed Db subnet group
SubnetIds:
- !Ref PrivateSubnet1
- !Ref PrivateSubnet2
DatabaseSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
VpcId: !Ref VPC
GroupName: database-sg
GroupDescription: Database security group
SecurityGroupIngress:
- Description: Access to RDS
# allowing all works with Fargate
CidrIp: 0.0.0.0/0
FromPort: 3306
ToPort: 3306
IpProtocol: tcp
DatabaseAccessSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: database-access-sg
GroupDescription: Security group for accessing db
VpcId: !Ref VPC
But if I change the DatabaseSecurityGroup Group ingress to only allow ingress through DatabaseAccessSecurityGroup I get errors when trying to connect through Fargate. Lambdas using the same security group have no issue.
SecurityGroupIngress:
- Description: Access to RDS
CidrIp: 0.0.0.0/0
SourceSecurityGroupId: !GetAtt DatabaseAccessSecurityGroup.GroupId
FromPort: 3306
ToPort: 3306
IpProtocol: tcp
Is there any way to get the Fargate Service to respect security group rules?
This issue was occurring because I was using a Service in the Cloudformation template, but spinning up the tasks via ecs.runTask, which overrode the security groups in the Service.

CloudFormation: Unable to access the EC2 instance created using CloudFomation through public DNS

I am deploying an EC2 instance using CloudFormation. Then I installed apache and uploaded the files to EC2 instance after deployment. When the instance is deployed I cannot access it using public DNS from browser.
This is my EC2 instance resource and its security group.
WebServerInstance:
Type: AWS::EC2::Instance
Properties:
InstanceType: !Ref InstanceType
KeyName: !Ref KeyName
SubnetId: !Ref PublicSubnet1
ImageId:
Fn::FindInMap:
- AWSRegionArch2AMI
- Ref: AWS::Region
- Fn::FindInMap:
- AWSInstanceType2Arch
- Ref: InstanceType
- Arch
AvailabilityZone: !Select
- 0
- Fn::GetAZs: !Ref AWS::Region
WebServerSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Enable HTTP access via port 80
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: '80'
ToPort: '80'
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: '22'
ToPort: '22'
CidrIp:
Ref: SSHLocation
VpcId: !Ref Vpc
When I access it from the browser, it just keeps loading loading and loading. I set the inbound rules on the security group too. What is wrong with it and how can I fix it?
This is my public DNS,
http://ec2-3-{xxx-xxx-xx}.eu-west-1.compute.amazonaws.com/
This is the Public subnet resource.
PublicSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref Vpc
CidrBlock: !Select [ 0, !Cidr [ !Ref VpcCidr, 12, 8 ] ]
MapPublicIpOnLaunch: True
AvailabilityZone: !Select
- 0
- Fn::GetAZs: !Ref AWS::Region
There is a route table for public subnet.
In the internet gateway console, there is only one gateway and which is not attached to the VPC in the template. Can this be the issue?
Edit
I got this error
There are several reasons outside the security group allowing access. The following should be checked:
Check your instances subnet has a route within its route table for 0.0.0.0/0 which has a destination of a internet gateway.
Each subnet will have an available route table (this will be the default route table if you did not specify one).
This can be completed by using the CloudFormation below
InternetGateway:
Type: AWS::EC2::InternetGateway
AttachGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId:
Ref: VPC
InternetGatewayId:
Ref: InternetGateway
RouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId:
Ref: myVPC
Route:
Type: AWS::EC2::Route
DependsOn: InternetGateway
Properties:
RouteTableId:
Ref: RouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId:
Ref: InternetGateway
SubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId:
Ref: Subnet
RouteTableId:
Ref: RouteTable
If you updated the default NACL make sure you added both port 80 and ephemeral ports to the rules.
Make sure apache is running on the host (not just installed). This can be done by running systemctl start apache on debian based OS or systemctl start httpd on a RHEL based.

Create EC2 in default public subnet only using cloudformation

I have CF template which is creating EC2 machine.
AWSTemplateFormatVersion: 2010-09-09
Mappings:
InstanceAMI:
# ubuntu 18.04
us-west-2:
ami: 'ami-0bbe6b35405ecebdb'
us-east-1:
ami: 'ami-0ac019f4fcb7cb7e6'
Parameters:
Endpoint:
Type: String
# TODO edit the default value
Description:
Resources:
NodeInstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
Path: "/"
Roles:
- !Ref NodeInstanceRole
NodeInstanceRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- sts:AssumeRole
Path: "/"
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AdministratorAccess
CdpDeplSvcSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Access Deployment service
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 8080
ToPort: 8080
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
Tags:
- Key: Name
Value: 'Access Deployment'
DeploymentMachine:
Type: AWS::EC2::Instance
Properties:
# AvailabilityZone: us-east-1a
ImageId: !FindInMap [InstanceAMI, !Ref "AWS::Region", ami]
InstanceType: 't2.small'
KeyName: 'key'
Tags:
- Key: Name
Value: 'Deployment'
BlockDeviceMappings:
- DeviceName: "/dev/sda1"
Ebs:
# VolumeType: "io1"
# Iops: "200"
DeleteOnTermination: "true"
VolumeSize: "30"
NetworkInterfaces:
- DeviceIndex: 0
AssociatePublicIpAddress: 'true'
DeleteOnTermination: 'true'
GroupSet:
- !GetAtt CdpDeplSvcSecurityGroup.GroupId
IamInstanceProfile: !Ref NodeInstanceProfile
It executing correctly. But the problem I am facing is sometimes it creates in default private subnet, sometimes in default public subnet.
I want to deploy this machine in the default public subnet only. I don't want to pass VPC id or subnet id as parameter. For that, what I have change here.
This will just put it into a random subnet - you need to hardcode subnet, or specify subnet through a parameter and then reference the parameter - this can provide you with some flexibility for varying the subnet per customer.
Potentially, during deployment of your stack, you could script the deployment, using the AWS CLI to get all public subnets, and pass one in as a parameter into your cloudformation stack.