Edit
Original details below.
In the process of chasing this down, I've now narrowed it down to the fact that this security group
DatabaseSecurityGroup:
Type: AWS::EC2::SecurityGroup
Condition: WantsMail
Properties:
GroupDescription: Security group for RDS Databases
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 3306
ToPort: 3306
SourceSecurityGroupId: !Ref MailSecurityGroup
VpcId:
!Ref Vpc
is not being created, even though WantsMail
is evaluating as true
. I know WantsMail
is evaluating as true
because I changed the Outputs section:
Outputs:
...
DatabaseSecurityGroup:
Condition: WantsMail
Description: Security group for RDS database access
Value: Foo
Export:
Name: FooName
Not only does that get output correctly, but the MailSecurityGroup (which also is conditional on WantsMail
) gets created. However, this security group does not. Nothing showing any YAML errors. No errors that I can find in the AWS CloudFormation UI. Any ideas either on what's wrong, or how to track down whatever error might be occurring?
Aside: Is this sort of edit the right thing to do here? Or should I have killed the original question and asked this more specific one, or ... ?
Original Details
I'm working on breaking my CloudFormation templates into functional stacks. Not yet to the point of doing any nesting, just doing everything self contained. For my security-groups template, I have something very simple working, but when I start to add conditions, I start getting an "Unresolved resource dependencies ... in the Outputs block of the template". I don't think there are any typos, and that along with things having to do with nesting seem to be the most common problems I've seen in other questions similar to this.
The basic, working template looks like this:
---
AWSTemplateFormatVersion: 2010-09-09
Description: 'Create Security Groups'
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: AWS Parameters
Parameters:
- SshAccessCidr
- Vpc
ParameterLabels:
SshAccessCidr:
default: SSH Access From
Vpc:
default: Vpc Id
Parameters:
SshAccessCidr:
AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$
Description: The CIDR IP range that is permitted to SSH to bastion instance. Note - a value of 0.0.0.0/0 will allow access from ANY IP address.
Type: String
Default: 0.0.0.0/0
Vpc:
AllowedPattern: ^(vpc-)([a-z0-9]{8}|[a-z0-9]{17})$
Description: The Vpc Id of an existing Vpc.
Type: AWS::EC2::VPC::Id
Resources:
BastionSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for Bastion instances
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: !Ref SshAccessCidr
VpcId:
!Ref Vpc
DatabaseSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for RDS Databases
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 3306
ToPort: 3306
CidrIp: 10.0.0.0/20
VpcId:
!Ref Vpc
Outputs:
BastionSecurityGroup:
Value: !Ref BastionSecurityGroup
Export:
Name: !Join [ ":", [ !Ref "AWS::StackName", BastionSecurityGroup ] ]
DatabaseSecurityGroup:
Value: !Ref DatabaseSecurityGroup
Export:
Name: !Join [ ":", [ !Ref "AWS::StackName", DatabaseSecurityGroup ] ]
and like I said, works great!
But, when I start to add conditions, it runs amok:
AWS::CloudFormation::Interface:
ParameterGroups:
...
- Label:
default: Optional security groups to create
Parameters:
- CreateMailSecGroup
ParameterLabels:
...
CreateMailSecGroup:
default: Create a security group for mail servers
Parameters:
...
CreateMailSecGroup:
AllowedValues:
- 'True'
- 'False'
Default: 'False'
Description: Create a security group for use by mail servers
Type: String
Conditions:
WantsMail: !Equals ['True', !Ref CreateMailSecGroup]
WantsNothing: !Not [ !Equals ['True', !Ref CreateMailSecGroup] ]
Resources:
DatabaseSecurityGroup:
Type: AWS::EC2::SecurityGroup
Condition: WantsMail
Properties:
GroupDescription: Security group for RDS Databases
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 3306
ToPort: 3306
SourceSecurityGroupId: !Ref MailSecurityGroup
VpcId:
!Ref Vpc
DatabaseSecurityGroup:
Type: AWS::EC2::SecurityGroup
Condition: WantsNothing
Properties:
GroupDescription: Security group for RDS Databases
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 3306
ToPort: 3306
CidrIp: 10.0.0.0/20
VpcId:
!Ref Vpc
MailSecurityGroup:
Type: AWS::EC2::SecurityGroup
Condition: WantsMail
Properties:
GroupDescription: Security group for MX Servers
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
SourceSecurityGroupId: !Ref BastionSecurityGroup
- IpProtocol: tcp
FromPort: 25
ToPort: 25
CidrIp: 0.0.0.0/0
...
VpcId:
!Ref Vpc
Outputs:
...
DatabaseSecurityGroup:
Description: Security group for RDS database access
Value: !Ref DatabaseSecurityGroup
Export:
Name: !Join [ ":", [ !Ref "AWS::StackName", DatabaseSecurityGroup ] ]
MailSecurityGroup:
Condition: WantsMail
Description: Security group for MX servers
Value: !Ref MailSecurityGroup
Export:
Name: !Join [ ":", [ !Ref "AWS::StackName", MailSecurityGroup ] ]
Now, if I make those changes, and do a update stack on the current (functioning) stack, and leave the new parameter CreateMailSecGroup
at its default value of False
, it does the right thing and says "nothing to change" after parsing the template. But if I change it to True
, it complains with
Unresolved resource dependencies [DatabaseSecurityGroup] in the Outputs block of the template
in the change set preview.
My initial suspicion was that, now that the DatabaseSecurityGroup is conditional, it must want a matching condition in the Outputs block, so I tried
Conditions:
WantsMail: !Equals ['True', !Ref CreateMailSecGroup]
WantsNothing: !Not [ !Equals ['True', !Ref CreateMailSecGroup] ]
OutputDatabase: !Or [ Condition: WantsMail, Condition: WantsNothing ]
...
Outputs:
DatabaseSecurityGroup:
Condition: OutputDatabase
Description: Security group for RDS database access
Value: !Ref DatabaseSecurityGroup
Export:
Name: !Join [ ":", [ !Ref "AWS::StackName", DatabaseSecurityGroup ] ]
No dice. Same error.
My last guess was that maybe the DatabaseSecurityGroup wasn't actually getting processed with the True
condition because of some weird ordering phenomenon, so I tried adding DependsOn: MailSecurityGroup
, but that didn't fix it either.
I'm really hoping this isn't a silly typo. Anyone have any ideas what I'm doing wrong? The condition stuff for the MailSecurityGroup doesn't seem to be throwing any errors, but I'm not sure if that's because there isn't an error, or because it got stopped at the outputs section of the DatabaseSecurityGroup and never got to the final one.
Thanks!
Hrmph. According to the documentation:
Both simple, and annoying. So, can't have two different resources named "DatabaseSecurityGroup" and switch between them based on conditions. Need to give them different names (and then, in the outputs, create multiple output sections for the different names, which are also selected based on the conditions).