This is a CloudFormation template to create application load balancer.
I'm getting an error that says- Value of property Subnets must be of type List of String.
Are the security group entities declared rightly?
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
Name:
Description: Name of the project
Type: String
Environment:
Description: Environment of the Application Load balancer
Type: String
PublicSubnet:
Description: Subnet
Type: List<AWS::EC2::Subnet::Id>
Vpc:
Description: VPC
Type: AWS::EC2::VPC::Id
Resources:
SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: ElastiCache Security Group
VpcId: !Ref Vpc
SecurityGroupIngress:
-
IpProtocol: tcp
FromPort: "80"
ToPort: "80"
FromPort: "443"
ToPort: "443"
CidrIp: "0.0.0.0/0"
Tags:
-
Key: Name
Value: "App-SG"
ApplicationLB:
Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer'
Properties:
IpAddressType: ipv4
Name: Test-ALB
Scheme: internet-facing
SecurityGroups:
- !Ref SecurityGroup
Subnets:
- !Ref PublicSubnet
Tags:
- Key: Name
Value: Test-ALB
Type: application
ALBListener:
Type: 'AWS::ElasticLoadBalancingV2::Listener'
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref ALBTargetGroup
LoadBalancerArn:
Ref: ApplicationLB
Port: '80'
Protocol: HTTP
ALBTargetGroup:
Type: 'AWS::ElasticLoadBalancingV2::TargetGroup'
Properties:
HealthCheckIntervalSeconds: 30
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 3
Port: 80
Protocol: HTTP
UnhealthyThresholdCount: 5
VpcId: !Ref Vpc
Also it would be helpful to review the entire template, incase there are more errors.
Your PublicSubnet is already a list. So you can just do:
Subnets: !Ref PublicSubnet
My AWS CloudFormation template for Application load balancer is throwing this error: Failed to retrieve external values.
Want help in rectifying this issue. I'm not sure where the error is occuring from.
I'm guessing the error might be in the certificate parameter section or the tags, maybe the !Sub value is not taking in the value.
AWSTemplateFormatVersion: '2010-09-09'
Parameters:
Name:
Description: Name of the project
Type: String
EnvironmentName:
Description: Environment of the Application Load balancer
Type: String
PublicSubnet:
Description: Subnet for creating the Application Load balancer
Type: List<AWS::EC2::Subnet::Id>
Vpc:
Description: VPC in which the resources are present
Type: AWS::EC2::VPC::Id
Certificate:
Description: Arn of the ssl certificate for HTTPS listener
Type: AWS::CertificateManager::Certificate::Arn
Resources:
SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: ALB Security Group
VpcId: !Ref Vpc
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: "80"
ToPort: "80"
CidrIp: "0.0.0.0/0"
- IpProtocol: tcp
FromPort: "443"
ToPort: "443"
CidrIp: "0.0.0.0/0"
Tags:
-
Key: Name
Value: !Sub ${EnvironmentName}-SG
ApplicationLB:
Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer'
Properties:
IpAddressType: ipv4
Name: Test-ALB
Scheme: internet-facing
SecurityGroups:
- !Ref SecurityGroup
Subnets: !Ref PublicSubnet
Tags:
- Key: Name
Value: !Sub ${EnvironmentName}-ALB
Type: application
HTTPSListener:
Type: "AWS::ElasticLoadBalancingV2::Listener"
Properties:
LoadBalancerArn: !Ref ApplicationLB
Port: 443
Protocol: "HTTPS"
SslPolicy: "ELBSecurityPolicy-2016-08"
Certificates:
-
CertificateArn: !Ref Certificate
DefaultActions:
-
Order: 1
Type: "fixed-response"
FixedResponseConfig:
ContentType: "text/plain"
MessageBody: "Please enter proper domain"
StatusCode: "200"
HTTPListener:
Type: "AWS::ElasticLoadBalancingV2::Listener"
Properties:
LoadBalancerArn: !Ref ApplicationLB
Port: 80
Protocol: "HTTP"
DefaultActions:
-
Order: 1
RedirectConfig:
Protocol: "HTTPS"
Port: "443"
Host: "#{host}"
Path: "/#{path}"
Query: "#{query}"
StatusCode: "HTTP_301"
Type: "redirect"
ALBTargetGroup:
Type: 'AWS::ElasticLoadBalancingV2::TargetGroup'
Properties:
HealthCheckIntervalSeconds: 30
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 3
Port: 80
Protocol: HTTP
UnhealthyThresholdCount: 5
VpcId: !Ref Vpc
Need clarification.
The error is related to the Type mentioned for the Certificate parameter.
Change it to String as below and pass certificate Arn as the value.
Certificate:
Description: Arn of the ssl certificate for HTTPS listener
Type: String
Sample parameters.json file
[
{
"ParameterKey": "EnvironmentName",
"ParameterValue": "dev"
},
{
"ParameterKey": "Name",
"ParameterValue": "stackoverflow"
},
{
"ParameterKey": "Vpc",
"ParameterValue": "vpc-0e104f6ad273a6648"
},
{
"ParameterKey": "PublicSubnet",
"ParameterValue": "subnet-0c2fc6571a7a6db2e, subnet-05a36fdef379c4fcd"
},
{
"ParameterKey": "Certificate",
"ParameterValue": "arn:aws:acm:us-east-1:111111111111:certificate/11ad06f1-b625-44b2-9797-4ecd81451af2"
}
]
I have created CloudFormaton Template with below resources
---
Resources:
InsuranceVPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 11.0.0.0/16
EnableDnsSupport: 'false'
EnableDnsHostnames: 'false'
InstanceTenancy: dedicated
Tags:
- Key: work
Value: insurance
- Key: name
Value: InsuranceVPC
InsuranceInternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: work
Value: insurance
- Key: name
Value: InsuranceInternetGateway
InsuranceSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId:
Ref: InsuranceVPC
CidrBlock: 11.0.2.0/24
AvailabilityZone: "ap-south-1a"
Tags:
- Key: work
Value: insurance
- Key: name
Value: InsuranceSubnet
AttachGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId:
Ref: InsuranceVPC
InternetGatewayId:
Ref: InsuranceInternetGateway
Ec2Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: "ami-0732b62d310b80e97"
InstanceType: "t2.medium"
KeyName: "DevOpsAutomation"
NetworkInterfaces:
- AssociatePublicIpAddress: "true"
DeviceIndex: "0"
GroupSet:
- Ref: "InsuranceSecurityGroup"
SubnetId:
Ref: "InsuranceSubnet"
InsuranceSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow http and ssh to client host
VpcId:
Ref: InsuranceVPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
SecurityGroupEgress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
All resources creations are successful except EC2Instance which fails with below error:
The requested configuration is currently not supported. Please check the documentation for supported configurations. (Service: AmazonEC2; Status Code: 400; Error Code: Unsupported; Request ID: a59a2d39-3aa9-4f7b-9cbd-db05dca0d61e)
The following resource(s) failed to create: [Ec2Instance]. . Rollback requested by use
What I have checked:
The ImageID and InstanceType exist in the same region (or AZ)
All other objects and its dependencies are met
though I understand I haven't yet created route table, route entries but that shouldn't affect EC2 instance resource creation
I am privileged user to create resources.
Please help or guide what I am missing here
I launched your template on my sandbox account.
I've identified some issues.
missing DependsOn on the instance,
VPC has dedicated tenancy,
and incorrect GroupSet.
I modified the template so it fully works now in us-east-1. You have to adjust it to your own region (AMI also needs to be changed back to your original one if not using us-east-1).
---
Resources:
InsuranceVPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 11.0.0.0/16
EnableDnsSupport: 'false'
EnableDnsHostnames: 'false'
InstanceTenancy: default
Tags:
- Key: work
Value: insurance
- Key: name
Value: InsuranceVPC
InsuranceInternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: work
Value: insurance
- Key: name
Value: InsuranceInternetGateway
InsuranceSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId:
Ref: InsuranceVPC
CidrBlock: 11.0.2.0/24
AvailabilityZone: "us-east-1a"
Tags:
- Key: work
Value: insurance
- Key: name
Value: InsuranceSubnet
AttachGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId:
Ref: InsuranceVPC
InternetGatewayId:
Ref: InsuranceInternetGateway
Ec2Instance:
Type: AWS::EC2::Instance
DependsOn: AttachGateway
Properties:
ImageId: "ami-08f3d892de259504d"
InstanceType: "t2.medium"
KeyName: "MyKeyPair"
NetworkInterfaces:
- AssociatePublicIpAddress: "true"
DeviceIndex: "0"
GroupSet:
- !GetAtt InsuranceSecurityGroup.GroupId
SubnetId:
Ref: "InsuranceSubnet"
InsuranceSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Allow http and ssh to client host
VpcId:
Ref: InsuranceVPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
SecurityGroupEgress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
Your VPC is set to dedicated tenancy, which has limits over the resources you can use launch in it (including certain instances types.
Some AWS services or their features won't work with a VPC with the instance tenancy set to dedicated. Check the service's documentation to confirm if there are any limitations.
Some instance types cannot be launched into a VPC with the instance tenancy set to dedicated. For more information about supported instances types, see Amazon EC2 Dedicated Instances.
You should check the above link above, to compare against your instance type.
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"
I have a few applications running as Microservices in aws. Some of them are running on port 80 and some of them are running on port 3000. I want my ALB to listen to traffic on both ports. Then I have a ListenRules to direct the traffic to Microservices. I want to achieve something like below,
Resources:
LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: !Ref EnvironmentName
Subnets: !Ref Subnets
SecurityGroups:
- !Ref SecurityGroup
Tags:
- Key: Name
Value: !Ref EnvironmentName
LoadBalancerListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref LoadBalancer
Port: [80,3000] # something like this
Protocol: HTTP
DefaultActions:
- Type: forward
TargetGroupArn: !Ref DefaultTargetGroup
The Listener should be repeated with each port that is to be opened. For example:
Resources:
LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Properties:
Name: !Ref EnvironmentName
Subnets: !Ref Subnets
SecurityGroups:
- !Ref SecurityGroup
Tags:
- Key: Name
Value: !Ref EnvironmentName
LoadBalancerListenerA:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref LoadBalancer
Port: 80
Protocol: HTTP
DefaultActions:
- Type: forward
TargetGroupArn: !Ref TargetGroupForPort80
LoadBalancerListenerB:
Type: AWS::ElasticLoadBalancingV2::Listener
Properties:
LoadBalancerArn: !Ref LoadBalancer
Port: 3000
Protocol: HTTP
DefaultActions:
- Type: forward
TargetGroupArn: !Ref TargetGroupForPort3000
This also allows the flexibility of setting different protocols (e.g. HTTPS) or target groups for each port.