AWS CloudFormation

CloudFormationとは

設定ファイルからAWS環境を構築するサービス
AWS Cliからawsコマンドが記載されたシェルを実行してインフラ環境を構築できる
=Infrastructure as Code
=IaC
コンソールから実行する方法とAWS Cliからシェルで実行する方法(後述)がある

テンプレート

CloudFormationで利用するjsonまたはymlファイルで定義する設定ファイル
・AWSTemplateFormatVersion
・Description
・Parameters
・Resources
・Outputs

スタック

テンプレートから作成したリソース
テンプレート内で複数のAWSサービスを定義した場合でも1つのスタックとしてまとめられる

VPC作成

AWSコマンド

aws cloudformation deploy
--stack-name aws_test-vpc
--template-file template/vpc.yml
--no-execute-changeset
--parameter-overrides
SystemName=aws_test

vpc.yml

vpc.yml

---
AWSTemplateFormatVersion: '2010-09-09'
Description: VPC Network Template

# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------#
Parameters:
 SystemName:
 Type: String
 Description: Your System Name.

Resources:
# ------------------------------------------------------------#
# VPC
# ------------------------------------------------------------#
 VPC:
 Type: AWS::EC2::VPC
 Properties:
  CidrBlock: 10.0.0.0/16
  EnableDnsSupport: 'true'
  EnableDnsHostnames: 'true'
  InstanceTenancy: default
  Tags:
  - Key: Name
  Value: !Sub ${SystemName}-VPC

# ------------------------------------------------------------#
# InternetGateway
# ------------------------------------------------------------#
 InternetGateway:
 Type: AWS::EC2::InternetGateway
 Properties:
  Tags:
  - Key: Name
  Value: !Sub ${SystemName}-IGW
 AttachGateway:
 Type: AWS::EC2::VPCGatewayAttachment
 Properties:
  VpcId: !Ref VPC
  InternetGatewayId: !Ref InternetGateway

# ------------------------------------------------------------#
# Subnet
# ------------------------------------------------------------#
 PublicSubnet1a:
 Type: AWS::EC2::Subnet
 DependsOn: AttachGateway
 Properties:
  AvailabilityZone: ap-northeast-1a
  CidrBlock: 10.0.0.0/24
  MapPublicIpOnLaunch: 'true'
  Tags:
  - Key: Name
  Value: !Sub ${SystemName}-Public-Subnet-1a
  VpcId: !Ref VPC
 PublicSubnet1c:
 Type: AWS::EC2::Subnet
 DependsOn: AttachGateway
 Properties:
  AvailabilityZone: ap-northeast-1c
  CidrBlock: 10.0.1.0/24
  MapPublicIpOnLaunch: 'true'
  Tags:
  - Key: Name
  Value: !Sub ${SystemName}-Public-Subnet-1c
  VpcId: !Ref VPC
 PrivateSubnet1a:
 Type: AWS::EC2::Subnet
 DependsOn: AttachGateway
 Properties:
  AvailabilityZone: ap-northeast-1a
  CidrBlock: 10.0.10.0/24
  MapPublicIpOnLaunch: 'false'
  Tags:
  - Key: Name
  Value: !Sub ${SystemName}-Private-Subnet-1a
  VpcId: !Ref VPC
 PrivateSubnet1c:
 Type: AWS::EC2::Subnet
 DependsOn: AttachGateway
 Properties:
  AvailabilityZone: ap-northeast-1c
  CidrBlock: 10.0.11.0/24
  MapPublicIpOnLaunch: 'false'
  Tags:
  - Key: Name
  Value: !Sub ${SystemName}-Private-Subnet-1c
  VpcId: !Ref VPC
 PrivateSubnet2a:
 Type: AWS::EC2::Subnet
 DependsOn: AttachGateway
 Properties:
  AvailabilityZone: ap-northeast-1a
  CidrBlock: 10.0.20.0/24
  MapPublicIpOnLaunch: 'false'
  Tags:
  - Key: Name
  Value: !Sub ${SystemName}-Private-Subnet-2c
  VpcId: !Ref VPC
 PrivateSubnet2c:
 Type: AWS::EC2::Subnet
 DependsOn: AttachGateway
 Properties:
  AvailabilityZone: ap-northeast-1c
  CidrBlock: 10.0.21.0/24
  MapPublicIpOnLaunch: 'false'
  Tags:
  - Key: Name
  Value: !Sub ${SystemName}-Private-Subnet-2c
  VpcId: !Ref VPC

# ------------------------------------------------------------#
# ElasticIP, NatGateway
# ------------------------------------------------------------#
 EIP1a:
 Type: AWS::EC2::EIP
 Properties:
  Domain: vpc
 NAT1a:
 Type: AWS::EC2::NatGateway
 Properties:
  AllocationId:
  Fn::GetAtt:
   - EIP1a
   - AllocationId
  SubnetId: !Ref PublicSubnet1a
  Tags:
  - Key: Name
   Value: !Sub ${SystemName}-ngw-1a
 EIP1c:
 Type: AWS::EC2::EIP
 Properties:
  Domain: vpc
 NAT1c:
 Type: AWS::EC2::NatGateway
 Properties:
  AllocationId:
  Fn::GetAtt:
   - EIP1c
   - AllocationId
  SubnetId: !Ref PublicSubnet1c
  Tags:
  - Key: Name
   Value: !Sub ${SystemName}-ngw-1c

