CloudFormationテンプレートを少し動的なものに変更してみた。 

## はじめに
[前回記事](https://qiita.com/President_Taka/items/8384110f259b31abdc1b)で作成したCloudFormationのテンプレートですが、値をベタ書きしている箇所は、変更時にはコードの修正が必要となります。そこで、parametersや組み込み関数を使用することで、スタック作成時にマネジメントコンソール上で変更できるように少し動的なテンプレートに変更していこうと思います。

## CidrBlock
CidrBlockにRef関数の導入と併せて、自動的にSubnetのCidr値も設定されるようにします。[こちらの記事](https://dev.classmethod.jp/cloud/aws/cidr_cloudformation/)が非常に参考になりました。


```yaml:Network_Layer.yml
# ------------------------------------------------------------#
#  Parametersの追加
# ------------------------------------------------------------#
Parameters:
  CidrBlock:
    Description: Please input an IP range
    Type: String
    Default: 10.0.0.0/16
# ------------------------------------------------------------#
#  VPCリソースの変更
# ------------------------------------------------------------#
  CidrBlock: 10.0.0.0/16  
      ↓
  CidrBlock: !Ref CidrBlock
# ------------------------------------------------------------#
#  Subnetリソースの変更
# ------------------------------------------------------------#
  PublicSubnet1a:
      CidrBlock: 10.0.0.0/24
          ↓
      CidrBlock: !Select [ 0, !Cidr [ !GetAtt SampleVPC.CidrBlock, 1, 8 ]]
      
  PrivateSubnet1a:
      CidrBlock: 10.0.1.0/24
          ↓
      CidrBlock: !Select [ 1, !Cidr [ !GetAtt SampleVPC.CidrBlock, 2, 8 ]]

  PublicSubnet1c:
      CidrBlock: 10.0.2.0/24
          ↓
      CidrBlock: !Select [ 2, !Cidr [ !GetAtt SampleVPC.CidrBlock, 3, 8 ]]
      
  PrivateSubnet1c:
      CidrBlock: 10.0.3.0/24
          ↓
      CidrBlock: !Select [ 3, !Cidr [ !GetAtt SampleVPC.CidrBlock, 4, 8 ]]
      
```

##AvailabilityZone
続いて、AvailabilityZoneについて修正します。こちらについてはAWS固有のパラメーター型の`AWS::EC2::AvailabilityZone::Name`を使用します。これによりスタックを作成しているリージョン内に存在する値を選択できるようになります。

```yaml:Network_Layer.yml
# ------------------------------------------------------------#
#  Parametersの追加
# ------------------------------------------------------------#
Parameters:
  AvailabilityZone1a:
    Description: Please input Availability Zone
    Type: AWS::EC2::AvailabilityZone::Name
    Default: ap-northeast-1a
  AvailabilityZone1c:
    Description: Please input Availability Zone
    Type: AWS::EC2::AvailabilityZone::Name
    Default: ap-northeast-1c

# ------------------------------------------------------------#
#  Subnetリソースの変更
# ------------------------------------------------------------#
  PublicSubnet1a:
      AvailabilityZone: ap-northeast-1a
         ↓
      AvailabilityZone: !Ref AvailabilityZone1a

  PrivateSubnet1a:
      AvailabilityZone: ap-northeast-1a
         ↓
      AvailabilityZone: !Ref AvailabilityZone1a
      
  PublicSubnet1c:
      AvailabilityZone: ap-northeast-1c
         ↓
      AvailabilityZone: !Ref AvailabilityZone1c
     
  PrivateSubnet1c:
      AvailabilityZone: ap-northeast-1c
         ↓
      AvailabilityZone: !Ref AvailabilityZone1c
```

## ImageId
上記と同様に、AWS固有のパラメーター型`AWS::EC2::Image::Id`を使用します。ただ、下記のユーザーガイドにあるように、こちらは選択式にはならないようです。
>Amazon EC2 イメージ ID (ami-0ff8a91507f77f867 など)。AWS CloudFormation コンソールには、このパラメータータイプの値のドロップダウンリストが表示されないことに注意してください。

```yaml:Application_Layer
# ------------------------------------------------------------#
#  Parametersの追加
# ------------------------------------------------------------#
Parameters:
  AmiID:
    Description: Please input Ami ID
    Type: AWS::EC2::Image::Id
    Default: ami-0af1df87db7b650f4

# ------------------------------------------------------------#
#  EC2リソースの変更
# ------------------------------------------------------------#
  SampleEC21a:
      ImageId: ami-0af1df87db7b650f4
         ↓
      ImageId: !Ref AmiID

  SampleEC21c:
      ImageId: ami-0af1df87db7b650f4
         ↓
      ImageId: !Ref AmiID
```

## KeyPair
最後にKeyPairも`AWS::EC2::KeyPair::KeyName`に変更します。これで、既存のKerPairから選択できるようになります。

```yaml:Application_Layer
# ------------------------------------------------------------#
#  Parametersの追加
# ------------------------------------------------------------#
Parameters:
  KeyPair:
    Description: Please input KeyPair name
    Type: AWS::EC2::KeyPair::KeyName

# ------------------------------------------------------------#
#  EC2リソースの変更
# ------------------------------------------------------------#
  SampleEC21a:
      KeyName: aws-test
         ↓
      KeyName: !Ref KeyPair

  SampleEC21c:
      KeyName: aws-test
         ↓
      KeyName: !Ref KeyPair
```
## 完成
修正後のコードはこんな感じになります。

```yaml:Network_Layer.yml
AWSTemplateFormatVersion: 2010-09-09
Description: Network_Layer Template

Parameters:
  CidrBlock:
    Description: Please input an IP range
    Type: String
    Default: 10.0.0.0/16
  AvailabilityZone1a:
    Description: Please input Availability Zone
    Type: AWS::EC2::AvailabilityZone::Name
    Default: ap-northeast-1a
  AvailabilityZone1c:
    Description: Please input Availability Zone
    Type: AWS::EC2::AvailabilityZone::Name
    Default: ap-northeast-1c


Resources:
# ------------------------------------------------------------#
#  VPC
# ------------------------------------------------------------#
  SampleVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref CidrBlock
      EnableDnsSupport: true
      EnableDnsHostnames: true
      InstanceTenancy: default
      Tags:
        - Key: name
          Value: SampleVPC

# ------------------------------------------------------------#
#  Subnet
# ------------------------------------------------------------#
  PublicSubnet1a:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref SampleVPC
      CidrBlock: !Select [ 0, !Cidr [ !GetAtt SampleVPC.CidrBlock, 1, 8 ]]
      MapPublicIpOnLaunch: true
      AvailabilityZone: !Ref AvailabilityZone1a
      Tags:
      - Key: Name
        Value: PublicSubnet1a
  PrivateSubnet1a:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref SampleVPC
      CidrBlock: !Select [ 1, !Cidr [ !GetAtt SampleVPC.CidrBlock, 2, 8 ]]
      AvailabilityZone: !Ref AvailabilityZone1a
      Tags:
      - Key: Name
        Value: PrivateSubnet1a
  PublicSubnet1c:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref SampleVPC
      CidrBlock: !Select [ 2, !Cidr [ !GetAtt SampleVPC.CidrBlock, 3, 8 ]]
      MapPublicIpOnLaunch: true
      AvailabilityZone: !Ref AvailabilityZone1c
      Tags:
      - Key: Name
        Value: PublicSubnet1c
  PrivateSubnet1c:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref SampleVPC
      CidrBlock: !Select [ 3, !Cidr [ !GetAtt SampleVPC.CidrBlock, 4, 8 ]]
      AvailabilityZone: !Ref AvailabilityZone1c
      Tags:
      - Key: Name
        Value: PrivateSubnet1c
```

```yaml:Application_Layer
AWSTemplateFormatVersion: 2010-09-09
Description: Application_Layer Template

Parameters:
  InstanceType:
    Type: String
    Default: t2.micro
    AllowedValues:
      - t2.micro
      - m1.small
      - m1.large
  AmiID:
    Description: Please input Ami ID
    Type: AWS::EC2::Image::Id
    Default: ami-0af1df87db7b650f4
  KeyPair:
    Description: Please input KeyPair name
    Type: AWS::EC2::KeyPair::KeyName

Resources:
# ------------------------------------------------------------#
#  EC2
# ------------------------------------------------------------#
  SampleEC21a:
    Type: AWS::EC2::Instance
    Properties:
      NetworkInterfaces:
        - SubnetId: !ImportValue NetworkLayer-PublicSubnet1a
          GroupSet:
            - !ImportValue SecurityLayer-SecurityGroupEC2
          DeviceIndex: 0
      InstanceType: !Ref InstanceType
      ImageId: !Ref AmiID
      Tags:
        - Key: Name
          Value: SampleEC21a
      KeyName: !Ref KeyPair
  SampleEC21c:
    Type: AWS::EC2::Instance
    Properties:
      NetworkInterfaces:
        - SubnetId: !ImportValue NetworkLayer-PublicSubnet1c
          GroupSet:
            - !ImportValue SecurityLayer-SecurityGroupEC2
          DeviceIndex: 0
      InstanceType: !Ref InstanceType
      ImageId: !Ref AmiID
      Tags:
        - Key: Name
          Value: SampleEC21c
      KeyName: !Ref KeyPair

```

## 最後に
実際にマネジメントコンソールでスタックを作成してみます。パラメータが追加されてそれぞれの値が変更できるようになりました。

<img width="1431" alt="スクリーンショット 2020-03-02 14.22.25.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/438093/33f88e81-bfce-6a48-ff03-e73623f4b8af.png">

<img width="1431" alt="スクリーンショット 2020-03-04 10.04.00.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/438093/4669b574-d2c8-b074-359e-2dbbfecba5fc.png">



SubnetのCidrBlockについても、目的の値で作成することができました。
<img width="1431" alt="スクリーンショット 2020-03-02 15.14.15.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/438093/39ab2dc7-2813-dcbc-a979-9afe42a4c099.png">


## 参考記事

`CidrBlock`
https://dev.classmethod.jp/cloud/aws/cidr_cloudformation/
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-cidr.html
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference-getatt.html
https://qiita.com/KoyoIshikawa/items/1524fd8579f8fe2cba16

`AvailabilityZone` `ImageID` `KerPair`
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/parameters-section-structure.html#aws-specific-parameter-types