0
1

CloudFormationでECSクラスターとCodePipelineを設定する

Posted at

作成されるリソース

  • 基本的なネットワーク
  • ECSクラスター(EC2 on ECS)
  • CodePipeline
    • CodeCommit
    • CodeBuild BuildProject
    • (ECS Deploy)

※CodePipelineのデプロイステージは未検証のためコメントアウト

テンプレート

ECSクラスターの作成は以下の公式ドキュメントを参考にしています。

AWSTemplateFormatVersion: 2010-09-09
Parameters:
  VPCCidrBlock:
    Description: The primary IPv4 CIDR block for the VPC.
    Type: String
    MinLength: '9'
    MaxLength: '18'
    Default: 10.0.0.0/16
    AllowedPattern: '(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})'

  SubnetCidrBits:
    Description: Subnet CidrBits, alowed bitween 8 and 30.
    Type: String
    MinLength: '1'
    MaxLength: '2'
    Default: '24'
    AllowedPattern: '(\d{1,2})'

  PJPrefix:
    Type: String
    Default: ecs

  ECSAMI:
    Description: The Amazon Machine Image ID used for the cluster
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ecs/optimized-ami/amazon-linux-2/recommended/image_id

  CodeCommitSourceBucket:
    Type: String
    Default: example-bucket

  CodeCommitSourceObjectKey:
    Type: String
    Default: example-key.zip

Mappings:
  CidrBits:
    '30':
      bit: 2
    '29':
      bit: 3
    '28':
      bit: 4
    '27':
      bit: 5
    '26':
      bit: 6
    '25':
      bit: 7
    '24':
      bit: 8
    '23':
      bit: 9
    '22':
      bit: 10
    '21':
      bit: 11
    '20':
      bit: 12
    '19':
      bit: 13
    '18':
      bit: 14
    '17':
      bit: 15
    '16':
      bit: 16
    '15':
      bit: 17
    '14':
      bit: 18
    '13':
      bit: 19
    '12':
      bit: 20
    '11':
      bit: 21
    '10':
      bit: 22
    '9':
      bit: 23
    '8':
      bit: 24