# ------------------------------------------------------------#
# RouteTable
# ------------------------------------------------------------#
 PublicRouteTable:
 Type: AWS::EC2::RouteTable
 DependsOn: AttachGateway
 Properties:
  VpcId: !Ref VPC
  Tags:
  - Key: Name
  Value: !Sub ${SystemName}-Public-RT
 PublicRoute:
 Type: AWS::EC2::Route
 DependsOn: AttachGateway
 Properties:
  RouteTableId: !Ref PublicRouteTable
  DestinationCidrBlock: 0.0.0.0/0
  GatewayId: !Ref InternetGateway
 PrivateRouteTable1a:
 Type: AWS::EC2::RouteTable
 DependsOn: NAT1a
 Properties:
  VpcId: !Ref VPC
  Tags:
  - Key: Name
  Value: !Sub ${SystemName}-Private-RT1a
 PrivateRoute1a:
 Type: AWS::EC2::Route
 DependsOn: NAT1a
 Properties:
  RouteTableId: !Ref PrivateRouteTable1a
  DestinationCidrBlock: 0.0.0.0/0
  NatGatewayId: !Ref NAT1a
 PrivateRouteTable1c:
 Type: AWS::EC2::RouteTable
 DependsOn: NAT1c
 Properties:
  VpcId: !Ref VPC
  Tags:
  - Key: Name
  Value: !Sub ${SystemName}-Private-RT1c
 PrivateRoute1c:
 Type: AWS::EC2::Route
 DependsOn: NAT1c
 Properties:
  RouteTableId: !Ref PrivateRouteTable1c
  DestinationCidrBlock: 0.0.0.0/0
  NatGatewayId: !Ref NAT1c
 PrivateRouteTable2:
 Type: AWS::EC2::RouteTable
 Properties:
  VpcId: !Ref VPC
  Tags:
  - Key: Name
  Value: !Sub ${SystemName}-Private-RT2

 PublicSubnet1aRouteTableAssociation:
 Type: AWS::EC2::SubnetRouteTableAssociation
 Properties:
  SubnetId: !Ref PublicSubnet1a
  RouteTableId: !Ref PublicRouteTable
 PublicSubnet1cRouteTableAssociation:
 Type: AWS::EC2::SubnetRouteTableAssociation
 Properties:
  SubnetId: !Ref PublicSubnet1c
  RouteTableId: !Ref PublicRouteTable
 PrivateSubnet1aRouteTableAssociation:
 Type: AWS::EC2::SubnetRouteTableAssociation
 Properties:
  SubnetId: !Ref PrivateSubnet1a
  RouteTableId: !Ref PrivateRouteTable1a
 PrivateSubnet1cRouteTableAssociation:
 Type: AWS::EC2::SubnetRouteTableAssociation
 Properties:
  SubnetId: !Ref PrivateSubnet1c
  RouteTableId: !Ref PrivateRouteTable1c
 PrivateSubnet2aRouteTableAssociation:
 Type: AWS::EC2::SubnetRouteTableAssociation
 Properties:
  SubnetId: !Ref PrivateSubnet2a
  RouteTableId: !Ref PrivateRouteTable2
 PrivateSubnet2cRouteTableAssociation:
 Type: AWS::EC2::SubnetRouteTableAssociation
 Properties:
  SubnetId: !Ref PrivateSubnet2c
  RouteTableId: !Ref PrivateRouteTable2

# ------------------------------------------------------------#
# Outputs
# ------------------------------------------------------------#
Outputs:
 VPC:
 Value: !Ref VPC
 Export:
  Name: !Sub ${SystemName}-VPC
 PublicSubnet1a:
 Value: !Ref PublicSubnet1a
 Export:
  Name: !Sub ${SystemName}-PublicSubnet1a
 PublicSubnet1c:
 Value: !Ref PublicSubnet1c
 Export:
  Name: !Sub ${SystemName}-PublicSubnet1c
 PrivateSubnet1a:
 Value: !Ref PrivateSubnet1a
 Export:
  Name: !Sub ${SystemName}-PrivateSubnet1a
 PrivateSubnet1c:
 Value: !Ref PrivateSubnet1c
 Export:
  Name: !Sub ${SystemName}-PrivateSubnet1c
 PrivateSubnet2a:
 Value: !Ref PrivateSubnet2a
 Export:
  Name: !Sub ${SystemName}-PrivateSubnet2a
 PrivateSubnet2c:
 Value: !Ref PrivateSubnet2c
 Export:
  Name: !Sub ${SystemName}-PrivateSubnet2c

セキュリティグループ作成

AWSコマンド

aws cloudformation deploy
--stack-name handson-securitygroup
--template-file template/securitygroup.yml
--no-execute-changeset
--parameter-overrides
SystemName=handson
AdminIpaddress=255.255.255.255

securitygroup.yml

securitygroup.yml

---
AWSTemplateFormatVersion: "2010-09-09"
Description: "Security Groups"

# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------#
Parameters:
 SystemName:
 Type: String
 Description: Your System Name.

 AdminIpaddress:
 Type: String
 Description: Your Administrator IP Address.

