【AWS CDK】CDKメリット Security Group Connectionsが熱い

## はじめに
AWS CDKにおいて個人的に「これいいよね」、「これ便利だよね」ってものをご紹介できればと思います。
「あ、これ使ってみたいかも」とか思われたらAWS CDKのご利用をご検討頂ければと思います。
今回は「Connections Class」です。
※ 本ブログに記載した内容は個人の見解であり、所属する会社、組織とは全く関係ありません。

## Connections Classとは 
Security Group間のネットワーク接続のための許可設定を簡単に投入出来ます。
ようは、Security Groupがアタッチされているリソースの送信元にはOutboundの設定を、Security Groupがアタッチされている送信先にはInboundの設定を簡単に入れ込むためのものになります。
言葉だけだと分かり辛いので、ユースケースに沿ってご紹介させて頂きます。

## ユースケース
### ユースケースイメージ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3635217/44fd3fc9-41f5-a92d-2824-f10fa2c1028d.png)

上記イメージのようにWebサーバのEC2からPostgreSQL RDSに通信する場合を例にとります。

Security Groupの要求を整理すると以下マトリックス図になります。
### Security Groupマトリックス図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3635217/23e4bbf5-1ee7-3e9b-8c6d-f5977dd19788.png)

今回のユースケース規模だとわざわざマトリックス図での整理はしないと思いますが、実際構築する際は利用するAWSサービスが多い=作成するSecurity Groupも多いと思います。
Security Groupが多くなるほど混乱するので、上記のような整理をされると思います。

## ユースケースに対してCloudFormationでの実装イメージ
今回のユースケースでは、web-ec2-sgのアウトバウントと、postgresql-rds-sgのインバウンドのルールをそれぞれ書く必要があります。

### web-ec2-sgのアウトバウンドに関するCloudFormationイメージ
~~~json:usecase-stack.template.json
  ~~略~~
  "webec2sgegress": {
   "Type": "AWS::EC2::SecurityGroupEgress",
   "Properties": {
    "IpProtocol": "tcp",
    "FromPort": 5432,
    "GroupId": {
     "Fn::GetAtt": [
      "webec2sg",
      "GroupId"
     ]
    },
    "ToPort": 5432
    "DestinationSecurityGroupId": {
     "Fn::GetAtt": [
      "postgresqlrdssg",
      "GroupId"
     ]
    },
   },
  },
~~~

### postgresql-rds-sgのインバウンドに関するCloudFormationイメージ
~~~json:usecase-stack.template.json
  ~~略~~
  "postgresqlrdssgingress": {
   "Type": "AWS::EC2::SecurityGroupIngress",
   "Properties": {
    "IpProtocol": "tcp",
    "FromPort": 5432,
    "SourceSecurityGroupId": {
     "Fn::GetAtt": [
      "webec2sg",
      "GroupId"
     ]
    },
    "ToPort": 5432
    "GroupId": {
     "Fn::GetAtt": [
      "postgresqlrdssg",
      "GroupId"
     ]
    },
   },
  },
~~~

上記イメージのように両方別々に書く必要があります。
これがConnectionsではすごくシンプルに書けてしまいます。

## ユースケースに対してAWS CDKでの実装イメージ
Connections ClassにはallowFrom MethodやallowTo Methodが用意されています。
両方使う必要はなくこのどちらかを使えば上記のようなCloudFormationテンプレートが構成されます。

### 前提Security Groupの作成
~~~typescript:usecase-connections.ts
    ~~略~~
    // web-EC2用のSG
    const webEc2Sg = new ec2.SecurityGroup(this, "Web-EC2-SG", {
      vpc: Vpc,
      allowAllOutbound: false,
      securityGroupName:"EC2-SG",
    });
   // PosgreSQL-RDS用のSG
    const psqlRdsSg = new ec2.SecurityGroup(this, "PosgreSQL-RDS-SG", {
      vpc: Vpc,
      allowAllOutbound: false,
      securityGroupName:"RDS-SG",
    });
~~~~
※「allowAllOutbound: false」にするとdefault設定のall アウトバウンド許可を削除してくれます。
　必要なければ削除した方がアタックを受けた際のデータ送信を遮断できる可能性があるためセキュアになるので、積極的な活用がお勧めです。

