CloudFormationのネスト機能を利用してみます。イメージは下記の公式資料より。
ユースケース
ネスト機能を利用するユースケースは、以下の2パターンがあります。
単一のCloudFormationスタックでは、テンプレートが長大となってしまう場合
単一のCloudFormationテンプレートで宣言できるResourceの最大数は200個までとなっているので、それを超過してしまうような場合です。下記のQiitaの投稿がそのケースにあたります。VPC, Subnet, EC2でテンプレートを別け、それを包括ような形で上位のテンプレートを作成している例となります。
作成するResourceの共通設定内容をテンプレート化して管理する方法
例えば、多数のALBを作成する場合、共通となる設定内容をテンプレート化して、各ALBで参照しながら作成する方法となります。
AWS CloudFormation のベストプラクティス - ネストされたスタックを使用して共通テンプレートパターンを再利用する
今回はこちらのユースケースでの、ネスト化されたスタック作成を試してみます。
作業
複数のALBを作成するにあたり、各ALBリソースで共通設定となるパラメータを、テンプレート化して作成してみます。
共通設定内容をテンプレート化
共通設定となるResourceを定義、各ALBごとに動的に変更されるだろう内容をParameterとして外出ししたCloudFormationを作成します。以下が、そのCloudFormationテンプレートとなります。
template-alb.yml
AWSTemplateFormatVersion: "2010-09-09" Description: "CloudFormation Template for ALB" Parameters: VpcId: Type: "AWS::EC2::VPC::Id" Subnet: Type: "List<AWS::EC2::Subnet::Id>" LoadBalancerName: Type: "String" EC2Instance: Type: "AWS::EC2::Instance::Id" Resources: SecurityGroup: # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-security-group-rule.html Type: "AWS::EC2::SecurityGroup" DeletionPolicy: "Delete" Properties: GroupName: !Sub for-alb-${LoadBalancerName} GroupDescription: !Sub for-alb-${LoadBalancerName} # SecurityGroupEgress: # - Security Group Rule SecurityGroupIngress: - CidrIp: "0.0.0.0/0" # CidrIpv6: String # Description: String IpProtocol: "tcp" FromPort: "80" ToPort: "80" # SourceSecurityGroupId: String # SourceSecurityGroupName: String # SourceSecurityGroupOwnerId: String Tags: - Key: "Name" Value: !Sub for-alb-${LoadBalancerName} VpcId: !Ref "VpcId" S3Bucket: # https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-s3-bucket.html Type: "AWS::S3::Bucket" DeletionPolicy: "Delete" DependsOn: "SecurityGroup" Properties: # AccessControl: String # AccelerateConfiguration: # AccelerateConfiguration # AnalyticsConfigurations: # - AnalyticsConfiguration # BucketEncryption: # BucketEncryption BucketName: !Sub logs-elb-${LoadBalancerName}-${AWS::AccountId} # CorsConfiguration: # CorsConfiguration # InventoryConfigurations: # - InventoryConfiguration LifecycleConfiguration: Rules: - # AbortIncompleteMultipartUpload: # AbortIncompleteMultipartUpload # ExpirationDate: String ExpirationInDays: "30" Id: !Sub s3-lifecycle-rule-${LoadBalancerName} NoncurrentVersionExpirationInDays: "30" # NoncurrentVersionTransitions: # - NoncurrentVersionTransition # Prefix: String Status: "Enabled" # TagFilters: # - TagFilter # Transitions: # - Transition # LoggingConfiguration: # LoggingConfiguration # MetricsConfigurations: # - MetricsConfiguration # NotificationConfiguration: # NotificationConfiguration # PublicAccessBlockConfiguration: # PublicAccessBlockConfiguration # ReplicationConfiguration: # ReplicationConfiguration # Tags: # - Key: String # Value: String # VersioningConfiguration: # VersioningConfiguration # WebsiteConfiguration: # WebsiteConfiguration S3BucketPolicy: # https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-s3-bucket.html Type: "AWS::S3::BucketPolicy" DeletionPolicy: "Delete" DependsOn: "S3Bucket" Properties: Bucket: !Ref S3Bucket PolicyDocument: Statement: - Action: - "s3:PutObject" Effect: "Allow" Resource: !Sub arn:aws:s3:::${S3Bucket}/${LoadBalancerName}/AWSLogs/${AWS::AccountId}/* Principal: AWS: "582318560864" LoadBalancer: # https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-elasticloadbalancingv2-loadbalancer.html Type: "AWS::ElasticLoadBalancingV2::LoadBalancer" DependsOn: "S3BucketPolicy" DeletionPolicy: "Delete" Properties: IpAddressType: "ipv4" LoadBalancerAttributes: - Key: "access_logs.s3.enabled" Value: "true" - Key: "access_logs.s3.bucket" Value: !Ref "S3Bucket" - Key: "access_logs.s3.prefix" Value: !Ref "LoadBalancerName" - Key: "deletion_protection.enabled" Value: "false" - Key: "idle_timeout.timeout_seconds" Value: "120" - Key: "routing.http2.enabled" Value: "true" Name: !Ref "LoadBalancerName" Scheme: "internet-facing" SecurityGroups: - !Ref "SecurityGroup" # SubnetMappings: # - SubnetMapping Subnets: !Ref "Subnet" # Tags: # - Key: String # Value: String Type: "application" TargetGroup: # https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-elasticloadbalancingv2-targetgroup.html Type: "AWS::ElasticLoadBalancingV2::TargetGroup" DependsOn: "LoadBalancer" DeletionPolicy: "Delete" Properties: HealthCheckIntervalSeconds: "30" HealthCheckPath: "/" HealthCheckPort: "traffic-port" HealthCheckProtocol: "HTTP" HealthCheckTimeoutSeconds: "5" HealthyThresholdCount: "5" Matcher: HttpCode: "200" Name: !Sub tg-${LoadBalancerName} Port: "80" Protocol: "HTTP" # Tags: # - Key: String # Value: String TargetGroupAttributes: - Key: "deregistration_delay.timeout_seconds" Value: "300" Targets: - # AvailabilityZone: String Id: !Ref EC2Instance # Port: Integer TargetType: "instance" UnhealthyThresholdCount: "2" VpcId: !Ref "VpcId" Listener: # https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-elasticloadbalancingv2-listener.html Type: "AWS::ElasticLoadBalancingV2::Listener" DependsOn: "TargetGroup" DeletionPolicy: "Delete" Properties: # Certificates: # - CertificateArn: String DefaultActions: - # AuthenticateCognitoConfig: AuthenticateCognitoConfig # AuthenticateOidcConfig: AuthenticateOidcConfig # FixedResponseConfig: FixedResponseConfig Order: "1" # RedirectConfig: RedirectConfig TargetGroupArn: !Ref TargetGroup Type: "forward" LoadBalancerArn: !Ref "LoadBalancer" Port: "80" Protocol: "HTTP" # SslPolicy: String
長々書いていますが、以下のような定義をしています。
- Paramter
- ALBを配置するVPC
- ALBを配置するSubnet
- ALB名
- ALB配下となるEC2
- Resource
参照されるテンプレートはS3にアップロードする必要があります。今回は s3://cloudformation-template-zunpero/alb/template-alb.yml
というS3パスにアップロードしています。
実際に作成するALBを定義したテンプレートを作成
上記でS3上にアップロードしたテンプレートファイルを参照しつつ、Parameterとした項目に値を設定するテンプレートを作成します。テンプレートのResourceTypeは AWS::CloudFormation::Stack
というものを利用します。
CloudFormation - AWS::CloudFormation::Stack
各パラメータについは上記の公式を見てもらうとして、1つ目のALB用。
sample1.yml
AWSTemplateFormatVersion: "2010-09-09" Description: "sample1" Resources: sample1: Type: AWS::CloudFormation::Stack DeletionPolicy: "Delete" Properties: # NotificationARNs: # - String Parameters: VpcId: "vpc-aaaaaaaa" Subnet: "subnet-11111111,subnet-22222222" LoadBalancerName: "sample1" EC2Instance: "i-000000000000000" # Tags: # - Resource Tag TemplateURL: "https://s3.amazonaws.com/cloudformation-template-zunpero/alb/template-alb.yml" # TimeoutInMinutes: Integer
2つ目のALB用。
sample2.yml
AWSTemplateFormatVersion: "2010-09-09" Description: "sample2" Resources: sample1: Type: AWS::CloudFormation::Stack DeletionPolicy: "Delete" Properties: # NotificationARNs: # - String Parameters: VpcId: "vpc-bbbbbbbb" Subnet: "subnet-33333333,subnet-44444444" LoadBalancerName: "sample2" EC2Instance: "i-111111111111111" # Tags: # - Resource Tag TemplateURL: "https://s3.amazonaws.com/cloudformation-template-zunpero/alb/template-alb.yml" # TimeoutInMinutes: Integer
作成/更新
用意した sample1.yml
と sample2.yml
のCloudFormationを実行すれば、AWSリソースが作成されます。
S3上にアップロードしたテンプレートファイルを更新して、CloudFormationスタックを更新すれば、当然AWSリソースが更新されることになります。困る点としては、 Change Sets
が全く利用できないという点ですね。。。参照側、被参照側のどちらのテンプレートを更新したとしても、Change Setsで変更点を表示してくれないです。