I have a CloudFormation template that has two conditions defined. If we're launching the stack in us-west-2 (where the Network ELB IS supported), then it sets CreateNetworkLoadBalancer
to True. If we're running this stack in sa-east-1 (Sao Paulo, where only the Classic ELB is supported), then CreateNetworkLoadBalancer
is set to False
, and CreateClassicLoadBalancer
is True
. (Example below)
Conditions:
CreateClassicLoadBalancer: !Equals [ !Ref "AWS::Region", sa-east-1 ]
CreateNetworkLoadBalancer: !Equals [ !Ref "AWS::Region", us-west-2 ]
The stack later defines two resources, a Network and a Classic ELB, as shown below. It spins up only the the appropriate LB, based on the region and the conditions. This all works as expected. (Example below)
Resources:
######################################################
# Network Load Balancer, Target Group, etc.
# NLB
NLB:
Type: "AWS::ElasticLoadBalancingV2::LoadBalancer"
Condition: CreateNetworkLoadBalancer
Properties:
LoadBalancerAttributes:
- Key: deletion_protection.enabled
Value: False
Name: !Join [ "-", [ !Ref awsTagsNamePrefix, "nlb" ] ]
Scheme: internet-facing
Subnets: !Ref bastionSubnetList
Tags:
- Key: Environment
Value: !Ref awsTagsEnvironment
- Key: Application
Value: !Ref awsTagsApplication
- Key: Name
Value: !Join [ "-", [ !Ref awsTagsNamePrefix, "nlb" ] ]
Type: network
IpAddressType: ipv4
# target group
NLBTargetGroup:
Type: "AWS::ElasticLoadBalancingV2::TargetGroup"
Condition: CreateNetworkLoadBalancer
Properties:
HealthCheckIntervalSeconds: 30
HealthCheckPort: 22
HealthCheckProtocol: TCP
HealthCheckTimeoutSeconds: 10
HealthyThresholdCount: 3
Name: !Join [ "-", [ !Ref awsTagsNamePrefix, "elb-target-group" ] ]
Port: 22
Protocol: TCP
Tags:
- Key: Environment
Value: !Ref awsTagsEnvironment
- Key: Application
Value: !Ref awsTagsApplication
- Key: Name
Value: !Join [ "-", [ !Ref awsTagsNamePrefix, "elb-target-group" ] ]
TargetGroupAttributes:
- Key: deregistration_delay.timeout_seconds
Value: 0
UnhealthyThresholdCount: 3
VpcId: !Select [0, !Ref vpcIdList]
# Listener
NLBListener:
Type: AWS::ElasticLoadBalancingV2::Listener
Condition: CreateNetworkLoadBalancer
Properties:
DefaultActions:
- TargetGroupArn: !Ref NLBTargetGroup
Type: forward
LoadBalancerArn: !Ref NLB
Port: 22
Protocol: TCP
######################################################
######################################################
# Classic ELB - for regions that do not yet support a Network LB.
# ELB
ELB:
Type: "AWS::ElasticLoadBalancing::LoadBalancer"
Condition: CreateClassicLoadBalancer
Properties:
Subnets: !Ref bastionSubnetList
HealthCheck:
HealthyThreshold: '3'
Interval: '10'
Target: TCP:22
Timeout: '5'
UnhealthyThreshold: '3'
ConnectionSettings:
IdleTimeout: '60'
CrossZone: 'true'
SecurityGroups:
- Ref: BastionELBSG
Listeners:
- InstancePort: '22'
LoadBalancerPort: '22'
Protocol: TCP
InstanceProtocol: TCP
LoadBalancerName: !Join [ "-", [ !Ref awsTagsNamePrefix, "bastion-elb" ] ]
Tags:
- Key: Environment
Value: !Ref awsTagsEnvironment
- Key: Application
Value: !Ref awsTagsApplication
- Key: Name
Value: !Join [ "-", [ !Ref awsTagsNamePrefix, "bastion-elb" ] ]
Via a Stack Update, we now want to add code to associate the existing Load Balancer (the one that was provisioned based on the region), to an AutoScaling group. To do this, AutoScaling has separate Properties for each (TargetGroupARNs
for Network LoadBalancer Target Groups, and LoadBalancerNames
for Classic ELBs). We're defining both in AWS::AutoScaling::AutoScalingGroup
, and relying on the True/False value of the condition, an intrinsic function (Fn::If
), and the AWS::NoValue
pseudo parameter. In theory the below code should work.
TargetGroupARNs:
!If
- CreateNetworkLoadBalancer
- !Ref NLBTargetGroup
- !Ref "AWS::NoValue"
LoadBalancerNames:
!If
- CreateClassicLoadBalancer
- !Ref ELB
- !Ref "AWS::NoValue"
However the syntax/format of the Yaml just isn't correct - We are getting the following error when running the stack (Value of property TargetGroupARNs must be of type List of String).
What is the proper way to tie the If, the conditions, and NoValue together so that a List of a single String (resulting in either a !Ref to NoValue or NLB) is assigned to the value of TargetGroupARNs
?
BastionASG:
Type: AWS::AutoScaling::AutoScalingGroup
Properties:
AvailabilityZones:
Fn::GetAZs: !Ref "AWS::Region"
Cooldown: '0'
DesiredCapacity: '0'
HealthCheckGracePeriod: '60'
HealthCheckType: EC2
MaxSize: '0'
MinSize: '0'
VPCZoneIdentifier: !Ref bastionSubnetList
LaunchConfigurationName:
Ref: BastionLC
Tags:
- Key: Environment
Value: !Ref awsTagsEnvironment
PropagateAtLaunch: true
- Key: Application
Value: !Ref awsTagsApplication
PropagateAtLaunch: true
- Key: Name
Value: !Join [ "-", [ !Ref awsTagsNamePrefix, "asg-ec2" ] ]
PropagateAtLaunch: true
TargetGroupARNs:
!If
- CreateNetworkLoadBalancer
- !Ref NLBTargetGroup
- !Ref "AWS::NoValue"
LoadBalancerNames:
!If
- CreateClassicLoadBalancer
- !Ref ELB
- !Ref "AWS::NoValue"
TerminationPolicies:
- OldestInstance