### allowFrom Methodを利用する場合
~~~typescript:usecase-connections.ts
    // allowFrom Method
    psqlRdsSg.connections.allowFrom(webEc2Sg, ec2.Port.tcp(5432));
~~~
allowFrom Methodでは、「PosgreSQL-RDS用のSG」のTCP 5432番に対して、「web-EC2用のSG」から着信するために通信を許可するイメージで書きます。
つまり、「PosgreSQL-RDS用のSG」のTCP 5432番に対して、「web-EC2用のSG」からの着信を許可するためにインバウンドの設定が入ります。
合わせて、「web-EC2用のSG」から「PosgreSQL-RDS用のSG」のTCP 5432番に対して送信を許可するためにアウトバウンドの設定が入ります。
この1文で上記CloudFormationの該当箇所が全て生成されます。

### allowTo Methodを利用する場合
 ~~~typescript:usecase-connections.ts
    // allowto Method
    webEc2Sg.connections.allowTo(psqlRdsSg, ec2.Port.tcp(5432));
~~~~
allowTo Methodでは、「web-EC2用のSG」から「PosgreSQL-RDS用のSG」のTCP 5432番へ送信するためにイメージで書きます。
つまり、「web-EC2用のSG」から「PosgreSQL-RDS用のSG」のTCP 5432番に対して送信を許可するためにアウトバウンドの設定が入ります。
合わせて、「PosgreSQL-RDS用のSG」のTCP 5432番に対して、「web-EC2用のSG」からの着信を許可するためにインバウンドの設定が入ります。
この1文で上記CloudFormationの該当箇所が全て生成されます。

目的に対して、実現できることの差異はありません。
どのSecurity Groupを起点にするかで使用するMethodを切り替える形です。

## 利用する際のポイント
AWS CDKのAPPを書く時は、基本的にベストプラクティスに従って使い勝手を考慮し、Constructsコードに分割し、Stackコードでつなげていく構成を取ると思います。
今回のユースケースイメージでは、「web-EC2用のSG」、「PosgreSQL-RDS用のSG」両方とも同一Constructsコードで書いてしまっていますが、実際は以下のように別Constructsコードで書くことが多いと思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3635217/b07f1c9a-a734-c439-f0bb-73a1fade6f6d.png)

### Stackコードイメージ
 ~~~typescript:usecase-connections-stack.ts
~~略~~
    // NW Constructsコードのリソース生成
    const nw = new nw(this, "usercase-nw", {
    });
    // WebEC2 Constructsコードのリソース生成
    const webEc2 = new webec2(this, "usecase-webec2", {
      vpc: nw.vpc,
    });
    // RDS Constructsコードのリソース生成
    const rds = new rds(this, "usecase-rds", {
      vpc: nw.vpc,
      webEc2Sg: webEc2.webEc2Sg
    });
~~~~

上記Stackコードイメージの場合、WebEC2に関連するリソース(「web-EC2用のSG」を含む)が先に生成され、その後RDSに関連するリソース(「PosgreSQL-RDS用のSG」を含む)が生成されます。
Connections Classを利用する場合、双方のSecurity Group Objectが必要になります。
よって、「RDS Constructsコード」側でないとConnections Classは利用できません。
このようにStackコードで生成される後段(今回のイメージでは「RDS Constructsコード」)で書く必要があり、Constructsコード分割、Stackコード構成検討の際に意識しておく必要があります。

また、複数人で1APPを書くときは、「基本的にallowFromを使う！」等決めておくとスムーズに進みます。
しかし、設定したい対向がSecurity Groupでないと利用できないので、その場合は素直に「addEgressRule」、「addIngressRule」を利用する必要があります。

## まとめ
以上より、Connections Classを使えば簡単に双方向通信でのSecurity Groupの設定ができることをご紹介させて頂きました。
付随するメリットですが、Security Groupがアタッチされるリソース同士で通信する場合は、どんなイメージで使うの？さえ整理しておけばいいので、概要設計的なイメージだけで十分です。
つまり、ユースケースイメージのようなイメージが処理フロー毎にあるだけで使えます。
Security Groupマトリックス図のような細かな整理は必要ありません。
正直これだけで嬉しいですよね。
ご興味を持って頂けたらAWS CDKでDeployを試してみてください。
※ 本ブログに記載した内容は個人の見解であり、所属する会社、組織とは全く関係ありません。