CloudFormation~テンプレートのネストとNACL~

こんにちはkatoです。

今回はCloudFormationのネストテンプレートの書き方とNACLの作成時の注意点に関してご紹介いたします。

 

概要

先日、少し規模の大きいAWS環境上のシステムをCloudFormationのテンプレートに落とし込む機会があり、その際少し詰まった個所などがあったので、今回ご紹介したいと思います。

今回ご紹介するのは、VPCやサブネット、NACLなどのネットワーク周りのリソースに関してになります。

なお、CloudFormationのスタックの書き方などの基本的な部分は今回は省略させていただきます。

また、今回ご説明させていただくテンプレートはJSONで記載したものとなりますので、YAML派の方はご注意ください。

 

テンプレートのネスト

まず初めにテンプレートのネストに関して、ご紹介したいと思います。

テンプレートを作成して、stackの実行を行ったらいきなり以下のエラーにぶつかりました。

テンプレートの検証エラー: Template format error: Number of resources, 219, is greater than maximum allowed, 200

CloudFormationでは単一のテンプレートで作成できるリソースの数に200個の制限があります。

あまりリソースが200個を超える機会はないと思いますが、NACLやSecurityGroup等の作成をCloudFormationで行おうとすると意外とリソース数がかさむのでご注意ください。

このように作成するリソースが増えた場合には、テンプレートを複数に分けてテンプレートをネスト実行させる必要があります。

テンプレートをネストさせる場合の手順としては下記となります。

①ネスト実行される子のテンプレートファイルをS3にアップロード
②CloudFormationにて親のテンプレートを実行
③順番に子のテンプレートファイルを実行

親のテンプレートにネスト実行する子のテンプレートファイルのS3URLを記載することで、親のテンプレートを実行した際に子のテンプレートがネスト実行されます。

簡単な記載例を下記に記します。

親テンプレート

{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Description" : "CloudFormation Nest Template Parent",
  "Parameters" : {
    "TemplateURL01" : {
      "Description" : "Enter TemplateURL01" ,
      "Type" : "String"
    },
    "TemplateURL02" : {
      "Description" : "Enter TemplateURL02" ,
      "Type" : "String"
    }
  },
  "Resources" : {
    "NestCloudformation01" : {
      "Type" : "AWS::CloudFormation::Stack",
      "Properties" : {
        "TemplateURL" : {"Ref" : "TemplateURL01"}
      }

    },
    "NestCloudformation02" : {
      "Type" : "AWS::CloudFormation::Stack",
      "Properties" : {
        "TemplateURL" : {"Ref" : "TemplateURL02"}
      }
    }
  }
}

上記の親テンプレートのサンプルはStackの実行時にパラメータとして子のURLを指定するようにしたものです。

単純に 複数のテンプレートを実行するだけなら、これだけで実行可能ですが、テンプレート間でリソース参照が発生する場合には、後述する設定を入れなければいけないのでご注意ください。

リソースを管理しやすくする意味でも、一つのテンプレートファイルにすべてのリソースを記載するのではなく、小分けにしてネストさせるような書き方をしたほうが良いかと思います。

1点注意点として、テンプレートのネストは、どこかでエラーが発生した場合全てのテンプレートがロールバックして取り消されますのでご注意ください。

 

テンプレート間でのリソース参照

前述しましたネストされたテンプレート間でのリソース参照に関してになります。

CloudFormationにてAWS環境を構築する場合、VPC IDやSubnet IDなどを参照しなければならないケースが多々あります。

テンプレートをネストさせた場合、先程記載しようなサンプルテンプレートではリソースを参照することができません。

テンプレート間でリソース参照を行う場合には、OutputsとGetAttの組み込み関数を利用します。

以下にサンプルを記載します。

 

親テンプレート