Resources:
# ------------------------------------------------------------#
# Security Groups
# ------------------------------------------------------------#
#-- app-alb-sg --#
 appAlbSg:
 Type: AWS::EC2::SecurityGroup
 Properties:
  GroupName: !Sub "${SystemName}-app-alb-sg"
  GroupDescription: !Sub "${SystemName}-app-alb-sg"
  VpcId:
  Fn::ImportValue:
   !Sub "${SystemName}-VPC"
  SecurityGroupIngress:
  - IpProtocol: tcp
   FromPort: 80
   ToPort: 80
   CidrIp: 0.0.0.0/0
   Description: all
  Tags:
  - Key: Name
   Value: !Sub "${SystemName}-app-alb-sg"
#-- admin-alb-sg --#
 adminAlbSg:
 Type: AWS::EC2::SecurityGroup
 Properties:
  GroupName: !Sub "${SystemName}-admin-alb-sg"
  GroupDescription: !Sub "${SystemName}-admin-alb-sg"
  VpcId:
  Fn::ImportValue:
   !Sub "${SystemName}-VPC"
  SecurityGroupIngress:
  - IpProtocol: tcp
   FromPort: 80
   ToPort: 80
   CidrIp: !Ref AdminIpaddress
   Description: AdminIpaddress
  Tags:
  - Key: Name
   Value: !Sub "${SystemName}-admin-alb-sg"
#-- app-fargate-sg --#
 appFargateSg:
 Type: AWS::EC2::SecurityGroup
 Properties:
  GroupName: !Sub "${SystemName}-app-fargate-sg"
  GroupDescription: !Sub "${SystemName}-app-fargate-sg"
  VpcId:
  Fn::ImportValue:
   !Sub "${SystemName}-VPC"
  SecurityGroupIngress:
  - IpProtocol: tcp
   FromPort: 80
   ToPort: 80
   SourceSecurityGroupId: !Ref appAlbSg
   Description: !Sub "${SystemName}-app-alb-sg"
  Tags:
  - Key: Name
   Value: !Sub "${SystemName}-app-fargate-sg"
#-- admin-fargate-sg --#
 adminFargateSg:
 Type: AWS::EC2::SecurityGroup
 Properties:
  GroupName: !Sub "${SystemName}-admin-fargate-sg"
  GroupDescription: !Sub "${SystemName}-admin-fargate-sg"
  VpcId:
  Fn::ImportValue:
   !Sub "${SystemName}-VPC"
  SecurityGroupIngress:
  - IpProtocol: tcp
   FromPort: 80
   ToPort: 80
   SourceSecurityGroupId: !Ref adminAlbSg
   Description: !Sub "${SystemName}-admin-alb-sg"
  Tags:
  - Key: Name
   Value: !Sub "${SystemName}-admin-fargate-sg"
#-- db-sg --#
 dbSg:
 Type: AWS::EC2::SecurityGroup
 Properties:
  GroupName: !Sub "${SystemName}-db-sg"
  GroupDescription: !Sub "${SystemName}-db-sg"
  VpcId:
  Fn::ImportValue:
   !Sub "${SystemName}-VPC"
  SecurityGroupIngress:
  - IpProtocol: tcp
   FromPort: 3306
   ToPort: 3306
   SourceSecurityGroupId: !Ref appFargateSg
   Description: !Sub "${SystemName}-app-fargate-sg"
  - IpProtocol: tcp
   FromPort: 3306
   ToPort: 3306
   SourceSecurityGroupId: !Ref adminFargateSg
   Description: !Sub "${SystemName}-admin-fargate-sg"
  Tags:
  - Key: Name
   Value: !Sub "${SystemName}-db-sg"
# ------------------------------------------------------------#
# Outputs
# ------------------------------------------------------------#
Outputs:
 appAlbSg:
 Value: !Ref appAlbSg
 Export:
  Name: !Sub "${SystemName}-app-alb-sg"
 adminAlbSg:
 Value: !Ref adminAlbSg
 Export:
  Name: !Sub "${SystemName}-admin-alb-sg"
 appFargateSg:
 Value: !Ref appFargateSg
 Export:
  Name: !Sub "${SystemName}-app-fargate-sg"
 adminFargateSg:
 Value: !Ref adminFargateSg
 Export:
  Name: !Sub "${SystemName}-admin-fargate-sg"
 dbSg:
 Value: !Ref dbSg
 Export:
  Name: !Sub "${SystemName}-db-sg"

CodeCommit作成

AWSコマンド

aws cloudformation deploy
--stack-name handson-codecommit
--template-file template/codecommit.yml
--no-execute-changeset
--parameter-overrides
SystemName=handson

codecommit.yml

codecommit.yml

---
AWSTemplateFormatVersion: '2010-09-09'
Description: CodeCommit

# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------#
Parameters:
 SystemName:
 Type: String
 Description: Your System Name.

Resources:
# ------------------------------------------------------------#
# CodeCommit
# ------------------------------------------------------------#
 codeCommitPhpApp:
 Type: AWS::CodeCommit::Repository
 Properties:
  RepositoryName: !Sub ${SystemName}-codecommit-php-app
  RepositoryDescription: !Sub ${SystemName}-codecommit-php-app

