SecurityGroupルールのCidrIpの省略と !Ref "AWS::NoValue" と "0.0.0.0/0"

AWS CloudFormationテンプレートで、SecurityGroupルールの `CidrIp` 指定についてちょっと誤解してたので、メモ。

# 対象範囲指定がないとルールが作成されない

「[AWS::EC2::SecurityGroup Ingress - AWS CloudFormation](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-security-group-rule-1.html)」を見ると `CidrIp` は省略可能（Required: No）となっています。ここで、省略すればAWSコンソールでいうところの「任意のIP」（`0.0.0.0/0`と`::0`）になるのかな、と思ったのが私の勘違い。

```
CidrIp
  The IPv4 address range, in CIDR format.
  Required: No
  Type: String
  Update requires: No interruption
```

実際には「 `CidrIp` を含むいくつかのパラメータのうち1つを必ず指定しなければいけない」とIngressルールのドキュメント先頭近くに書かれています。

>You must specify only one of the following properties: `CidrIp`, `CidrIpv6`, `SourcePrefixListId`, `SourceSecurityGroupId`, or `SourceSecurityGroupName`.
>([AWS::EC2::SecurityGroup Ingress - AWS CloudFormation](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-security-group-rule-1.html))

Egressルールのドキュメントにも類似のどれか一つ指定必須の注意書きがあり、こちらにははっきりと「これらのパラメータのうち一つを指定しなかった場合、スタックの作成には成功しますが、ルールはセキュリティグループに追加されません」と付記されていました。

>You must specify a destination security group (`DestinationPrefixListId` or `DestinationSecurityGroupId`) or a CIDR range (`CidrIp` or `CidrIpv6`). If you do not specify one of these parameters, the stack will launch successfully but the rule will not be added to the security group.
>（[AWS::EC2::SecurityGroupEgress - AWS CloudFormation](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-security-group-egress.html)）

つまりほかのパラメータも指定していない状態で「省略すれば『任意のIP』になる」という期待は的外れで、このルールが作成されません。

# 省略と !Ref "AWS::NoValue" と "0.0.0.0/0"

実際に次のようなCloudFormationテンプレートで、スタックを作成してみました。 `IsRemoteAccessCIDR` は今回 `false` になるようにスタック作成時のパラメータを指定しています。このため各プロトコルに対するルールで、 `CidrIp` は以下が指定されたことになります。

|プロトコル|FromPort|CidrIP|
|---|---|---|
|SSH ingress|22|"0.0.0.0/0"|
|RDP ingress|3389|!Ref "AWS::NoValue"|
|NICE DCV ingress|8443|（指定省略）|

```yaml
  EC2SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: EC2SecurityGroup
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - 
          IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: !If [IsRemoteAccessCIDR, !Ref RemoteAccessCIDR, "0.0.0.0/0"]
          Description: "SSH ingress"
        - 
          IpProtocol: tcp
          FromPort: 3389
          ToPort: 3389
          CidrIp: !If [IsRemoteAccessCIDR, !Ref RemoteAccessCIDR, !Ref "AWS::NoValue"]
          Description: "RDP ingress"
        - 
          IpProtocol: tcp
          FromPort: 8443
          ToPort: 8443
          Description: "NICE DCV ingress"
```

作成されたセキュリティグループのインバウンドルールは `CidrIp` に `0.0.0.0/0` を指定したSSHのルールだけが作成されており、 `!Ref "AWS::NoValue"` を指定したRDPと指定を省略したNICE DCV用のルールは作成されていませんでした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55718/ccfcdd82-9b90-6d32-0807-ca8fbd7a043f.png)

指定時と省略時の動作はドキュメントの記載通り、 `!Ref "AWS::NoValue"` 指定時も、これは「[指定すると、対応するリソースプロパティを削除](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/pseudo-parameter-reference.html#cfn-pseudo-param-novalue)」するので省略時と同等になり、ドキュメントの記載通りの動作でした。

# 参考

* [AWS::EC2::SecurityGroup Ingress - AWS CloudFormation](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-security-group-rule-1.html)
* [AWS::EC2::SecurityGroupEgress - AWS CloudFormation](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-security-group-egress.html)
* [AWS::NoValue - 擬似パラメータ参照 - AWS CloudFormation](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/pseudo-parameter-reference.html#cfn-pseudo-param-novalue)