ROSA検証用のVPCを作る（CFnで）

## 「2回以上やることは、なんでも自動化されるべきだ」

* 本当にそう思います。

## ROSAの検証の度、AWS CLIで作っているもの

* [Deployment models for AWS Network Firewall](https://aws.amazon.com/jp/blogs/networking-and-content-delivery/deployment-models-for-aws-network-firewall/) より。これのALBが無いやつ。
* 作っているROSAはSTS有効化、Multi-AZ、自前のVPC...

![](https://d2908q01vomqb2.cloudfront.net/5b384ce32d8cdef02bc3a139d4cac0a22bb029e8/2020/11/18/anfw-public-igw-deployment-high-res1-1-1.png)

## 全部CFnにしてやるー(^q^)

```yaml
AWSTemplateFormatVersion: "2010-09-09"
Description: 
  VPC, Subnet

Metadata: 
  "AWS::CloudFormation::Interface": 
    ParameterGroups: 
      - Label: 
          default: "Project Name Prefix"
        Parameters: 
          - ClusterPrefix
      - Label: 
          default: "Network Configuration"
        Parameters: 
          - VPCCIDR
          - FirewallSubnetACIDR
          - FirewallSubnetCCIDR
          - FirewallSubnetDCIDR
          - ProtectedSubnetACIDR
          - ProtectedSubnetCCIDR
          - ProtectedSubnetDCIDR
          - PrivateSubnetACIDR
          - PrivateSubnetCCIDR
          - PrivateSubnetDCIDR
    ParameterLabels: 
      VPCCIDR: 
        default: "VPC CIDR"
      FirewallSubnetACIDR: 
        default: "FirewallSubnetA CIDR"
      FirewallSubnetCCIDR: 
        default: "FirewallSubnetC CIDR"
      FirewallSubnetCCIDR: 
        default: "FirewallSubnetD CIDR"
      ProtectedSubnetACIDR: 
        default: "ProtectedSubnetA CIDR"
      ProtectedSubnetCCIDR: 
        default: "ProtectedSubnetC CIDR"
      ProtectedSubnetCCIDR: 
        default: "ProtectedSubnetD CIDR"
      PrivateSubnetACIDR: 
        default: "PrivateSubnetA CIDR"
      PrivateSubnetCCIDR: 
        default: "PrivateSubnetC CIDR"
      PrivateSubnetCCIDR: 
        default: "PrivateSubnetD CIDR"

# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------# 
Parameters:
  ClusterPrefix:
    Type: String
    Default: "ROSA"

  VPCCIDR:
    Type: String
    Default: "10.0.0.0/16"

  FirewallSubnetACIDR:
    Type: String
    Default: "10.0.10.0/24"
  FirewallSubnetCCIDR:
    Type: String
    Default: "10.0.11.0/24"
  FirewallSubnetDCIDR:
    Type: String
    Default: "10.0.12.0/24"
  ProtectedSubnetACIDR:
    Type: String
    Default: "10.0.20.0/24"
  ProtectedSubnetCCIDR:
    Type: String
    Default: "10.0.21.0/24"
  ProtectedSubnetDCIDR:
    Type: String
    Default: "10.0.22.0/24"

  PrivateSubnetACIDR:
    Type: String
    Default: "10.0.30.0/24"
  PrivateSubnetCCIDR:
    Type: String
    Default: "10.0.31.0/24"
  PrivateSubnetDCIDR:
    Type: String
    Default: "10.0.32.0/24"

Resources: 
# ------------------------------------------------------------#
#  VPC
# ------------------------------------------------------------#
# VPC
  VPC: 
    Type: "AWS::EC2::VPC"
    Properties: 
      CidrBlock: !Ref VPCCIDR
      EnableDnsSupport: "true"
      EnableDnsHostnames: "true"
      InstanceTenancy: default
      Tags: 
        - Key: Name
          Value: !Sub "${ClusterPrefix}-vpc"

# InternetGateway
  InternetGateway: 
    Type: "AWS::EC2::InternetGateway"
    Properties: 
      Tags: 
        - Key: Name
          Value: !Sub "${ClusterPrefix}-igw"

# IGW Attach
  InternetGatewayAttachment: 
    Type: "AWS::EC2::VPCGatewayAttachment"
    Properties: 
      InternetGatewayId: !Ref InternetGateway
      VpcId: !Ref VPC

# DHCPOptions
  DHCPOptions:
    Type: AWS::EC2::DHCPOptions
    Properties:
      DomainName: ap-northeast-1.compute.internal
      DomainNameServers:
        - AmazonProvidedDNS
      Tags:
        - Key: Name
          Value: !Sub "${ClusterPrefix}-dhcp-options"
  DHCPOptionsAssociation:
    Type: AWS::EC2::VPCDHCPOptionsAssociation
    Properties:
      VpcId: !Ref VPC
      DhcpOptionsId: !Ref DHCPOptions

# ------------------------------------------------------------#
#  Subnet
# ------------------------------------------------------------#          
# FirewallSubnetA
  FirewallSubnetA: 
    Type: "AWS::EC2::Subnet"
    Properties: 
      AvailabilityZone: "ap-northeast-1a"
      CidrBlock: !Ref FirewallSubnetACIDR
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${ClusterPrefix}-firewall-subnet-a"
# FirewallSubnetC
  FirewallSubnetC: 
    Type: "AWS::EC2::Subnet"
    Properties: 
      AvailabilityZone: "ap-northeast-1c"
      CidrBlock: !Ref FirewallSubnetCCIDR
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${ClusterPrefix}-firewall-subnet-c"
# FirewallSubnetD
  FirewallSubnetD: 
    Type: "AWS::EC2::Subnet"
    Properties: 
      AvailabilityZone: "ap-northeast-1d"
      CidrBlock: !Ref FirewallSubnetDCIDR
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${ClusterPrefix}-firewall-subnet-d"
# ProtectedSubnetA
  ProtectedSubnetA: 
    Type: "AWS::EC2::Subnet"
    Properties: 
      AvailabilityZone: "ap-northeast-1a"
      CidrBlock: !Ref ProtectedSubnetACIDR
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${ClusterPrefix}-protected-subnet-a"
# ProtectedSubnetC
  ProtectedSubnetC: 
    Type: "AWS::EC2::Subnet"
    Properties: 
      AvailabilityZone: "ap-northeast-1c"
      CidrBlock: !Ref ProtectedSubnetCCIDR
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${ClusterPrefix}-protected-subnet-c"
# ProtectedSubnetD
  ProtectedSubnetD: 
    Type: "AWS::EC2::Subnet"
    Properties: 
      AvailabilityZone: "ap-northeast-1d"
      CidrBlock: !Ref ProtectedSubnetDCIDR
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${ClusterPrefix}-protected-subnet-d"
# PrivateSubnetA
  PrivateSubnetA: 
    Type: "AWS::EC2::Subnet"
    Properties: 
      AvailabilityZone: "ap-northeast-1a"
      CidrBlock: !Ref PrivateSubnetACIDR
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${ClusterPrefix}-private-subnet-a"
# PrivateSubnetC
  PrivateSubnetC: 
    Type: "AWS::EC2::Subnet"
    Properties: 
      AvailabilityZone: "ap-northeast-1c"
      CidrBlock: !Ref PrivateSubnetCCIDR
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${ClusterPrefix}-private-subnet-c"
# PrivateSubnetD
  PrivateSubnetD: 
    Type: "AWS::EC2::Subnet"
    Properties: 
      AvailabilityZone: "ap-northeast-1d"
      CidrBlock: !Ref PrivateSubnetDCIDR
      VpcId: !Ref VPC
      Tags: 
        - Key: Name
          Value: !Sub "${ClusterPrefix}-private-subnet-d"

# ------------------------------------------------------------#
#  RouteTable
# ------------------------------------------------------------#          
# IGWRouteTable
  IGWRouteTable: 
    Type: "AWS::EC2::RouteTable"
    Properties: 
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${ClusterPrefix}-igw-route"
# FirewallRouteTableA
  FirewallRouteTableA: 
    Type: "AWS::EC2::RouteTable"
    Properties: 
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${ClusterPrefix}-firewall-route-a"
# FirewallRouteTableC
  FirewallRouteTableC: 
    Type: "AWS::EC2::RouteTable"
    Properties: 
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${ClusterPrefix}-firewall-route-c"
# FirewallRouteTableD
  FirewallRouteTableD: 
    Type: "AWS::EC2::RouteTable"
    Properties: 
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${ClusterPrefix}-firewall-route-d"
# ProtectedRouteTableA
  ProtectedRouteTableA: 
    Type: "AWS::EC2::RouteTable"
    Properties: 
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${ClusterPrefix}-protected-route-a"
# ProtectedRouteTableC
  ProtectedRouteTableC: 
    Type: "AWS::EC2::RouteTable"
    Properties: 
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${ClusterPrefix}-protected-route-c"
# ProtectedRouteTableD
  ProtectedRouteTableD: 
    Type: "AWS::EC2::RouteTable"
    Properties: 
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${ClusterPrefix}-protected-route-d"
# PrivateRouteTableA
  PrivateRouteTableA: 
    Type: "AWS::EC2::RouteTable"
    Properties: 
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${ClusterPrefix}-private-route-a"
# PrivateRouteTableC
  PrivateRouteTableC: 
    Type: "AWS::EC2::RouteTable"
    Properties: 
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${ClusterPrefix}-private-route-c"
# PrivateRouteTableD
  PrivateRouteTableD: 
    Type: "AWS::EC2::RouteTable"
    Properties: 
      VpcId: !Ref VPC 
      Tags: 
        - Key: Name
          Value: !Sub "${ClusterPrefix}-private-route-d"

# ------------------------------------------------------------#
# NAT Gateway
# ------------------------------------------------------------#
# EIP for NATGatewayA
  NATGatewayAEIP: 
    Type: "AWS::EC2::EIP"
    Properties: 
      Domain: vpc
# NATGatewayA
  NATGatewayA: 
    Type: "AWS::EC2::NatGateway"
    DependsOn:
      - NATGatewayAEIP
      - ProtectedSubnetA
    Properties: 
      AllocationId: !GetAtt NATGatewayAEIP.AllocationId 
      SubnetId: !Ref ProtectedSubnetA
      Tags: 
        - Key: Name
          Value: !Sub "${ClusterPrefix}-natgw-a"
# EIP for NATGatewayC
  NATGatewayCEIP: 
    Type: "AWS::EC2::EIP"
    Properties: 
      Domain: vpc
# NATGatewayC
  NATGatewayC: 
    Type: "AWS::EC2::NatGateway"
    DependsOn:
      - NATGatewayCEIP
      - ProtectedSubnetC
    Properties: 
      AllocationId: !GetAtt NATGatewayCEIP.AllocationId 
      SubnetId: !Ref ProtectedSubnetC
      Tags: 
        - Key: Name
          Value: !Sub "${ClusterPrefix}-natgw-c"
# EIP for NATGatewayD
  NATGatewayDEIP: 
    Type: "AWS::EC2::EIP"
    Properties: 
      Domain: vpc
# NATGatewayD
  NATGatewayD: 
    Type: "AWS::EC2::NatGateway"
    DependsOn:
      - NATGatewayDEIP
      - ProtectedSubnetD
    Properties: 
      AllocationId: !GetAtt NATGatewayDEIP.AllocationId 
      SubnetId: !Ref ProtectedSubnetD
      Tags: 
        - Key: Name
          Value: !Sub "${ClusterPrefix}-natgw-d"
# ------------------------------------------------------------#
# Network Firewall
# ------------------------------------------------------------#
# AWS Network Firewall:
  NetworkFirewall:
    Type: AWS::NetworkFirewall::Firewall
    Properties:
      FirewallName: !Sub "${ClusterPrefix}-firewall"
      FirewallPolicyArn: !Ref EgressFirewallPolicy
      VpcId: !Ref VPC 
      SubnetMappings:
        - SubnetId: !Ref FirewallSubnetA
        - SubnetId: !Ref FirewallSubnetC
        - SubnetId: !Ref FirewallSubnetD
      Tags:
        - Key: Name
          Value: !Sub "${ClusterPrefix}-network-firewall"

  ICMPAlertStatefulRuleGroup:
    Type: 'AWS::NetworkFirewall::RuleGroup'
    Properties:
      RuleGroupName: !Sub "${ClusterPrefix}-icmp-alert"
      Type: STATEFUL
      Capacity: 100
      RuleGroup:
        RulesSource:
          StatefulRules:
            - Action: ALERT
              Header:
                Direction: ANY
                Protocol: ICMP
                Destination: ANY
                Source: ANY
                DestinationPort: ANY
                SourcePort: ANY
              RuleOptions:
                - Keyword: "sid:1"
      Tags:
        - Key: Name
          Value: !Sub "${ClusterPrefix}-icmp-alert" 
  DomainAllowStatefulRuleGroup:
    Type: 'AWS::NetworkFirewall::RuleGroup'
    Properties:
      RuleGroupName: !Sub "${ClusterPrefix}-domain-allow"
      Type: STATEFUL
      Capacity: 100
      RuleGroup:
        RuleVariables:
          IPSets:
            HOME_NET:
              Definition:
                - !Ref VPCCIDR
        RulesSource:
          RulesSourceList:
            TargetTypes:
              - HTTP_HOST
              - TLS_SNI
                # TODO : I'll fix them later...
            Targets: 
              - ".nosnch.in"
              - ".osdsecuritylogs.splunkcloud.com"
              - ".quay.io"
              - ".quay.rhcloud.com"
              - ".rhel.pool.ntp.org"
              - ".amazonaws.com"
              - "cluster-id-shard.ap-northeast-1.amazonaws.com"
              - "api.access.redhat.com"
              - "api.deadmanssnitch.com"
              - "api.openshift.com"
              - "api.pagerduty.com"
              - "art-rhcos-ci.s3.amazonaws.com"
              - "cert-api.access.redhat.com"
              - "cloud.redhat.com"
              - "cm-quay-production-s3.s3.amazonaws.com"
              - "console.redhat.com"
              - "ec2.amazonaws.com"
              - "ec2.ap-northeast-1.amazonaws.com"
              - "elasticloadbalancing.ap-northeast-1.amazonaws.com"
              - "events.amazonaws.com"
              - "events.pagerduty.com"
              - "http-inputs-osdsecuritylogs.splunkcloud.com"
              - "iam.amazonaws.com"
              - "infogw.api.openshift.com"
              - "mirror.openshift.com"
              - "observatorium.api.openshift.com"
              - "openshift.org"
              - "quay-registry.s3.amazonaws.com"
              - "quayio-production-s3.s3.amazonaws.com"
              - "registry.access.redhat.com"
              - "registry.redhat.io"
              - "route53.amazonaws.com"
              - "sftp.access.redhat.com"
              - "sso.redhat.com"
              - "storage.googleapis.com"
              - "sts.amazonaws.com"
            GeneratedRulesType: "ALLOWLIST"
      Tags:
        - Key: Name
          Value: !Sub "${ClusterPrefix}-domain-allow"   
  EgressFirewallPolicy:
    Type: AWS::NetworkFirewall::FirewallPolicy
    Properties:
      FirewallPolicyName: !Sub "${ClusterPrefix}-firewall-policy"
      FirewallPolicy:
        StatelessDefaultActions:
          - 'aws:forward_to_sfe'
        StatelessFragmentDefaultActions:
          - 'aws:forward_to_sfe'
        StatefulRuleGroupReferences:
          - ResourceArn: !Ref DomainAllowStatefulRuleGroup
          - ResourceArn: !Ref ICMPAlertStatefulRuleGroup
      Tags:
        - Key: Name
          Value: !Sub "${ClusterPrefix}-firewall-policy"

  FirewallLogFlowGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub "/${ClusterPrefix}/anfw/flow"
  FirewallLogAlertGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub "/${ClusterPrefix}/anfw/alert"
  FirewallLog:
    Type: AWS::NetworkFirewall::LoggingConfiguration
    Properties:
      FirewallArn: !Ref NetworkFirewall
      LoggingConfiguration:
        LogDestinationConfigs:
          - LogType: FLOW
            LogDestinationType: CloudWatchLogs
            LogDestination:
              logGroup: !Sub "/${ClusterPrefix}/anfw/flow"
          - LogType: ALERT
            LogDestinationType: CloudWatchLogs
            LogDestination:
              logGroup: !Sub "/${ClusterPrefix}/anfw/alert"

# ------------------------------------------------------------#
# Route
# ------------------------------------------------------------# 
# IGWRouteA
  IGWRouteA: 
    Type: "AWS::EC2::Route"
    Properties: 
      RouteTableId: !Ref IGWRouteTable
      DestinationCidrBlock: !Ref ProtectedSubnetACIDR
      VpcEndpointId: !Select [ 1, Fn::Split: [ ":", !Select [ 0, !GetAtt NetworkFirewall.EndpointIds ] ] ]
# IGWRouteC
  IGWRouteC: 
    Type: "AWS::EC2::Route"
    Properties: 
      RouteTableId: !Ref IGWRouteTable
      DestinationCidrBlock: !Ref ProtectedSubnetCCIDR
      VpcEndpointId: !Select [ 1, Fn::Split: [ ":", !Select [ 1, !GetAtt NetworkFirewall.EndpointIds ] ] ]
# IGWRouteD
  IGWRouteD: 
    Type: "AWS::EC2::Route"
    Properties: 
      RouteTableId: !Ref IGWRouteTable
      DestinationCidrBlock: !Ref ProtectedSubnetDCIDR
      VpcEndpointId: !Select [ 1, Fn::Split: [ ":", !Select [ 2, !GetAtt NetworkFirewall.EndpointIds ] ] ]
# FirewallRouteA
  FirewallRouteA: 
    Type: "AWS::EC2::Route"
    Properties: 
      RouteTableId: !Ref FirewallRouteTableA
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId: !Ref InternetGateway 
# FirewallRouteC
  FirewallRouteC: 
    Type: "AWS::EC2::Route"
    Properties: 
      RouteTableId: !Ref FirewallRouteTableC
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId: !Ref InternetGateway 
# FirewallRouteD
  FirewallRouteD: 
    Type: "AWS::EC2::Route"
    Properties: 
      RouteTableId: !Ref FirewallRouteTableD
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId: !Ref InternetGateway 
# ProtectedRouteA
  ProtectedRouteA: 
    Type: "AWS::EC2::Route"
    Properties: 
      RouteTableId: !Ref ProtectedRouteTableA
      DestinationCidrBlock: "0.0.0.0/0"
      VpcEndpointId: !Select [ 1, Fn::Split: [ ":", !Select [ 0, !GetAtt NetworkFirewall.EndpointIds ] ] ]
# ProtectedRouteC
  ProtectedRouteC: 
    Type: "AWS::EC2::Route"
    Properties: 
      RouteTableId: !Ref ProtectedRouteTableC
      DestinationCidrBlock: "0.0.0.0/0"
      VpcEndpointId: !Select [ 1, Fn::Split: [ ":", !Select [ 1, !GetAtt NetworkFirewall.EndpointIds ] ] ]
# ProtectedRouteD
  ProtectedRouteD: 
    Type: "AWS::EC2::Route"
    Properties: 
      RouteTableId: !Ref ProtectedRouteTableD
      DestinationCidrBlock: "0.0.0.0/0"
      VpcEndpointId: !Select [ 1, Fn::Split: [ ":", !Select [ 2, !GetAtt NetworkFirewall.EndpointIds ] ] ]

# PrivateRouteA
  PrivateRouteA: 
    Type: "AWS::EC2::Route"
    Properties: 
      RouteTableId: !Ref PrivateRouteTableA
      DestinationCidrBlock: "0.0.0.0/0"
      NatGatewayId: !Ref NATGatewayA
# PrivateRouteC
  PrivateRouteC: 
    Type: "AWS::EC2::Route"
    Properties: 
      RouteTableId: !Ref PrivateRouteTableC
      DestinationCidrBlock: "0.0.0.0/0"
      NatGatewayId: !Ref NATGatewayC
# PrivateRouteD
  PrivateRouteD: 
    Type: "AWS::EC2::Route"
    Properties: 
      RouteTableId: !Ref PrivateRouteTableD
      DestinationCidrBlock: "0.0.0.0/0"
      NatGatewayId: !Ref NATGatewayD

# ------------------------------------------------------------#
# RouteTable Associate
# ------------------------------------------------------------# 
# IGWRouteTableAssociation
  IGWRouteTableAssociation: 
    Type: "AWS::EC2::GatewayRouteTableAssociation"
    Properties: 
      GatewayId: !Ref InternetGateway
      RouteTableId: !Ref IGWRouteTable
# FirewallSubnetARouteTableAssociation
  FirewallSubnetARouteTableAssociation: 
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties: 
      SubnetId: !Ref FirewallSubnetA
      RouteTableId: !Ref FirewallRouteTableA
# FirewallSubnetCRouteTableAssociation
  FirewallSubnetCRouteTableAssociation: 
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties: 
      SubnetId: !Ref FirewallSubnetC
      RouteTableId: !Ref FirewallRouteTableC
# FirewallSubnetDRouteTableAssociation
  FirewallSubnetDRouteTableAssociation: 
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties: 
      SubnetId: !Ref FirewallSubnetD
      RouteTableId: !Ref FirewallRouteTableD
# ProtectedSubnetARouteTableAssociation
  ProtectedSubnetARouteTableAssociation: 
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties: 
      SubnetId: !Ref ProtectedSubnetA
      RouteTableId: !Ref ProtectedRouteTableA
# ProtectedSubnetCRouteTableAssociation
  ProtectedSubnetCRouteTableAssociation: 
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties: 
      SubnetId: !Ref ProtectedSubnetC
      RouteTableId: !Ref ProtectedRouteTableC
# ProtectedSubnetDRouteTableAssociation
  ProtectedSubnetDRouteTableAssociation: 
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties: 
      SubnetId: !Ref ProtectedSubnetD
      RouteTableId: !Ref ProtectedRouteTableD
# PrivateSubnetARouteTableAssociation
  PrivateSubnetARouteTableAssociation: 
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties: 
      SubnetId: !Ref PrivateSubnetA
      RouteTableId: !Ref PrivateRouteTableA
# PrivateSubnetCRouteTableAssociation
  PrivateSubnetCRouteTableAssociation: 
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties: 
      SubnetId: !Ref PrivateSubnetC
      RouteTableId: !Ref PrivateRouteTableC
# PrivateSubnetDRouteTableAssociation
  PrivateSubnetDRouteTableAssociation: 
    Type: "AWS::EC2::SubnetRouteTableAssociation"
    Properties: 
      SubnetId: !Ref PrivateSubnetD
      RouteTableId: !Ref PrivateRouteTableD

# ------------------------------------------------------------#
# Output Parameters
# ------------------------------------------------------------#                
Outputs:
# VPC
  VPC:
    Value: !Ref VPC
    Export:
      Name: !Sub "${ClusterPrefix}-vpc"

  VPCCIDR:
    Value: !Ref VPCCIDR
    Export:
      Name: !Sub "${ClusterPrefix}-vpc-cidr"

# Subnet
  FirewallSubnetA:
    Value: !Ref FirewallSubnetA
    Export:
      Name: !Sub "${ClusterPrefix}-firewall-subnet-a"
  FirewallSubnetACIDR:
    Value: !Ref FirewallSubnetACIDR
    Export:
      Name: !Sub "${ClusterPrefix}-firewall-subnet-a-cidr"
  FirewallSubnetC:
    Value: !Ref FirewallSubnetC
    Export:
      Name: !Sub "${ClusterPrefix}-firewall-subnet-c"
  FirewallSubnetCCIDR:
    Value: !Ref FirewallSubnetCCIDR
    Export:
      Name: !Sub "${ClusterPrefix}-firewall-subnet-c-cidr"
  FirewallSubnetD:
    Value: !Ref FirewallSubnetD
    Export:
      Name: !Sub "${ClusterPrefix}-firewall-subnet-d"
  FirewallSubnetDCIDR:
    Value: !Ref FirewallSubnetDCIDR
    Export:
      Name: !Sub "${ClusterPrefix}-firewall-subnet-d-cidr"
  ProtectedSubnetA:
    Value: !Ref ProtectedSubnetA
    Export:
      Name: !Sub "${ClusterPrefix}-protected-subnet-a"
  ProtectedSubnetACIDR:
    Value: !Ref ProtectedSubnetACIDR
    Export:
      Name: !Sub "${ClusterPrefix}-protected-subnet-a-cidr"
  ProtectedSubnetC:
    Value: !Ref ProtectedSubnetC
    Export:
      Name: !Sub "${ClusterPrefix}-protected-subnet-c"
  ProtectedSubnetCCIDR:
    Value: !Ref ProtectedSubnetCCIDR
    Export:
      Name: !Sub "${ClusterPrefix}-protected-subnet-c-cidr"
  ProtectedSubnetD:
    Value: !Ref ProtectedSubnetD
    Export:
      Name: !Sub "${ClusterPrefix}-protected-subnet-d"
  ProtectedSubnetDCIDR:
    Value: !Ref ProtectedSubnetDCIDR
    Export:
      Name: !Sub "${ClusterPrefix}-protected-subnet-d-cidr"
  PrivateSubnetA:
    Value: !Ref PrivateSubnetA
    Export:
      Name: !Sub "${ClusterPrefix}-private-subnet-a"
  PrivateSubnetACIDR:
    Value: !Ref PrivateSubnetACIDR
    Export:
      Name: !Sub "${ClusterPrefix}-private-subnet-a-cidr"
  PrivateSubnetC:
    Value: !Ref PrivateSubnetC
    Export:
      Name: !Sub "${ClusterPrefix}-private-subnet-c"
  PrivateSubnetCCIDR:
    Value: !Ref PrivateSubnetCCIDR
    Export:
      Name: !Sub "${ClusterPrefix}-private-subnet-c-cidr"
  PrivateSubnetD:
    Value: !Ref PrivateSubnetD
    Export:
      Name: !Sub "${ClusterPrefix}-private-subnet-d"
  PrivateSubnetDCIDR:
    Value: !Ref PrivateSubnetDCIDR
    Export:
      Name: !Sub "${ClusterPrefix}-private-subnet-d-cidr"

# Route
  FirewallRouteTableA:
    Value: !Ref FirewallRouteTableA
    Export:
      Name: !Sub "${ClusterPrefix}-firewall-route-a"
  FirewallRouteTableC:
    Value: !Ref FirewallRouteTableC
    Export:
      Name: !Sub "${ClusterPrefix}-firewall-route-c"
  FirewallRouteTableD:
    Value: !Ref FirewallRouteTableD
    Export:
      Name: !Sub "${ClusterPrefix}-firewall-route-d"
  ProtectedRouteTableA:
    Value: !Ref ProtectedRouteTableA
    Export:
      Name: !Sub "${ClusterPrefix}-protected-route-a"
  ProtectedRouteTableC:
    Value: !Ref ProtectedRouteTableC
    Export:
      Name: !Sub "${ClusterPrefix}-protected-route-c"
  ProtectedRouteTableD:
    Value: !Ref ProtectedRouteTableD
    Export:
      Name: !Sub "${ClusterPrefix}-protected-route-d"
  PrivateRouteTableA:
    Value: !Ref PrivateRouteTableA
    Export:
      Name: !Sub "${ClusterPrefix}-private-route-a"
  PrivateRouteTableC:
    Value: !Ref PrivateRouteTableC
    Export:
      Name: !Sub "${ClusterPrefix}-private-route-c"
  PrivateRouteTableD:
    Value: !Ref PrivateRouteTableD
    Export:
      Name: !Sub "${ClusterPrefix}-private-route-d"

# VPCe
  NetworkFirewallVPCeA:
    Value: !Select [ 1, Fn::Split: [ ":", !Select [ 0, !GetAtt NetworkFirewall.EndpointIds ] ] ]
    Export:
      Name: !Sub "${ClusterPrefix}-network-firewall-a"
  NetworkFirewallVPCeC:
    Value: !Select [ 1, Fn::Split: [ ":", !Select [ 1, !GetAtt NetworkFirewall.EndpointIds ] ] ]
    Export:
      Name: !Sub "${ClusterPrefix}-network-firewall-c"
  NetworkFirewallVPCeD:
    Value: !Select [ 1, Fn::Split: [ ":", !Select [ 2, !GetAtt NetworkFirewall.EndpointIds ] ] ]
    Export:
      Name: !Sub "${ClusterPrefix}-network-firewall-d"

```

## 愚痴：NetworkFirewallをCFnから作ったとき、VPCeのIdが取れない

以下の通り、リストで返しやがるので、ちょっと不格好だけど組み込み関数を駆使した。

#### `AWS::NetworkFirewall::Firewall`のReturn valuesの仕様

> EndpointIds
The unique IDs of the firewall endpoints for all of the subnets that you attached to the firewall.
The subnets are not listed in any particular order.
*For example: ["us-west-2c:vpce-111122223333", "us-west-2a:vpce-987654321098", "us-west-2b:vpce-012345678901"].*

#### 同じように困っている人

* [AWS CloudFormation deployment of AWS Network Firewall](https://repost.aws/questions/QUzulcO9cnSwyfiKDqLGo8-w/aws-cloud-formation-deployment-of-aws-network-firewall)
* `Custom Resource`でLambdaをcallする手順に案内されていて笑った

#### やったこと

```yaml
Value: !Select [ 1, Fn::Split: [ ":", !Select [ 0, !GetAtt NetworkFirewall.EndpointIds ] ] ]
```

## 注意事項

* Productionで使わないこと
* インストールにコケても泣かないこと
* 私がこの記事をROSA側の変更に合わせてメンテすることはない