Resources:
  VPC:
    Type: 'AWS::EC2::VPC'
    Properties:
      EnableDnsSupport: 'true'
      EnableDnsHostnames: 'true'
      CidrBlock: !Ref VPCCidrBlock
      Tags:
        - Key: Name
          Value: !Sub '${PJPrefix}-VPC'
  PublicSubnet1:
    Type: 'AWS::EC2::Subnet'
    Properties:
      AvailabilityZone: !Select
        - 0
        - 'Fn::GetAZs': !Ref 'AWS::Region'
      CidrBlock:
        Fn::Select:
          - 0
          - Fn::Cidr:
            - Fn::GetAtt:
              - VPC
              - CidrBlock
            - 4
            - Fn::FindInMap:
              - CidrBits
              - !Ref SubnetCidrBits
              - bit
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub '${PJPrefix}-PublicSubnet1'
      VpcId: !Ref VPC
  PublicSubnet2:
    Type: 'AWS::EC2::Subnet'
    Properties:
      CidrBlock:
        Fn::Select:
          - 1
          - Fn::Cidr:
            - Fn::GetAtt:
              - VPC
              - CidrBlock
            - 4
            - Fn::FindInMap:
              - CidrBits
              - !Ref SubnetCidrBits
              - bit
      AvailabilityZone: !Select
        - 1
        - 'Fn::GetAZs': !Ref 'AWS::Region'
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub '${PJPrefix}-PublicSubnet2'
      VpcId: !Ref VPC
  PrivateSubnet1:
    Type: 'AWS::EC2::Subnet'
    Properties:
      CidrBlock:
        Fn::Select:
          - 2
          - Fn::Cidr:
            - Fn::GetAtt:
              - VPC
              - CidrBlock
            - 4
            - Fn::FindInMap:
              - CidrBits
              - !Ref SubnetCidrBits
              - bit
      AvailabilityZone: !Select
        - 0
        - 'Fn::GetAZs': !Ref 'AWS::Region'
      Tags:
        - Key: Name
          Value: !Sub '${PJPrefix}-PrivateSubnet1'
      VpcId: !Ref VPC
  PrivateSubnet2:
    Type: 'AWS::EC2::Subnet'
    Properties:
      CidrBlock:
        Fn::Select:
          - 3
          - Fn::Cidr:
            - Fn::GetAtt:
              - VPC
              - CidrBlock
            - 4
            - Fn::FindInMap:
              - CidrBits
              - !Ref SubnetCidrBits
              - bit
      AvailabilityZone: !Select
        - 1
        - 'Fn::GetAZs': !Ref 'AWS::Region'
      Tags:
        - Key: Name
          Value: !Sub '${PJPrefix}-PrivateSubnet2'
      VpcId: !Ref VPC
  InternetGateway:
    Type: 'AWS::EC2::InternetGateway'
    DependsOn: VPC
    Properties:
      Tags:
        - Key: Name
          Value: InternetGateway
  VPCGatewayAttachment:
    Type: 'AWS::EC2::VPCGatewayAttachment'
    DependsOn:
      - VPC
      - InternetGateway
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway
  PublicRouteTable:
    Type: 'AWS::EC2::RouteTable'
    Properties:
      Tags:
        - Key: Name
          Value: PublicRouteTable
      VpcId: !Ref VPC
  PublicRoute:
    Type: 'AWS::EC2::Route'
    DependsOn: VPCGatewayAttachment
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
  PublicSubnetRouteTableAssociation1:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    DependsOn:
      - PublicSubnet1
      - PublicRouteTable
    Properties:
      SubnetId: !Ref PublicSubnet1
      RouteTableId: !Ref PublicRouteTable
  PublicSubnetRouteTableAssociation2:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    DependsOn:
      - PublicSubnet2
      - PublicRouteTable
    Properties:
      SubnetId: !Ref PublicSubnet2
      RouteTableId: !Ref PublicRouteTable
  ElasticIP1:
    Type: 'AWS::EC2::EIP'
    Properties:
      Domain: vpc
      Tags:
        - Key: Name
          Value: ElasticIP1
  ElasticIP2:
    Type: 'AWS::EC2::EIP'
    Properties:
      Domain: vpc
      Tags:
        - Key: Name
          Value: ElasticIP2
  NATGateway1:
    Type: 'AWS::EC2::NatGateway'
    Properties:
      AllocationId: !GetAtt
        - ElasticIP1
        - AllocationId
      SubnetId: !Ref PublicSubnet1
      Tags:
        - Key: Name
          Value: NATGateway1
  NATGateway2:
    Type: 'AWS::EC2::NatGateway'
    Properties:
      AllocationId: !GetAtt
        - ElasticIP2
        - AllocationId
      SubnetId: !Ref PublicSubnet2
      Tags:
        - Key: Name
          Value: NATGateway2
  PrivateRoutetable1:
    Type: 'AWS::EC2::RouteTable'
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: PrivateRouteTable1
  PrivateRoute1:
    Type: 'AWS::EC2::Route'
    DependsOn:
      - NATGateway1
    Properties:
      RouteTableId: !Ref PrivateRoutetable1
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NATGateway1
  PrivateSubnetRouteTableAssociation1:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      RouteTableId: !Ref PrivateRoutetable1
      SubnetId: !Ref PrivateSubnet1
  PrivateRoutetable2:
    Type: 'AWS::EC2::RouteTable'
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: PrivateRouteTable2
  PrivateRoute2:
    Type: 'AWS::EC2::Route'
    Properties:
      RouteTableId: !Ref PrivateRoutetable2
      DestinationCidrBlock: 0.0.0.0/0
      NatGatewayId: !Ref NATGateway2
  PrivateSubnetRouteTableAssociation2:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      SubnetId: !Ref PrivateSubnet2
      RouteTableId: !Ref PrivateRoutetable2
  PrivateRoutetable3:
    Type: 'AWS::EC2::RouteTable'
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: PrivateRouteTable3

