I am defining a cloudformation stack where the security group should allow ingress traffic from specified IP addresses. I have defined these IP addresses as mapping and they will grow in future when we onboard new customers on our platform. My current cloudformation stack looks like
AWSTemplateFormatVersion: '2010-09-09'
Description: Security group.
Parameters:
VPCStackName:
Type: String
Description: The name of VPC stack
Mappings:
# Security group configuration for different environments
SecurityGroupConfiguration:
PROD:
IPAddress: "149.250.241.202/32 149.250.241.202/32"
NON-PROD:
IPAddress: "149.250.241.202/32, 149.250.241.204/32, 149.250.241.205/32"
Resources:
# Add security groups and their ingress
PublicSubnetSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Test security group
VpcId:
Fn::ImportValue:
!Sub "${VPCStackName}-vpcid"
SecurityGroupIngress:
- CidrIp: !FindInMap ['SecurityGroupConfiguration', 'PROD', 'IPAddress']
IpProtocol: -1
This does not allow the SG to be created no matter I separate them by ' ', ',' or ';'.
2nd method I wanted to try was to define these mappings as a list and iterate them dynamically depending on number of elements configured. For PROD
and NON-PROD
the list will have different number of IP addresses, so I won't be able to define indexes. E.g. Production will have 4 IP addresses and Non-Prod might have only 2 IP addresses. If I define indexes for !Select, the same CFN template will not work for both the environments.
AWSTemplateFormatVersion: '2010-09-09'
Description: Security group.
Parameters:
VPCStackName:
Type: String
Description: The name of VPC stack
Mappings:
# Security group configuration for different environments
SecurityGroupConfiguration:
PROD:
IPAddress:
- 149.250.241.202/32
- 149.250.241.203/32
NON-PROD:
IPAddress:
- 149.250.241.202/32
- 149.250.241.204/32
- 149.250.241.205/32
Resources:
# Add security groups and their ingress
PublicSubnetSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Test security group
VpcId:
Fn::ImportValue:
!Sub "${VPCStackName}-vpcid"
SecurityGroupIngress:
- CidrIp: for (i in SecurityGroupConfiguration)
<Dynamically iterate over list to produce all the ip addresses>
!Select [i, !FindInMap ['SecurityGroupConfiguration', 'PROD', 'IPAddress']]
IpProtocol: -1
Is there a way to get around this problem?
short answer: prefix-lists
A possible solution that completely avoids your problem is to create a prefix-list containing all the IPs (and can be modified at a later time) and instead reference that prefix-list in your SG.
Future updates to your prefix-list takes effect immediately wihtout any modification to your live SG.
Please see Prefix lists for details.
Note: please do keep in mind your VPC limits if you set this up for a large number of IPs (see Amazon VPC quotas)
AWS cloudformation's Custom resources enable you to write custom provisioning logic in templates that AWS CloudFormation runs anytime you create, update (if you changed the custom resource), or delete stacks.
You can use AWS Lambda-backed Custom Resources. When you associate a Lambda function with a custom resource, the function is invoked whenever the custom resource is created, updated, or deleted. AWS CloudFormation calls a Lambda API to invoke the function and to pass all the request data (such as the request type and resource properties) to the function.
The power and customizability of Lambda functions in combination with AWS CloudFormation you can update the security group in custom way.
There are some open source project which can help you write it quickly
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With