# ------------------------------------------------------------#
# Outputs
# ------------------------------------------------------------#
Outputs:
 codeCommitPhpApp:
 Value: !Ref codeCommitPhpApp
 Export:
  Name: !Sub ${SystemName}-codecommit-php-app
 codeCommitPhpAppName:
 Value: !GetAtt codeCommitPhpApp.Name
 Export:
  Name: !Sub ${SystemName}-codecommit-php-app-name
 codeCommitPhpAppArn:
 Value: !GetAtt codeCommitPhpApp.Arn
 Export:
  Name: !Sub ${SystemName}-codecommit-php-app-arn

ECR作成

AWSコマンド

aws cloudformation deploy
--stack-name handson-ecr
--template-file template/ecr.yml
--no-execute-changeset
--parameter-overrides
SystemName=handson

ecr.yml

ecr.yml

 ---
 AWSTemplateFormatVersion: '2010-09-09'
 Description: ECR

 # ------------------------------------------------------------#
 # Parameters
 # ------------------------------------------------------------#
 Parameters:
 SystemName:
  Type: String
  Description: Your System Name.

 Resources:
 # ------------------------------------------------------------#
 # ECR
 # ------------------------------------------------------------#
 ecrPhpApp:
  Type: AWS::ECR::Repository
  Properties:
  RepositoryName: !Sub ${SystemName}-ecr-php-app
  LifecyclePolicy:
   LifecyclePolicyText: |
   {
    "rules": [
    {
     "rulePriority": 1,
     "description": "Delete more than 20 images",
     "selection": {
     "tagStatus": "any",
     "countType": "imageCountMoreThan",
     "countNumber": 20
     },
     "action": {
     "type": "expire"
     }
    }
    ]
   }

 # ------------------------------------------------------------#
 # Outputs
 # ------------------------------------------------------------#
 Outputs:
 ecrPhpApp:
  Value: !GetAtt ecrPhpApp.Arn
  Export:
  Name: !Sub ${SystemName}-ecr-php-app

RDS作成

事前準備:データベース接続パスワード登録

※コンソールで実行
AWS Systems Manager > パラメータストア > パラメータを作成
名前:DBPassword
値:XXXXXXX

AWSコマンド

aws cloudformation deploy
--stack-name handson-rds
--template-file template/rds.yml
--no-execute-changeset
--parameter-overrides
SystemName=handson

rds.yml

rds.yml


AWSTemplateFormatVersion: ‘2010-09-09’
Description: RDS

# ————————————————————#
# Parameters
# ————————————————————#
Parameters:
SystemName:
Type: String
Description: Your System Name.

Resources:
# ————————————————————#
# ParameterGroup
# ————————————————————#
paramGroup:
Type: AWS::RDS::DBParameterGroup
Properties:
Description: !Sub ${SystemName} rds parameter group
Family: mysql8.0
Tags:
– Key: Name
Value: !Sub ${SystemName}-db-parametergroup

# ————————————————————#
# DBSubnetGroup
# ————————————————————#
dbSubnetGroup:
Type: AWS::RDS::DBSubnetGroup
Properties:
DBSubnetGroupName: !Sub ${SystemName}-db-subnetgroup
DBSubnetGroupDescription: !Sub ${SystemName}-db-subnetgroup
SubnetIds:
– Fn::ImportValue: !Sub ${SystemName}-PrivateSubnet2a
– Fn::ImportValue: !Sub ${SystemName}-PrivateSubnet2c
Tags:
– Key: Name
Value: !Sub ${SystemName}-db-subnetgroup

# ————————————————————#
# DBInstance
# ————————————————————#
db:
Type: AWS::RDS::DBInstance
Properties:
DBName: !Sub ${SystemName}db
AllocatedStorage: ‘5’
DBInstanceClass: db.t3.micro
DBInstanceIdentifier: !Sub ${SystemName}db
DBSubnetGroupName: !Ref dbSubnetGroup
DBParameterGroupName: !Ref paramGroup
Engine: MySQL
EngineVersion: 8.0.16
MasterUsername: MasterUser
MasterUserPassword: ‘{{resolve:ssm-secure:DBPassword:1}}’
MultiAZ: true
VPCSecurityGroups:
– Fn::ImportValue: !Sub ${SystemName}-db-sg

# ————————————————————#
# Outputs
# ————————————————————#
Outputs:
paramGroup:
Value: !Ref paramGroup
Export:
Name: !Sub ${SystemName}-db-parametergroup
dbSubnetGroup:
Value: !Ref dbSubnetGroup
Export:
Name: !Sub ${SystemName}-db-subnetgroup
db:
Value: !Ref db
Export:
Name: !Sub ${SystemName}-db
dbUser:
Value: MasterUser
Export:
Name: !Sub ${SystemName}-db-user
dbEndpoint:
Value: !GetAtt db.Endpoint.Address
Export:
Name: !Sub ${SystemName}-db-endpoint
dbPort:
Value: !GetAtt db.Endpoint.Port
Export:
Name: !Sub ${SystemName}-db-port

ECS作成

AWSコマンド

aws cloudformation deploy
--stack-name handson-ecs-cluster
--template-file template/ecs-cluster.yml
--no-execute-changeset
--parameter-overrides
SystemName=handson

ecs-cluster.yml

ecs-cluster.yml

---
AWSTemplateFormatVersion: '2010-09-09'
Description: ECS Cluster

# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------#
Parameters:
 SystemName:
 Type: String
 Description: Your System Name.