########### Create ECS Resource ############

  ECSCluster:
    Type: AWS::ECS::Cluster
    Properties:
      ClusterSettings:
        - Name: containerInsights
          Value: enabled

  ECSAutoScalingGroup:
    Type: AWS::AutoScaling::AutoScalingGroup
    DependsOn:
      # This is to ensure that the ASG gets deleted first before these
    # resources, when it comes to stack teardown.
      - ECSCluster
      - EC2Role
    Properties:
      VPCZoneIdentifier:
        - !Ref PrivateSubnet1
        - !Ref PrivateSubnet2
      LaunchTemplate:
        LaunchTemplateId: !Ref ContainerInstances
        Version: !GetAtt ContainerInstances.LatestVersionNumber
      MinSize: 2
      MaxSize: 10
      DesiredCapacity: 2
      NewInstancesProtectedFromScaleIn: true
    UpdatePolicy:
      AutoScalingReplacingUpdate:
        WillReplace: "true"

  ContainerInstances:
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateName: "asg-launch-template"
      LaunchTemplateData:
        ImageId: !Ref ECSAMI
        InstanceType: t3.medium
        IamInstanceProfile:
          Name: !Ref EC2InstanceProfile
        SecurityGroupIds:
          - !Ref ContainerHostSecurityGroup
        # This injected configuration file is how the EC2 instance
      # knows which ECS cluster on your AWS account it should be joining
        UserData:
          Fn::Base64: !Sub |
           #!/bin/bash -xe
            echo ECS_CLUSTER=${ECSCluster} >> /etc/ecs/ecs.config
            yum install -y aws-cfn-bootstrap
            /opt/aws/bin/cfn-init -v --stack ${AWS::StackId} --resource ContainerInstances --configsets full_install --region ${AWS::Region} &
         # Disable IMDSv1, and require IMDSv2
        MetadataOptions:
          HttpEndpoint: enabled
          HttpTokens: required
        TagSpecifications:
          - ResourceType: instance
            Tags:
            - Key: Name
              Value: ECS Cluster
  EC2InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: /
      Roles: 
      - !Ref EC2Role

  CapacityProvider:
    Type: AWS::ECS::CapacityProvider
    Properties:
      AutoScalingGroupProvider:
        AutoScalingGroupArn: !Ref ECSAutoScalingGroup
        ManagedScaling:
          InstanceWarmupPeriod: 60
          MinimumScalingStepSize: 1
          MaximumScalingStepSize: 100
          Status: ENABLED
          # Percentage of cluster reservation to try to maintain
          TargetCapacity: 100
        ManagedTerminationProtection: ENABLED
   # Create a cluster capacity provider assocation so that the cluster
  # will use the capacity provider
  CapacityProviderAssociation:
    Type: AWS::ECS::ClusterCapacityProviderAssociations
    Properties:
      CapacityProviders:
        - !Ref CapacityProvider
      Cluster: !Ref ECSCluster
      DefaultCapacityProviderStrategy:
        - Base: 0
          CapacityProvider: !Ref CapacityProvider
          Weight: 1
  # A security group for the EC2 hosts that will run the containers.
  # This can be used to limit incoming traffic to or outgoing traffic
  # from the container's host EC2 instance.
  ContainerHostSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Access to the EC2 hosts that run containers
      VpcId: !Ref VPC
  # Role for the EC2 hosts. This allows the ECS agent on the EC2 hosts
  # to communciate with the ECS control plane, as well as download the docker
  # images from ECR to run on your host.
  EC2Role:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      ManagedPolicyArns:
      # See reference: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/security-iam-awsmanpol.html#security-iam-awsmanpol-AmazonEC2ContainerServiceforEC2Role
        - arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role
      # This managed policy allows us to connect to the instance using SSM
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
  # This is a role which is used within Fargate to allow the Fargate agent
  # to download images, and upload logs.
  ECSTaskExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ecs-tasks.amazonaws.com
            Action:
              - sts:AssumeRole
            Condition:
              ArnLike:
                aws:SourceArn: !Sub arn:${AWS::Partition}:ecs:${AWS::Region}:${AWS::AccountId}:*
              StringEquals:
                aws:SourceAccount: AWS::AccountId
      Path: /
      # This role enables all features of ECS. See reference:
    # https://docs.aws.amazon.com/AmazonECS/latest/developerguide/security-iam-awsmanpol.html#security-iam-awsmanpol-AmazonECSTaskExecutionRolePolicy
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy



  CodeCommit:
    Type: AWS::CodeCommit::Repository
    Properties:
      RepositoryName: app
      RepositoryDescription: This is a repository for my project.
      Code:
        BranchName: main
        S3: 
          Bucket: !Ref CodeCommitSourceBucket
          Key: !Ref CodeCommitSourceObjectKey

  CodeBuild:         
    Type: AWS::CodeBuild::Project
    Properties:
      Artifacts: 
        Type: CODEPIPELINE
      Environment: 
        ComputeType: BUILD_GENERAL1_SMALL
        Image: aws/codebuild/amazonlinux2-x86_64-standard:4.0
        PrivilegedMode: true
        Type: LINUX_CONTAINER
      LogsConfig: 
        CloudWatchLogs:
          Status: ENABLED
      ServiceRole: !GetAtt CodeBuildRole.Arn
      Source: 
        Type: CODEPIPELINE
      TimeoutInMinutes: 5

  CodeBuildRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument: 
        Version: "2012-10-17"
        Statement: 
          - Effect: "Allow"
            Principal: 
              Service: 
                - "codebuild.amazonaws.com"
            Action: 
              - "sts:AssumeRole"
      ManagedPolicyArns: 
        - arn:aws:iam::aws:policy/AWSCodeBuildAdminAccess
        - arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess 
      Path: /

  CodePipeline:
    Type: AWS::CodePipeline::Pipeline
    Properties:
      ArtifactStore: 
        Location: !Ref S3Bucket
        Type: S3
      RoleArn: !GetAtt CodePipelineRole.Arn
      Stages: 
        - Name: Source
          Actions: 
            - ActionTypeId: 
                Category: Source
                Owner: AWS
                Provider: CodeCommit
                Version: 1
              Name: Source
              Configuration: 
                BranchName: main
                RepositoryName: !GetAtt CodeCommit.Name
              OutputArtifacts: 
                - Name: sourceArtifact
              RunOrder: 1
        - Name: Build
          Actions: 
            - ActionTypeId: 
                Category: Build
                Owner: AWS
                Provider: CodeBuild
                Version: 1
              Name: Build
              Configuration:
                ProjectName: !Ref CodeBuild
              InputArtifacts: 
                - Name: sourceArtifact
              OutputArtifacts: 
                - Name: buildArtifact
              RunOrder: 2
        # - Name: Deploy
        #   Actions: 
        #     - ActionTypeId: 
        #         Category: Deploy
        #         Owner: AWS
        #         Provider: Amazon ECS
        #         Version: 1
        #       Name: Build
        #       Configuration:
        #         ClusterName: !Ref ECSCluster
        #         ServiceName: !Ref ECSCluster
        #       InputArtifacts: 
        #         - Name: buildArtifact
        #       RunOrder: 3

  S3Bucket:
    Type: 'AWS::S3::Bucket'

  CodePipelineRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument: 
        Version: "2012-10-17"
        Statement: 
          - Effect: "Allow"
            Principal: 
              Service: 
                - "codepipeline.amazonaws.com"
            Action: 
              - "sts:AssumeRole"

      Path: /
  RolePolicies: 
    Type: "AWS::IAM::Policy"
    Properties: 
      PolicyName: "CodePipelinePolicy"
      Roles:
        - !Ref CodePipelineRole
      PolicyDocument: 
        Version: "2012-10-17"
        Statement: 
        - Effect: "Allow"
          Action: 
          - "codecommit:CancelUploadArchive"
          - "codecommit:GetBranch"
          - "codecommit:GetCommit"
          - "codecommit:GetUploadArchiveStatus"
          - "codecommit:UploadArchive"
          Resource: "*"
        - Effect: "Allow"
          Action: 
          - "codedeploy:CreateDeployment"
          - "codedeploy:GetApplicationRevision"
          - "codedeploy:GetDeployment"
          - "codedeploy:GetDeploymentConfig"
          - "codedeploy:RegisterApplicationRevision"
          Resource: "*"
        - Effect: "Allow"
          Action: 
          - "codebuild:BatchGetBuilds"
          - "codebuild:StartBuild"
          Resource: "*"
        - Effect: "Allow"
          Action: 
          - "devicefarm:ListProjects"
          - "devicefarm:ListDevicePools"
          - "devicefarm:GetRun"
          - "devicefarm:GetUpload"
          - "devicefarm:CreateUpload"
          - "devicefarm:ScheduleRun"
          Resource: "*"
        - Effect: "Allow"
          Action: 
          - "lambda:InvokeFunction"
          - "lambda:ListFunctions"
          Resource: "*"
        - Effect: "Allow"
          Action: 
          - "iam:PassRole"
          Resource: "*"
        - Effect: "Allow"
          Action: 
          - "elasticbeanstalk:*"
          - "ec2:*"
          - "elasticloadbalancing:*"
          - "autoscaling:*"
          - "cloudwatch:*"
          - "s3:*"
          - "sns:*"
          - "cloudformation:*"
          - "rds:*"
          - "sqs:*"
          - "ecs:*"
          Resource: "*"
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1