'How to get the list of CIDR IPs for AWS Security Group using parameters in Cloudformation template

I am using the below snippet for creating security group with CIDR IPs based on the input parameter. if the input parameter has 1 CIDR IP in it then the security group should be created with only 1 egress attached to it but if the input parameter has 2 CIDR IPs in it the security group should be created with 2 egress attached to it. I am getting cfn-lint error [cfn-lint] E0000:found unexpected ':' at AWS::NoValue.

If I wrap it around quotes(single or double) like "AWS::NoValue", I get the following lint error

[cfn-lint] E2523:Only one of [CidrIp, CidrIpv6, DestinationSecurityGroupId, DestinationPrefixListId] should be specified when condition "CIDRIP1Provided" is False at Resources/MySecurityGroup/Properties/SecurityGroupEgress/0

Is there any other way to achieve my goal? Thanks in advance

Parameters:
  VPCid:
    Default: /app/network/VPCId
    Type: 'AWS::SSM::Parameter::Value<String>'
  CIDRIPs:
    Description: Comma-delimited list of CIDR IPs in the format "CIDRIP1,CIDRIP2". Limit of 2
    Type: CommaDelimitedList
Conditions:
  CIDRIP1Provided: 
    Fn::Not: 
      - Fn::Equals:
        - Fn::Select:
          - 0
          - Fn::Split:
            - ","
            - Fn::Sub:
              - "${IP},,"
              - IP: !Join [',', !Ref CIDRIPs] 
        - ""
  CIDRIP2Provided: 
    Fn::Not: 
      - Fn::Equals:
        - Fn::Select:
          - 1
          - Fn::Split:
            - ","
            - Fn::Sub:
              - "${IP},,"
              - IP: !Join [',', !Ref CIDRIPs] 
        - ""
Resources:
          
  MySecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: My Security Group
      GroupName: my-security-group
      VpcId: !Ref 'VPCid'
      SecurityGroupEgress:
      - IpProtocol: tcp
        ToPort: 443
        FromPort: 443
        CidrIp: !If [CIDRIP1Provided, !Select [ 0, !Ref CIDRIPs ], !Ref AWS::NoValue]
      - IpProtocol: tcp
        ToPort: 443
        FromPort: 443
        CidrIp: !If [CIDRIP2Provided, !Select [ 1, !Ref CIDRIPs ], !Ref AWS::NoValue]


Solution 1:[1]

This will work by using Condition: ConditionItem on Resource items.

Resources:
  MySecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !Ref VPCid
      GroupDescription: Sample source security group
  OutboundRule1:
    Condition: CIDRIP1Provided
    Type: AWS::EC2::SecurityGroupEgress
    Properties:
      IpProtocol: tcp
      FromPort: 443
      ToPort: 443
      CidrIp: !Select [0, !Ref CIDRIPs]
      GroupId:
        Fn::GetAtt:
          - MySecurityGroup
          - GroupId
  OutboundRule2:
    Condition: CIDRIP2Provided
    Type: AWS::EC2::SecurityGroupEgress
    Properties:
      IpProtocol: tcp
      FromPort: 443
      ToPort: 443
      CidrIp: !Select [1, !Ref CIDRIPs]
      GroupId:
        Fn::GetAtt:
          - MySecurityGroup
          - GroupId

Note that if both rule1 and rule2 don't match, sg creates default outbound rule allowing all traffic, which does not seem removed with Cfn. To cancel this default rule, creating the condition evaluate the 1 and 2 fail (isDummyRequired) and set the dummy ip like this.

  DummyRule:
    Condition: isDummyRequired
    Type: AWS::EC2::SecurityGroupEgress
    Properties:
      IpProtocol: tcp
      FromPort: 65000
      ToPort: 65000
      CidrIp: '255.255.255.255/32'
      GroupId:
        Fn::GetAtt:
          - MySecurityGroup
          - GroupId

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1