CloudFormationでハマった

# 概要
CloudFormationでハマったことを淡々と書いていく

# ハマったこと
## InternetGatewayをアタッチする
### ダメな例

```yaml
    IGWRoute:
      Type: AWS::EC2::Route
      DependsOn: IGW
      Properties:
        RouteTableId: !Ref PublicRouteTable
        DestinationCidrBlock: 0.0.0.0/0
        GatewayId: !Ref IGW
```

### エラー

```
route table rtb-xxxxxx and network gateway igw-xxxxxx belong to different networks
```

### 解決策
先に `AWS::EC2::VPCGatewayAttachment` でIGWをVPCに紐付けてあげないとです。

```yaml
  AttachGatewayToVPC:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties: 
      InternetGatewayId: !Ref IGW
      VpcId: !Ref VPC
          
  IGWRoute:
    Type: AWS::EC2::Route
    DependsOn: AttachGatewayToVPC
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref IGW
```

## サブネットを作成する

### ダメな例

```yaml
  SubnetPublic1a:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: !Ref SubnetPublic1aCidr
      AvailabilityZone: !Ref AWS::Region
      Tags:
        - Key: Name
          Value: !Join ['-', ['sb', !Ref Prefix, 'public1a']]
```

### エラー

```
Value (ap-northeast-1) for parameter availabilityZone is invalid. Subnets can currently only be created in the following availability zones: ap-northeast-1a, ap-northeast-1c.
```

### 解決策
`!Ref AWS::Region` だと `ap-northeast-1` までしか取れないからですね。

他のリージョンに `a` と `c` が必ずあるわけではないですが、あるところを狙っていきましょうという対応。オレゴンとか使えば間違いないんじゃないでしょうか。
// あと、 `AWS::XXX` の関数が入ったJoinの良い感じの書き方を知りたいです。

```yaml
  SubnetPublic1a:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: !Ref SubnetPublic1aCidr
      AvailabilityZone: !Join
        - ''
        - - !Ref AWS::Region
          - 'a'
      Tags:
        - Key: Name
          Value: !Join ['-', ['sb', !Ref Prefix, 'public1a']]
```

## ALBを作る
まず前提として、 `AWS::ElasticLoadBalancing::LoadBalancer` はCLB用。
`AWS::ElasticLoadBalancingV2::XXX` がALB用。2017/10/22時点ではNLBもV2で作れることを確認。

### 作成順
1. `AWS::ElasticLoadBalancingV2::LoadBalancer`
    - Webコンソールでいうロードバランサーの項目をまず作成を行う
2. `AWS::ElasticLoadBalancingV2::TargetGroup`
    - Webコンソールでいうターゲットグループの項目の作成を行う
    - ここはWebコンソールと違い、ALBと同時作成が行えるわけではない。
        - CloudFormationではALBとTGは別れているため、それぞれ作成する
3. `AWS::ElasticLoadBalancingV2::Listener`
    - 最後にリスナーの設定を行う
    - ここはHTTPもしくはHTTPSで受けたリクエストをどのターゲットグループへ配るかの設定
4. `AWS::ElasticLoadBalancingV2::ListenerRule`
    - [リスナールール](http://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/load-balancer-listeners.html) の設定（任意）。

### コード

```yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: ALB Template

Parameters:
  Prefix:
    Type: String
  VPCId:
    Type: String
  SubnetPublic1a:
    Type: String
  SubnetPublic1c:
    Type: String
  ALBSg:
    Type: String

Resources:
  TargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Join ['-', ['tg', !Ref Prefix]]
      VpcId: !Ref VPCId
      # HealthCheck
      HealthCheckIntervalSeconds: 30
      HealthCheckPath: '/'
      HealthCheckPort: '80'
      HealthCheckProtocol: HTTP
      HealthCheckTimeoutSeconds: 5
      HealthyThresholdCount: 5
      UnhealthyThresholdCount: 2
      Matcher:
        HttpCode: '200'
      # Routing
      # MEMO: トラフィックポートの場合は 'traffic-port'
      Port: '80'
      Protocol: HTTP
      TargetType: 'instance'
      TargetGroupAttributes:
        # ターゲットの登録解除までの待機時間
        - Key: 'deregistration_delay.timeout_seconds'
          Value: 300
      
  ALB:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Type: 'application'
      Name: !Join ['-', ['alb', !Ref Prefix]]
      SecurityGroups: [!Ref ALBSg]
      IpAddressType: 'ipv4'
      Scheme: 'internet-facing'
      Subnets:
        - !Ref SubnetPublic1a
        - !Ref SubnetPublic1c
      # MEMO:
      #   LoadBalancerAttributesはLBのログの保存・削除保護・アイドルタイムアウトの設定を行う事が可能
      # 参考:
      #   ドキュメント
      #     http://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/APIReference/API_LoadBalancerAttribute.html
      #     http://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/application-load-balancers.html#load-balancer-attributes
      #   アイドルタイムアウトについて(CLB)
      #     http://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/classic/config-idle-timeout.html
      LoadBalancerAttributes:
        - Key: idle_timeout.timeout_seconds
          Value: 140
      
  HTTPListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      # Certificates: !Ref ACMArn
      DefaultActions:
        - Type: 'forward'
          TargetGroupArn: !Ref TargetGroup
      LoadBalancerArn: !Ref ALB
      Port: '80'
      Protocol: 'HTTP'
      # http://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/application/create-https-listener.html
      # SslPolicy: 'ELBSecurityPolicy-2016-08'
  
  ListenerRule:
    Type: AWS::ElasticLoadBalancingV2::ListenerRule
    Properties:
      Actions:
        - Type: 'forward'
          TargetGroupArn: !Ref TargetGroup
      # MEMO:
      #   リスナーの条件
      # 参考:
      #   http://docs.aws.amazon.com/ja_jp/elasticloadbalancing/latest/APIReference/API_RuleCondition.html
      Conditions:
        - Field: 'path-pattern'
          Values: ['/img/*']
      ListenerArn: !Ref HTTPListener
      Priority: 10
```

### TODO
* ドレイニングの設定周りがよくわからないので調べる
  - 接続中のHTTPリクエストの処理後、ターゲットグループの `idle_timeout.timeout_seconds` だけ待つのか
  - 切断リクエストが来てから `idle_timeout.timeout_seconds` だけ待つのか