Resources:
# ------------------------------------------------------------#
# ECS Cluster
# ------------------------------------------------------------#
 ecsCluster:
 Type: AWS::ECS::Cluster
 Properties:
  ClusterName: !Sub ${SystemName}-cluster
  ClusterSettings:
  - Name: containerInsights
  Value: enabled
 logGroup:
 Type: AWS::Logs::LogGroup
 Properties:
  LogGroupName: !Sub /${SystemName}-loggroup

# ------------------------------------------------------------#
# Outputs
# ------------------------------------------------------------#
Outputs:
 ecsCluster:
 Value: !Ref ecsCluster
 Export:
  Name: !Sub ${SystemName}-cluster
 logGroup:
 Value: !Ref logGroup
 Export:
  Name: !Sub ${SystemName}-loggroup

phpMyAdminコンテナ作成

AWSコマンド

aws cloudformation deploy
--stack-name handson-admin-alb-fargate
--template-file template/admin-alb-fargate.yml
--no-execute-changeset
--capabilities CAPABILITY_IAM
--parameter-overrides
SystemName=handson

admin-alb-fargate.yml

admin-alb-fargate.yml

---
AWSTemplateFormatVersion: '2010-09-09'
Description: Admin alb fargate

# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------#
Parameters:
 SystemName:
 Type: String
 Description: Your System Name.

Resources:
# ------------------------------------------------------------#
# ALB
# ------------------------------------------------------------#
 adminALB:
 Type: AWS::ElasticLoadBalancingV2::LoadBalancer
 Properties:
  Name: !Sub ${SystemName}-admin-alb
  Scheme: internet-facing
  Type: application
  IpAddressType: ipv4
  Subnets:
  - Fn::ImportValue:
   !Sub ${SystemName}-PublicSubnet1a
  - Fn::ImportValue:
   !Sub ${SystemName}-PublicSubnet1c
  SecurityGroups:
  - Fn::ImportValue:
   !Sub ${SystemName}-admin-alb-sg

 adminAlbTg:
 Type: AWS::ElasticLoadBalancingV2::TargetGroup
 Properties:
  Name: !Sub ${SystemName}-admin-alb-tg
  Protocol: HTTP
  Port: 80
  TargetType: ip
  VpcId:
  Fn::ImportValue:
   !Sub ${SystemName}-VPC
  HealthCheckProtocol: HTTP
  HealthCheckPath: /
  HealthCheckPort: traffic-port
  HealthyThresholdCount: 5
  UnhealthyThresholdCount: 2
  HealthCheckTimeoutSeconds: 5
  HealthCheckIntervalSeconds: 30
  Matcher:
  HttpCode: 200
  TargetGroupAttributes:
  - Key: deregistration_delay.timeout_seconds
   Value: 30

 adminListener:
 Type: AWS::ElasticLoadBalancingV2::Listener
 Properties:
  LoadBalancerArn: !Ref adminALB
  Protocol: HTTP
  Port: 80
  DefaultActions:
  - Type: forward
   TargetGroupArn: !Ref adminAlbTg

# ------------------------------------------------------------#
# ECS Task
# ------------------------------------------------------------#
 adminTaskExecutionRole:
 Type: AWS::IAM::Role
 Properties:
  AssumeRolePolicyDocument:
  Version: "2012-10-17"
  Statement:
   - Effect: "Allow"
   Principal:
    Service:
    - "ecs-tasks.amazonaws.com"
   Action:
    - "sts:AssumeRole"
  ManagedPolicyArns:
  - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
  - arn:aws:iam::aws:policy/AmazonSSMReadOnlyAccess

 adminTask:
 Type: AWS::ECS::TaskDefinition
 Properties:
  Family: !Sub ${SystemName}-admin-task
  RequiresCompatibilities:
  - FARGATE
  NetworkMode: awsvpc
  TaskRoleArn: !Ref adminTaskExecutionRole
  ExecutionRoleArn: !Ref adminTaskExecutionRole
  Cpu: 256
  Memory: 512
  ContainerDefinitions:
  - Name: phpmyadmin
   Image: phpmyadmin/phpmyadmin:latest
   PortMappings:
   - Protocol: tcp
    HostPort: 80
    ContainerPort: 80
   LogConfiguration:
   LogDriver: awslogs
   Options:
    awslogs-group:
    Fn::ImportValue:
     !Sub ${SystemName}-loggroup
    awslogs-region: !Sub "${AWS::Region}"
    awslogs-stream-prefix: adminTask
   Environment:
   - Name: PMA_HOST
    Value:
    Fn::ImportValue: !Sub ${SystemName}-db-endpoint
   - Name: PMA_PORT
    Value:
    Fn::ImportValue: !Sub ${SystemName}-db-port

# ------------------------------------------------------------#
# ECS Service
# ------------------------------------------------------------#
 adminService:
 Type: AWS::ECS::Service
 DependsOn: adminALB
 Properties:
  ServiceName: !Sub ${SystemName}-admin-service
  LaunchType: FARGATE
  Cluster:
  Fn::ImportValue:
   !Sub ${SystemName}-cluster
  TaskDefinition: !Ref adminTask
  DesiredCount: 1
  DeploymentConfiguration:
  MinimumHealthyPercent: 50
  MaximumPercent: 200
  LoadBalancers:
  - TargetGroupArn: !Ref adminAlbTg
   ContainerName: phpmyadmin
   ContainerPort: 80
  NetworkConfiguration:
  AwsvpcConfiguration:
   AssignPublicIp: DISABLED
   SecurityGroups:
   - Fn::ImportValue:
    !Sub ${SystemName}-admin-fargate-sg
   Subnets:
   - Fn::ImportValue:
    !Sub ${SystemName}-PrivateSubnet1a
   - Fn::ImportValue:
    !Sub ${SystemName}-PrivateSubnet1c