{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Description" : "CloudFormation Nest Template Parent",
  "Parameters" : {
    "TemplateURL01" : {
      "Description" : "Enter TemplateURL01" ,
      "Type" : "String"
    },
    "TemplateURL02" : {
      "Description" : "Enter TemplateURL02" ,
      "Type" : "String"
    }
  },
  "Resources" : {
    "NestCloudformation01" : {
      "Type" : "AWS::CloudFormation::Stack",
      "Properties" : {
        "TemplateURL" : {"Ref" : "TemplateURL01"}
      }

    },
    "NestCloudformation02" : {
      "Type" : "AWS::CloudFormation::Stack",
      "Properties" : {
        "TemplateURL" : {"Ref" : "TemplateURL02"},
        "Parameters" : {
          "VpcId" : { "Fn::GetAtt" : ["NestCloudformation01", "Outputs.VpcId"] }
        }
      }
    }
  }
}

 

子テンプレートA

  "AWSTemplateFormatVersion" : "2010-09-09",
  "Description" : "AWS VPC Network create",
  "Resources" : {
    "VPC" : {
      "Type" : "AWS::EC2::VPC",
      "Properties" : {
        "CidrBlock" : "192.168.0.0/16",
        "Tags" : [
          {"Key" : "Name", "Value" : "VPC-01" }
        ]
      }
    }
  },
  "Outputs" : {
    "VpcId" : {
      "Value" :  { "Ref" : "VPC" }
    }
  }
}

 

子テンプレートB

{
  "AWSTemplateFormatVersion" : "2010-09-09",
  "Description" : "AWS IGW create",
  "Parameters" : {
    "VpcId" : { "Type": "AWS::EC2::VPC::Id" }
  },
  "Resources" : {
    "InternetGateway" : {
      "Type" : "AWS::EC2::InternetGateway",
      "Properties" : {
        "Tags" : [
          {"Key" : "Name", "Value" : "IGW-01" }
        ]
      }
    },
    "AttachGateway" : {
      "Type" : "AWS::EC2::VPCGatewayAttachment",
      "Properties" : {
         "VpcId" : { "Ref" : "VpcId" },
         "InternetGatewayId" : { "Ref" : "InternetGateway" }
      }
    }
  }
}

上記サンプルは子テンプレートAでVPCを作成し、子テンプレートBで作成したVPCにInternet Gatewayをアタッチするものになります。

まず子テンプレートAで 作成したリソースをOutputsで親テンプレートに渡します。

親テンプレートでは子テンプレートAから受け取ったリソースをパラメータとして子テンプレートBに渡します。

親テンプレートで子テンプレートのOutputsを取得するためにGetAtt組み込み関数を利用しています。

子テンプレートBでは親テンプレートから受け取るパラメータのタイプを指定します。

VPC IDの場合は「AWS::EC2::VPC::Id」、サブネットIDの場合は「AWS::EC2::Subnet::Id」といった具合に受け取るパラメータに応じてタイプの指定を変更します。

後の使い方は普通のテンプレートと同様になります。

リソース参照の数が多くなると記載が大変になりますが、テンプレートをネストさせる上では必須の内容となっております。

 

NACLの作成

CloudFormationでNACLを作成する場合に必要となるリソースは下記の3つになります。

・NACL
・ルール(Entry)
・サブネットの関連付け

ここで注意する点が3点あります。

まず初めにNACLのルール数制限です。

NACLにはデフォルトで20個までのルールしか付けられないので、もちろんCloudFormationでも20個以上のルールを指定した場合にはエラーとなります。

CloudFormationで新規にNACLを含む環境構築を行う場合などには、事前に制限緩和申請を行うことを忘れないようにしてください。

2点目がルールの作成に関してです。

NACLのルールにはそれぞれルール番号が割り当てられているため、CloudFormationでルールを作成しようと思うと、1つのルールに対して1つのリソースといったようにリソースの数がかなり多くなります。

インバウンドとアウトバウンドのルールをそれぞれ細かく区切っているような場合には、ここだけでかなりのリソース数となってしまいます。

3つ目がサブネットの関連付けに関してです。

NACLとサブネットの関連付けの際にはサブネットIDを指定するのですが、ここではサブネットIDをリスト指定することができません。

ルールと同じように、サブネットの数だけリソースを作成する必要があります。

下記のような記載は一見成功しそうですが、エラーとなるのでご注意ください。

 