# ------------------------------------------------------------#
# Outputs
# ------------------------------------------------------------#
Outputs:
 adminALB:
 Value: !Ref adminALB
 Export:
  Name: !Sub ${SystemName}-admin-alb
 adminALBUrl:
 Value:
  Fn::Join:
  - ""
  - - http://
   - Fn::GetAtt: adminALB.DNSName
   - /
 Export:
  Name: !Sub ${SystemName}-admin-alb-url
 adminAlbTg:
 Value: !Ref adminAlbTg
 Export:
  Name: !Sub ${SystemName}-admin-alb-tg
 adminTask:
 Value: !Ref adminTask
 Export:
  Name: !Sub ${SystemName}-admin-task
 adminService:
 Value: !Ref adminService
 Export:
  Name: !Sub ${SystemName}-admin-service

phpアプリ作成

ECRへのpush

AWS接続
aws ecr get-login-password
--region us-east-1 | docker login
--username AWS
--password-stdin 999999999999.dkr.ecr.us-east-1.amazonaws.com

localアプリビルド
docker build -t handson-ecr-php-app .

localアプリタグ設定
docker tag handson-ecr-php-app:latest 999999999999.dkr.ecr.us-east-1.amazonaws.com/handson-ecr-php-app:latest

push
docker push 999999999999.dkr.ecr.us-east-1.amazonaws.com/handson-ecr-php-app:latest

AWSコマンド

aws cloudformation deploy
--stack-name handson-app-alb-fargate
--template-file template/app-alb-fargate.yml
--no-execute-changeset
--capabilities CAPABILITY_IAM
--parameter-overrides
SystemName=handson
ImageUri=999999999999.dkr.ecr.ap-northeast-1.amazonaws.com/handson-ecr-php-app

app-alb-fargate.yml

app-alb-fargate.yml

---
AWSTemplateFormatVersion: '2010-09-09'
Description: App alb fargate

# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------#
Parameters:
 SystemName:
 Type: String
 Description: Your System Name.
 ImageUri:
 Type: String
 Description: Application Image URI

Resources:
# ------------------------------------------------------------#
# ALB
# ------------------------------------------------------------#
 appALB:
 Type: AWS::ElasticLoadBalancingV2::LoadBalancer
 Properties:
  Name: !Sub ${SystemName}-app-alb
  Scheme: internet-facing
  Type: application
  IpAddressType: ipv4
  Subnets:
  - Fn::ImportValue:
   !Sub ${SystemName}-PublicSubnet1a
  - Fn::ImportValue:
   !Sub ${SystemName}-PublicSubnet1c
  SecurityGroups:
  - Fn::ImportValue:
   !Sub ${SystemName}-app-alb-sg

 appAlbTg:
 Type: AWS::ElasticLoadBalancingV2::TargetGroup
 Properties:
  Name: !Sub ${SystemName}-app-alb-tg
  Protocol: HTTP
  Port: 80
  TargetType: ip
  VpcId:
  Fn::ImportValue:
   !Sub ${SystemName}-VPC
  HealthCheckProtocol: HTTP
  HealthCheckPath: /
  HealthCheckPort: traffic-port
  HealthyThresholdCount: 5
  UnhealthyThresholdCount: 2
  HealthCheckTimeoutSeconds: 5
  HealthCheckIntervalSeconds: 30
  Matcher:
  HttpCode: 200
  TargetGroupAttributes:
  - Key: deregistration_delay.timeout_seconds
   Value: 30

 appListener:
 Type: AWS::ElasticLoadBalancingV2::Listener
 Properties:
  LoadBalancerArn: !Ref appALB
  Protocol: HTTP
  Port: 80
  DefaultActions:
  - Type: forward
   TargetGroupArn: !Ref appAlbTg

# ------------------------------------------------------------#
# ECS Task
# ------------------------------------------------------------#
 appTaskExecutionRole:
 Type: AWS::IAM::Role
 Properties:
  AssumeRolePolicyDocument:
  Version: "2012-10-17"
  Statement:
   - Effect: "Allow"
   Principal:
    Service:
    - "ecs-tasks.amazonaws.com"
   Action:
    - "sts:AssumeRole"
  ManagedPolicyArns:
  - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
  - arn:aws:iam::aws:policy/AmazonSSMReadOnlyAccess

 appTask:
 Type: AWS::ECS::TaskDefinition
 Properties:
  Family: !Sub ${SystemName}-app-task
  RequiresCompatibilities:
  - FARGATE
  NetworkMode: awsvpc
  TaskRoleArn: !Ref appTaskExecutionRole
  ExecutionRoleArn: !Ref appTaskExecutionRole
  Cpu: 256
  Memory: 512
  ContainerDefinitions:
  - Name: app
   Image: !Ref ImageUri
   PortMappings:
   - Protocol: tcp
    HostPort: 80
    ContainerPort: 80
   LogConfiguration:
   LogDriver: awslogs
   Options:
    awslogs-group:
    Fn::ImportValue:
     !Sub ${SystemName}-loggroup
    awslogs-region: !Sub "${AWS::Region}"
    awslogs-stream-prefix: appTask
   Environment:
   - Name: DBHOST
    Value:
    Fn::ImportValue: !Sub ${SystemName}-db-endpoint
   - Name: DB
    Value:
    Fn::ImportValue: !Sub ${SystemName}-db
   - Name: DBUSER
    Value:
    Fn::ImportValue: !Sub ${SystemName}-db-user
   Secrets:
   - Name: DBPASSWORD
    ValueFrom: !Sub ${SystemName}-DBPassword

# ------------------------------------------------------------#
# ECS Service
# ------------------------------------------------------------#
 appService:
 Type: AWS::ECS::Service
 DependsOn: appALB
 Properties:
  ServiceName: !Sub ${SystemName}-app-service
  LaunchType: FARGATE
  Cluster:
  Fn::ImportValue:
   !Sub ${SystemName}-cluster
  TaskDefinition: !Ref appTask
  DesiredCount: 2
  DeploymentConfiguration:
  MinimumHealthyPercent: 50
  MaximumPercent: 200
  LoadBalancers:
  - TargetGroupArn: !Ref appAlbTg
   ContainerName: app
   ContainerPort: 80
  NetworkConfiguration:
  AwsvpcConfiguration:
   AssignPublicIp: DISABLED
   SecurityGroups:
   - Fn::ImportValue:
    !Sub ${SystemName}-app-fargate-sg
   Subnets:
   - Fn::ImportValue:
    !Sub ${SystemName}-PrivateSubnet1a
   - Fn::ImportValue:
    !Sub ${SystemName}-PrivateSubnet1c

# ------------------------------------------------------------#
# Outputs
# ------------------------------------------------------------#
Outputs:
 appALB:
 Value: !Ref appALB
 Export:
  Name: !Sub ${SystemName}-app-alb
 appALBUrl:
 Value:
  Fn::Join:
  - ""
  - - http://
   - Fn::GetAtt: appALB.DNSName
   - /
 Export:
  Name: !Sub ${SystemName}-app-alb-url
 appAlbTg:
 Value: !Ref appAlbTg
 Export:
  Name: !Sub ${SystemName}-app-alb-tg
 appTask:
 Value: !Ref appTask
 Export:
  Name: !Sub ${SystemName}-app-task
 appService:
 Value: !Ref appService
 Export:
  Name: !Sub ${SystemName}-app-service

PipeLine作成

AWSコマンド

aws cloudformation deploy
--stack-name handson-app-codepipeline
--template-file template/app-codepipeline.yml
--no-execute-changeset
--capabilities CAPABILITY_IAM
--parameter-overrides
SystemName=handson

app-codepipeline.yml

app-codepipeline.yml

AWSTemplateFormatVersion: 2010-09-09
Description: CodePipeline For ECS Fargate with CodeCommit

# ------------------------------------------------------------#
# Parameters
# ------------------------------------------------------------#
Parameters:
 SystemName:
 Type: String
 Description: Your System Name.