"NACLAssociation" : {
  "Type" : "AWS::EC2::SubnetNetworkAclAssociation",
  "Properties" : {
    "SubnetId" : {
      "Ref" : "SubnetA",
      "Ref" : "SubnetB"
    },
  "NetworkAclId" : { "Ref" : "NACL" }
}

 

以下にNACLを作成するテンプレートのサンプルを記載します。

 

{ 
  "AWSTemplateFormatVersion" : "2010-09-09", 
  "Description" : "AWS NACL",
  "Parameters" : {
    "VPC" : { "Type": "AWS::EC2::VPC::Id" },]
    "SubnetA" : { "Type": "AWS::EC2::Subnet::Id" },
    "SubnetC" : { "Type": "AWS::EC2::Subnet::Id" }
  },
  "Resources" : { 
    "NACL" : {
      "Type" : "AWS::EC2::NetworkAcl",
      "Properties" : {
        "VpcId" : { "Ref" : "VPC" },
        "Tags" : [ { "Key" : "Name", "Value" : "NACL" } ]
      }
    },
    "NACLEntry10" : {
      "Type" : "AWS::EC2::NetworkAclEntry",
      "Properties" : {
        "NetworkAclId" : { "Ref" : "NACL" },
        "RuleNumber" : "10",
        "Protocol" : "-1",
        "RuleAction" : "deny",
        "Egress" : "false",
        "CidrBlock" : "192.168.0.0/24",
        "PortRange" : { "From" : "-1", "To" : "-1" }
      }
    },
    "NACLEntry20" : {
      "Type" : "AWS::EC2::NetworkAclEntry",
      "Properties" : {
        "NetworkAclId" : { "Ref" : "NACL" },
        "RuleNumber" : "11",
        "Protocol" : "-1",
        "RuleAction" : "deny",
        "Egress" : "false",
        "CidrBlock" : "192.168.10.0/24",
        "PortRange" : { "From" : "-1", "To" : "-1" }
      }
    },
    "NACLEntryOut10" : {
      "Type" : "AWS::EC2::NetworkAclEntry",
      "Properties" : {
        "NetworkAclId" : { "Ref" : "NACL" },
        "RuleNumber" : "10",
        "Protocol" : "-1",
        "RuleAction" : "deny",
        "Egress" : "true",
        "CidrBlock" : "192.168.0.0/24",
        "PortRange" : { "From" : "-1", "To" : "-1" }
      }
    },
    "NACLEntryOut20" : {
      "Type" : "AWS::EC2::NetworkAclEntry",
      "Properties" : {
        "NetworkAclId" : { "Ref" : "NACL" },
        "RuleNumber" : "11",
        "Protocol" : "-1",
        "RuleAction" : "deny",
        "Egress" : "true",
        "CidrBlock" : "192.168.10.0/24",
        "PortRange" : { "From" : "-1", "To" : "-1" }
      }
    },
    "NACLAssociation01" : {
      "Type" : "AWS::EC2::SubnetNetworkAclAssociation",
      "Properties" : {
        "SubnetId" : { "Ref" : "SubnetA" },
        "NetworkAclId" : { "Ref" : "NACL" }
      }
    },
    "NACLAclAssociation02" : {
      "Type" : "AWS::EC2::SubnetNetworkAclAssociation",
      "Properties" : {
        "SubnetId" : { "Ref" : "SubnetC" },
        "NetworkAclId" : { "Ref" : "NACL" }
      }
    }
  }
}

上記サンプルは、インバウンドとアウトバウンドのルールを含むNACLを作成し、サブネットの関連付けを行っています。

インバウンドとアウトバウンドのルールの記載はほぼ同じ内容となっており、Egressの設定値でインバウンドかアウトバウンドかが決まります(falseがインバウンド、trueがアウトバウンド)。

上記の様に単純なNACLであっても多くのリソースの作成が必要となるので、CloudFormationでNACLを作成する場合には、リソース数の制限にも注意しながらテンプレートの作成を進めてください。

 

まとめ

今回はCloudFormationのネストテンプレートやNACLの書き方にフォーカスしてご説明させていただきました。

CloudFormationはAWS環境を簡単に複製できるので、とても便利な機能となっておりますが、テンプレートの書き方の癖や修正やデバッグの面倒くささがついて回ります。

正しくテンプレートを書けるようになれば管理も楽なので、様々なシステムにも流用できるようになるかと思います。

 

 

 

このブログの著者