Resources:
# ------------------------------------------------------------#
# IAM
# ------------------------------------------------------------#
 #-- CodeBuild用Role --#
 appCodeBuildServiceRole:
 Type: AWS::IAM::Role
 Properties:
  Path: /
  AssumeRolePolicyDocument:
  Version: "2012-10-17"
  Statement:
   - Effect: Allow
   Principal:
    Service: codebuild.amazonaws.com
   Action: sts:AssumeRole
  ManagedPolicyArns:
  - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryFullAccess
  - arn:aws:iam::aws:policy/AmazonS3FullAccess
  - arn:aws:iam::aws:policy/CloudWatchLogsFullAccess

 #-- CodePipeline用Role --#
 appCodePipelineServiceRole:
 Type: AWS::IAM::Role
 Properties:
  Path: /
  AssumeRolePolicyDocument:
  Version: 2012-10-17
  Statement:
   - Effect: Allow
   Principal:
    Service: codepipeline.amazonaws.com
   Action: sts:AssumeRole
  Policies:
  - PolicyName: PipelinePolicy
   PolicyDocument:
   Version: 2012-10-17
   Statement:
    - Resource:
     - !Sub arn:aws:s3:::${ArtifactBucket}/*
    Effect: Allow
    Action:
     - s3:PutObject
     - s3:GetObject
     - s3:GetObjectVersion
     - s3:GetBucketVersioning
    - Resource: "*"
    Effect: Allow
    Action:
     - codecommit:GetRepository
     - codecommit:ListBranches
     - codecommit:GetUploadArchiveStatus
     - codecommit:UploadArchive
     - codecommit:CancelUploadArchive
     - codedeploy:CreateDeployment
     - codedeploy:GetApplication
     - codedeploy:GetApplicationRevision
     - codedeploy:GetDeployment
     - codedeploy:GetDeploymentConfig
     - codedeploy:RegisterApplicationRevision
     - codebuild:StartBuild
     - codebuild:StopBuild
     - codebuild:BatchGet*
     - codebuild:Get*
     - codebuild:List*
     - codecommit:GetBranch
     - codecommit:GetCommit
     - s3:*
     - ecs:*
     - elasticloadbalancing:*
     - autoscaling:*
     - iam:PassRole

# ------------------------------------------------------------#
# S3
# ------------------------------------------------------------#
 #-- アーティファクト用S3バケット --#
 ArtifactBucket:
 Type: AWS::S3::Bucket

# ------------------------------------------------------------#
# CodeBuild
# ------------------------------------------------------------#
 appCodeBuildProject:
 Type: AWS::CodeBuild::Project
 Properties:
  Name: !Sub ${SystemName}-app-build-project
  Artifacts:
  Type: CODEPIPELINE
  Description: !Sub Building stage for php app
  Environment:
  ComputeType: BUILD_GENERAL1_SMALL
  EnvironmentVariables:
   - Name: AWS_ACCOUNT_ID
   Value: !Ref AWS::AccountId
   - Name: IMAGE_NAME
   Value: !Sub ${SystemName}-ecr-php-app
   - Name: CONTAINER_NAME
   Value: !Sub app
  Image: aws/codebuild/amazonlinux2-x86_64-standard:2.0
  Type: LINUX_CONTAINER
  PrivilegedMode: True
  ServiceRole: !Ref appCodeBuildServiceRole
  Source:
  Type: CODEPIPELINE
  TimeoutInMinutes: 30

# ------------------------------------------------------------#
# CodePipeline
# ------------------------------------------------------------#
 #-- CodePipeline起動用CloudWatchEvents --#
 appCloudWatchEventRole:
 Type: 'AWS::IAM::Role'
 Properties:
  AssumeRolePolicyDocument:
  Version: 2012-10-17
  Statement:
   - Effect: Allow
   Principal:
    Service:
    - events.amazonaws.com
   Action: 'sts:AssumeRole'
  Path: /
  Policies:
  - PolicyName: cwe-pipeline-execution
   PolicyDocument:
   Version: 2012-10-17
   Statement:
    - Effect: Allow
    Action: 'codepipeline:StartPipelineExecution'
    Resource: !Join
     - ''
     - - 'arn:aws:codepipeline:'
     - !Ref 'AWS::Region'
     - ':'
     - !Ref 'AWS::AccountId'
     - ':'
     - !Ref appCodePipeline

 #-- CodePipeline起動用CloudWatchEvents --#
 appCloudwatchEventsRule:
 Type: AWS::Events::Rule
 Properties:
  EventPattern:
  source:
   - aws.codecommit
  detail-type:
   - CodeCommit Repository State Change
  resources:
   - Fn::ImportValue: !Sub ${SystemName}-codecommit-php-app-arn
  detail:
   event:
   - referenceCreated
   - referenceUpdated
   referenceType:
   - branch
   referenceName:
   - master
  Targets:
  - Arn: !Join
   - ''
   - - 'arn:aws:codepipeline:'
   - !Ref 'AWS::Region'
   - ':'
   - !Ref 'AWS::AccountId'
   - ':'
   - !Ref appCodePipeline
   Id: !Sub ${SystemName}-app-codepipeline
   RoleArn: !GetAtt appCloudWatchEventRole.Arn

 #-- CodePipeline --#
 appCodePipeline:
 Type: AWS::CodePipeline::Pipeline
 DependsOn: ArtifactBucket
 Properties:
  ArtifactStore:
  Location: !Ref ArtifactBucket
  Type: S3
  Name: !Sub ${SystemName}-app-codepipeline
  RestartExecutionOnUpdate: false
  RoleArn: !GetAtt appCodePipelineServiceRole.Arn
  Stages:
  - Name: Source
   Actions:
   - Name: SourceAction
    ActionTypeId:
    Category: Source
    Owner: AWS
    Provider: CodeCommit
    Version: 1
    OutputArtifacts:
    - Name: SourceOutput
    Configuration:
    RepositoryName:
     Fn::ImportValue: !Sub ${SystemName}-codecommit-php-app-name
    BranchName: master
    PollForSourceChanges: false
    RunOrder: 1
  - Name: Build
   Actions:
   - Name: CodeBuild
    InputArtifacts:
    - Name: SourceOutput
    ActionTypeId:
    Category: Build
    Owner: AWS
    Provider: CodeBuild
    Version: 1
    Configuration:
    ProjectName: !Ref appCodeBuildProject
    OutputArtifacts:
    - Name: CodebuildOutput
    RunOrder: 1
  - Name: Deploy
   Actions:
   - Name: appDeploy
    ActionTypeId:
    Category: Deploy
    Owner: AWS
    Version: 1
    Provider: ECS
    Configuration:
    ClusterName:
     Fn::ImportValue: !Sub ${SystemName}-cluster
    ServiceName:
     Fn::ImportValue: !Sub ${SystemName}-app-service
    FileName: imagedefinitions.json
    RunOrder: 1
    InputArtifacts:
    - Name: CodebuildOutput

# ------------------------------------------------------------#
# Outputs
# ------------------------------------------------------------#
Outputs:
 ArtifactBucket:
 Value: !Ref ArtifactBucket
 Export:
  Name: !Sub ${SystemName}-ArtifactBucket

Follow me!

前の記事

AWS CI/CD

次の記事

HTML5 video