ecs execの仕組み

AWS Systems Manager (SSM) セッションマネージャーを使用して実行中のコンテナとの接続を確立する
Linux コンテナでのみサポート

-> fargateでは特定のバージョン(1.4.0)以降を使うと、SSMをインストールする必要がない

CloudFormationのpropertiesで、EnableExecuteCommand: trueと書く

EcsExecRolePolicy:
  Type: AWS::IAM::Policy
  Properties:
    PolicyName: Ecs-exec-role
    PolicyDocument:
      Version: 2012-10-17
      Statement:
        - Effect: Allow
          Action:
            - "ssmmesages:CreateControlChannel"
            - "ssmmesages:CreateDataChannel"
            - "ssmmesages:OpenControlChannel"
            - "ssmmesages:OpenDataChannel"
          Resource: "*"
      Roles:

Service:
  Type: AWS::ECS::Service
  Properties:
    Cluster: !Ref Cluster
    DeploymentConfiguration:
      MaximumPercent: 200
      MinimumHealthyPercent: 100
    DesiredCount: !Ref DesiredCount
    EnableExecuteCommand: true
    LaunchType: FARGATE
    LoadBalancers:
      - ContainerName: app
        ContainerPort: 80
        TargetGroupArn: arn
    NetworkConfiguration:
      AwsvpcConfiguration:
        AssignPublicIp: ENABLED
        SecurityGroups:
          - arn
        Subnets:
          - arn
          - arn
    ServiceName: SampleFargate
    TaskDefinition: !RefTaskDefinition

なるほど、OKそうには見える

[AWS CloudFormation] S3とCloudFront(Origin Access Identityは使わない)

AWSTemplateFormatVersion: "2010-09-09"
Description:
  S3 and CloudFront for Static website hosting

Metadata:
  "AWS::CloudFormation::Interface":
    ParameterGroups:
      - Label:
          default: "S3 and CooudFront Configuration"
        Parameters:
          - WebsiteDomainName
          - CFSSLCertificateId

    ParameterLables:
      WebsiteDomainName:
        default: "WebsiteDomainName"
      CFSSLCertificateId:
        default: "CFSSLCertificateId"

# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------# 
Parameters:
  WebsiteDomainName:
    Type: String

  CFSSLCertificateId:
    Type: String


Resources:
# ------------------------------------------------------------#
#  S3 Bucket
# ------------------------------------------------------------#        
# Bucket
  Bucket:
    Type: "AWS::S3::Bucket"
    Properties:
      BucketName: !Ref WebsiteDomainName
      AccessControl: PublicRead
      WebsiteConfiguration:
        IndexDocument: index.html

  BucketPolicy:
    Type: "AWS::S3::BucketPolicy"
    Properties:
      Bucket: !Ref Bucket
      PolicyDocument:
        Statement:
        - Action: "s3:GetObject"
          Effect: Allow
          Resource: !Sub "arn:aws:s3:::${Bucket}/*"
          Principal: '*'

# ------------------------------------------------------------#
#  CloudFront
# ------------------------------------------------------------#
  CloudFrontDistribution:
    Type: "AWS::CloudFront::Distribution"
    Properties:
      DistributionConfig:
        PriceClass: PriceClass_All
        Aliases:
        - !Ref WebsiteDomainName
        Origins:
        - CustomOriginConfig:
            OriginProtocolPolicy: http-only
          DomainName: !Sub "${WebsiteDomainName}.s3-website-${AWS::Region}.amazonaws.com"
          Id: !Sub "S3-Website-${WebsiteDomainName}.s3-website-ap-northeast-1.amazonaws.com"
        DefaultCacheBehavior:
          TargetOriginId: !Sub "S3-Website-${WebsiteDomainName}.s3-website-ap-northeast-1.amazonaws.com"
          ViewerProtocolPolicy: redirect-to-https
          AllowedMethods:
          - GET
          - HEAD
          CachedMethods:
          - GET
          - HEAD
          DefaultTTL: 3600
          MaxTTL: 86400
          MinTTL: 60
          Compress: true
          ForwardedValues:
            Cookies:
              Forward: none
            QueryString: false
        ViewerCertificate:
          SslSupportMethod: sni-only
          MinimumProtocolVersion: TLSv1.1_2016
          AcmCertificateArn: !Sub "arn:aws:acm:us-east-1:${AWS::AccountId}:certificate/${CFSSLCertificateId}"
        HttpVersion: http2
        Enabled: true

# ------------------------------------------------------------#
# Output Parameters
# ------------------------------------------------------------# 
Outputs:
#WebsiteURL:
  WebsiteURL:
    Value: !GetAtt Bucket.WebsiteURL

#DistributionID
  DistributionID:
    Value: !Ref CloudFrontDistribution

#DomainName
  DomainName:
    Value: !GetAtt CloudFrontDistribution.DomainName

なるほど、これはやばいわ

[AWS CloudFormation] EBSスナップショットのData Lifecycle Managerを設定

Amazon Data Lifecycle Managerを利用すると、バックアップしたいEBSとそのバックアップポリシーを定義するだけで自動的にスナップショットを取得することができる

EBSNameTagを指定する

AWSTemplateFormatVersion: "2010-09-09"
Description:
  Create DLM LifecyclePolicy

# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------# 
Parameters:
  EBSNameTag:
    Type: String

# ------------------------------------------------------------#
#  DLM LifecyclePolicy
# ------------------------------------------------------------#
Resources:
  LifecyclePolicy:
    Type: "AWS::DLM::LifecyclePolicy"
    Properties:
      Description: "Daily EBS Snapshot"
      State: "ENABLED"
      ExecutionRoleArn: !Sub "arn:aws:iam::${AWS::AccountId}:role/AWSDataLifecycleManagerDefaultRole"
      PolicyDetails:
        ResourceTypes:
          - "VOLUME"
        TargetTags:
          -
            Key: "Name"
            Value: !Ref EBSNameTag

        Schedules:
          -
            Name: !Sub "${EBSNameTag}-daily-snapshot"
            TagsToAdd:
              -
                Key: "type"
                Value: "DailySnapshot"

            CreateRule:
              Interval: 24
              IntervalUnit: "HOURS"
              Times:
                - "17:00"
            RetainRule:
              Count: 3
            CopyTags: true

なるほど、backupもcloudformationで書けちゃうのね…

[AWS CloudFormation] CloudFormationマクロを作成

### CloudFormationマクロとは
CloudFormationテンプレートの標準的な定義だけでは実現できない処理を、テンプレート内からLambda関数(マクロ)を呼び出すことで実現できるようにするCloudFormation拡張機能のこと

AWSTemplateFormatVersion: "2010-09-09"
Description: 
  CloudFormation Macro Create

# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------# 
Parameters:
  CFnMacroName:
    Type: String

# ------------------------------------------------------------#
#  LambdaExecutionRole
# ------------------------------------------------------------#        
Resources:
  LambdaExecutionRole:
    Type: "AWS::IAM::Role"
    Properties:
      RoleName: !Sub "${CFnMacroName}-LambdaExecutionRole"
      Policies:
        - PolicyName: !Sub "${CFnMacroName}-LambdaExecutionRole-Policy"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - "logs:CreateLogGroup"
                  - "logs:CreateLogStream"
                  - "logs:PutLogEvents"
                Resource: "arn:aws:logs:*:*:*"

              - Effect: Allow
                Action:
                  - "ssm:PutParameter"
                Resource: "*"

      AssumeRolePolicyDocument: 
        Version: "2012-10-17"
        Statement: 
          - Sid: ""
            Effect: Allow
            Principal: 
              Service: "lambda.amazonaws.com"
            Action: "sts:AssumeRole"

# ------------------------------------------------------------#
#  Lambda Function for CloudFormation Macro
#  example : generate random string(10) and register SSM
# ------------------------------------------------------------#  
  LambdaFunction:
    Type: "AWS::Lambda::Function"
    Properties:
      FunctionName: !Ref CFnMacroName
      Role: !GetAtt LambdaExecutionRole.Arn
      Handler: index.handler

      Code:
        ZipFile: !Sub |
          import boto3
          import string
          import random

          ssm = boto3.client('ssm')

          def handler(event, context):
            key = event['params']['Key']
            description = event['params']['Description']
            randomstr = ''.join(random.choices(string.ascii_letters + string.digits, k=10))

            ssm.put_parameter(
              Name=key,
              Value=randomstr,
              Type='SecureString',
              Description=description
            )
            return {'requestId': event['requestId'], 'status': 'success', 'fragment': randomstr}

      Runtime: "python3.6"
      MemorySize: 128
      Timeout: 5

# ------------------------------------------------------------#
#  CloudFormation Macro
# ------------------------------------------------------------#  
  CFnMacro:
    Type: "AWS::CloudFormation::Macro"
    Properties:
      FunctionName: !Ref LambdaFunction
      Name: !Ref CFnMacroName
      Description: !Ref CFnMacroName 

index.pyが作られる

これは凄いな

[AWS CloudFormation] ELBログ用のS3 Bucket作成

AWSTemplateFormatVersion: "2010-09-09"
Description:
  S3 Bucket for ELB AccessLog Create

Metadata:
  "AWS::CloudFormation::Interface":
    ParameterGroups:
      - Label:
          default: "S3 Bucket for ELB AccessLog Configuration"
        Parameters:
          - ELBLogBucketName

    ParameterLabels:
      ELBLogBucketName:
        default: "ELBLogBucketName"

# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------# 
Parameters:
  ELBLogBucketName:
    Type: String

# ------------------------------------------------------------#
# ELBAccountId Mappings
# ------------------------------------------------------------# 
Mappings:
  ELBAccountID:
    ap-northeast-1:
      "AccountId": ""

Resources:
# ------------------------------------------------------------#
#  S3
# ------------------------------------------------------------#        
# ELBLogBucket
  ELBLogBucket:
    Type: "AWS::S3::Bucket"
    Properties:
      BucketName: !Ref ELBLogBucketName

#BucketPolicy
  ELBLogBucketPolicy:
    Type: "AWS::S3::BucketPolicy"
    Properties:
      Bucket: !Ref ELBLogBucket
      PolicyDocument:
        Id: "AWSCFn-AccessLogs-Policy-20180920"
        Version: "2012-10-17"
        Statement:
          - Sid: AWSCFn-20180920
            Effect: "Allow"
            Action:
              - "s3:PutObject"
            Resource: !Sub "arn:aws:s3:::${ELBLogBucket}/AWSLogs/${AWS::AccountId}/*"
            Principal:
              AWS: !FindInMap [ ELBAccountId, !Ref "AWS::Region", AccountId ]

# ------------------------------------------------------------#                
# Output Parameters
# ------------------------------------------------------------# 
Outputs:
#ELBLogBucket
  ELBLogBucket:
    Value: !Ref ELBLogBucket
    Export:
      Name: !Ref ELBLogBucketName

ELBの管理画面で、AccessLogをS3に設定できるのね。

[AWS CloudFormation] System Managerのパラメータストアの値を登録

System Managerのパラメータストアとは、Key/Valueのパラメータを集中管理する機能

Stringタイプのパラメータを登録する場合のサンプル

AWSTemplateFormatVersion: "2010-09-09"
Description:
  SSM Parameter Create

# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------# 
Parameters:
  MasterUserName:
    Type: String
    NoEcho: true
    MinLength: 1
    MaxLength: 16
    AllowedPattern: "[a-zA-Z][a-zA-Z0-9]*"
    ConstraintDescription: "must begin with a letter and contain only alphanumeric characters."

# ------------------------------------------------------------#
#  SSM Parameter
# ------------------------------------------------------------# 
Resources:
  Parameter:
    Type: "AWS::SSM::Parameter"
    Properties:
      Name: "MasterUsername"
      Type: "String"
      Value: !Ref MasterUserName
      Description: "MasterUsername for RDS"

うん、これはよーわからんね。

[AWS CloudFormation] cloudwatchによるautorecovery

EC2インスタンスをCloudWatchでモニタリングし、基になるハードウェア障害またはAWSによる復旧を必要とする問題でインスタンスが正常に機能しなくなった事象を検知すると、CloudWatchが自動的にインスタンスを復旧する

cloudwatch-autorecovery.yml

AWSTemplateFormatVersion: "2010-09-09"
Description:
  Auto Recovery for EC2

# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------# 
Parameters:
  EC2Instance:
    Type: AWS::EC2::Instance::Id

Resources:
# ------------------------------------------------------------#
#  AutoRecoveryAlarm
# ------------------------------------------------------------# 
  AutoRecoveryAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: !Sub "${EC2Instance}-autorecovery"
      Namespace: AWS/EC2
      MetricName: StatusCheckFailed_System
      Statistic: Minimum
      Period: 60
      EvaluationPeriods: 10
      ComparisonOperator: GreaterThanThreshold
      Threshold: 0
      AlarmActions:
        - !Sub "arn:aws:automate:${AWS::Region}:ec2:recover"
      Dimensions:
        - Name: InstanceId
          Value: !Ref EC2Instance

なるほど、これはいけるね

[AWS CloudFormation] ALB + EC2 2台構成

AWSTemplateFormatVersion: "2010-09-09"
Description:
  ALB and EC2 Instance Create

Metadata:
  "AWS::CloudFormation::Interface":
    ParameterGroups:
      - Label:
          default: "Project Name Prefix"
        Parameters:
          - PJPrefix
      - Label:
          default: "InternetALB Configuration"
        Parameters:
          - InternetALBName
      - Label:
          default: "EC2Instance Configuration"
        Parameters:
          - KeyPairName
          - EC2InstanceName
          - EC2InstanceAMI
          - EC2InstanceInstanceType
          - EC2InstanceVolumeType
          - EC2InstanceVolumeSize
          - SSHAccessSourceIP

    ParameterLabels:
      IneternetALBName:
        default: "InternetALBName"
      KeyPairName:
        default: "KeyPiarName"
      EC2InstanceName:
        default: "EC2 Name"
      EC2InstanceAMI:
        default: "EC2 AMI"
      EC2InstanceInstanceType:
        default: "EC2 InstanceType"
      EC2InstanceVolumeType:
        default: "EC2 VolumeType"
      EC2InstanceVolumeSize:
        default: "EC2 VolumeSize"
      SSHAccessSourceIP:
        default: "SSH AccessSourceIP"

# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------# 
Parameters:
  PJPrefix:
    Type: String
#InternetALB
  InternetALBName:
    Type: String
    Default: "web"


#EC2Instance
  KeyPairName:
    Type: AWS::EC2::KeyPair::KeyName
    Default: ""
  EC2InstanceName:
    Type: String
    Default: "web"
  EC2InstanceAMI:
    Type: String
    Default: ""
  EC2InstanceInstanceType:
    Type: String
    Default: "t2.micro"
  EC2InstanceVolumeType:
    Type: String
    Default: "gp2"
  EC2InstanceVolumeSize:
    Type: String
    Default: "30"
  SSHAccessSourceIP:
    Type: String

Resources:
# ------------------------------------------------------------#
#  IAM Role for EC2
# ------------------------------------------------------------# 
  EC2IAMRole:
    Type: "AWS::IAM::Role"
    Properties:
      RoleName: !Sub "${PJPrefix}-${EC2InstanceName}-role"
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - "ec2.amazonaws.com"
            Action:
              - "sts:AssumeRole"

      Path: "/"
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM"
        - "arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess"

  EC2InstanceProfile:
    Type: "AWS::IAM::InstanceProfile"
    Properties:
      Path: "/"
      Roles:
        - Ref: EC2IAMRole
      InstanceProfileName: !Sub "${PJPrefix}-${EC2InstanceName}-profile"

# ------------------------------------------------------------#
#  EC2Instance AZ:A
# ------------------------------------------------------------#
  EC2Instance01:
    Type: "AWS::EC2::Instance"
    Properties:
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-${EC2InstanceName}-01"
      ImageId: !Ref EC2InstanceAMI
      InstanceType: !Ref EC2InstanceInstanceType
      KeyName: !Ref KeyPairName
      IamInstanceProfile: !Ref EC2InstanceProfile
      DisableApiTermination: false
      EbsOptimized: false
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            DeleteOnTermination: true
            VolumeType: !Ref EC2InstanceVolumeType
            VolumeSize: !Ref EC2InstanceVolumeSize
      SecurityGroupIds:
        - !Ref ManagedSecurityGroup
        - !Ref WebSecurityGroup
      SubnetId: {"Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-a"}
      UserData: !Base64 | 
        #! /bin/bash
        yum update -y

# ------------------------------------------------------------#
#  EC2Instance AZ:C
# ------------------------------------------------------------#
  EC2Instance02:
    Type: "AWS::EC2::Instance"
    Properties:
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-${EC2InstanceName}-02"
      ImageId: !Ref EC2InstanceAMI
      InstanceType: !Ref EC2InstanceInstanceType
      KeyName: !Ref KeyPairName
      IamInstanceProfile: !Ref EC2InstanceProfile
      DisableApiTermination: false
      EbsOptimized: false
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            DeleteOnTermination: true
            VolumeType: !Ref EC2InstanceVolumeType
            VolumeSize: !Ref EC2InstanceVolumeSize
      SecurityGroupIds:
        - !Ref ManagedSecurityGroup
        - !Ref WebSecurityGroup
      SubnetId: {"Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-c"}
      UserData: !Base64 | 
        #! /bin/bash
        yum update -y

# ------------------------------------------------------------#
#  SecurityGroup for Managed
# ------------------------------------------------------------#
  ManagedSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      VpcId: {"Fn::ImportValue": !Sub "${PJPrefix}-vpc"}
      GroupName: !Sub "${PJPrefix}-managed-sg"
      GroupDescription: "-"
      Tags:
        - Key: "Name"
          Value: !Sub "${PJPrefix}-managed-sg"

# Rule
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: !Ref SSHAccessSourceIP

# ------------------------------------------------------------#
#  SecurityGroup for ALB
# ------------------------------------------------------------#
  ALBSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-vpc" }
      GroupName: !Sub "${PJPrefix}-alb-sg"
      GroupDescription: "-"
      Tags:
        - Key: "Name"
          Value: !Sub "${PJPrefix}-alb-sg"

# Rule
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: "0.0.0.0/0"

        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: "0.0.0.0/0"

# ------------------------------------------------------------#
#  SecurityGroup for Web
# ------------------------------------------------------------#
  WebSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-vpc" }
      GroupName: !Sub "${PJPrefix}-web-sg"
      GroupDescription: "-"
      Tags:
        - Key: "Name"
          Value: !Sub "${PJPrefix}-web-sg"

# Rule
  WebSecurityGroupIngress:
    Type: "AWS::EC2::SecurityGroupIngress"
    Properties:
      IpProtocol: tcp
      FromPort: 80
      ToPort: 80
      SourceSecurityGroupId: !GetAtt [ ALBSecurityGroup, GroupId ]
      GroupId: !GetAtt [ WebSecurityGroup, GroupId ]

# ------------------------------------------------------------#
#  ElasticIP for EC2Instance01
# ------------------------------------------------------------# 
  ElasticIP01:
    Type: "AWS::EC2::EIP"
    Properties:
      Domain: vpc

  ElasticIPAssociate01:
    Type: AWS::EC2::EIPAssociation
    Properties:
      AllocationId: !GetAtt ElasticIP01.AllocationId
      InstanceId: !Ref EC2Instance01

# ------------------------------------------------------------#
#  ElasticIP for EC2Instance02
# ------------------------------------------------------------# 
  ElasticIP02:
    Type: "AWS::EC2::EIP"
    Properties:
      Domain: vpc

  ElasticIPAssociate02:
    Type: AWS::EC2::EIPAssociation
    Properties:
      AllocationId: !GetAtt ElasticIP02.AllocationId
      InstanceId: !Ref EC2Instance02

# ------------------------------------------------------------#
#  Target Group
# ------------------------------------------------------------#
  TargetGroup:
    Type: "AWS::ElasticLoadBalancingV2::TargetGroup"
    Properties:
      VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-vpc"}
      Name: !Sub "${PJPrefix}-${InternetALBName}-tg"
      Protocol: HTTP
      Port: 80
      HealthCheckProtocol: HTTP
      HealthCheckPath: "/"
      HealthCheckPort: "traffic-port"
      HealthyThresholdCount: 2
      UnhealthyThresholdCount: 2
      HealthCheckTimeoutSeconds: 5
      HealthCheckIntervalSeconds: 10
      Matcher:
        HttpCode: 200
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-${InternetALBName}-tg"
      TargetGroupAttributes:
        - Key: "deregistration_delay.timeout_seconds"
          Value: 300
        - Key: "stickiness.enabled"
          Value: false
        - Key: "stickiness.type"
          Value: lb_cookie
        - Key: "stickiness.lb_cookie.duration_seconds"
          Value: 86400
      Targets:
        - Id: !Ref EC2Instance01
        - Id: !Ref EC2Instance02
          Port: 80


# ------------------------------------------------------------#
#  Internet ALB
# ------------------------------------------------------------#
  InternetALB:
    Type: "AWS::ElasticLoadBalancingV2::LoadBalancer"
    Properties:
      Name: !Sub "${PJPrefix}-${InternetALBName}-alb"
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-${InternetALBName}-alb"
      Scheme: "internet-facing"
      LoadBalancerAttributes:
        - Key: "deletion_protection.enabled"
          Value: false
        - Key: "idle_timeout.timeout_seconds"
          Value: 60
      SecurityGroups:
        - !Ref ALBSecurityGroup
      Subnets:
        - { "Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-a" }
        - { "Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-c" }

  ALBListener:
    Type: "AWS::ElasticLoadBalancingV2::Listener"
    Properties:
      DefaultActions:
        - TargetGroupArn: !Ref TargetGroup
          Type: forward
      LoadBalancerArn: !Ref InternetALB
      Port: 80
      Protocol: HTTP

# ------------------------------------------------------------#
# Output Parameters
# ------------------------------------------------------------#
Outputs:
#InternetALB
  ALBDNSName:
    Value: !GetAtt InternetALB.DNSName
    Export:
      Name: !Sub "${PJPrefix}-${InternetALBName}-alb-dnsname"

#EC2Instance01
  EC2Instance01ID:
    Value: !Ref EC2Instance01
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-01-id"

  EC2Instance01PrivateIp:
    Value: !GetAtt EC2Instance01.PrivateIp
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-01-private-ip"

  EC2Instance01EIP:
    Value: !GetAtt EC2Instance01.PublicIp
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-01-eip"

  EC2Instance01RoleName:
    Value: !Sub "${PJPrefix}-${EC2InstanceName}-01-role"
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-01-role-name"

#EC2Instance02
  EC2Instance02ID:
    Value: !Ref EC2Instance02
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-02-id"

  EC2Instance02PrivateIp:
    Value: !GetAtt EC2Instance02.PrivateIp
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-02-private-ip"

  EC2Instance02EIP:
    Value: !GetAtt EC2Instance02.PublicIp
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-02-eip"

  EC2Instance02RoleName:
    Value: !Sub "${PJPrefix}-${EC2InstanceName}-02-role"
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-02-role-name"

うん、ここまでは来る。
問題はこれから。

[AWS CloudFormation] ELB + EC2 2台構成を作る

prefix: 0.0.0.0/0
amiID: ami-0ab0bbbd329f565e6

AWSTemplateFormatVersion: "2010-09-09"
Description:
  ELB and EC2 Instance Create

Metadata:
  "AWS::CloudFormation::Interface":
    ParameterGroups:
      - Label:
          default: "Project Name Prefix"
        Parameters:
          - PJPrefix
      - Label:
          default: "InternetELB Configuration"
        Parameters:
          - InternetELBName
      - Label:
          default: "EC2Instance Configuration"
        Parameters:
          - KeyPairName
          - EC2InstanceName
          - EC2InstanceAMI
          - EC2InstanceInstanceType
          - EC2InstanceVolumeType
          - EC2InstanceVolumeSize
          - SSHAccessSourceIP

    ParameterLabels:
      IneternetELBName:
        default: "InternetELBName"
      KeyPairName:
        default: "KeyPiarName"
      EC2InstanceName:
        default: "EC2 Name"
      EC2InstanceAMI:
        default: "EC2 AMI"
      EC2InstanceInstanceType:
        default: "EC2 InstanceType"
      EC2InstanceVolumeType:
        default: "EC2 VolumeType"
      EC2InstanceVolumeSize:
        default: "EC2 VolumeSize"
      SSHAccessSourceIP:
        default: "SSH AccessSourceIP"

# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------# 
Parameters:
  PJPrefix:
    Type: String
#InternetELB
  InternetELBName:
    Type: String
    Default: "web"


#EC2Instance
  KeyPairName:
    Type: AWS::EC2::KeyPair::KeyName
    Default: ""
  EC2InstanceName:
    Type: String
    Default: "web"
  EC2InstanceAMI:
    Type: String
    Default: ""
  EC2InstanceInstanceType:
    Type: String
    Default: "t2.micro"
  EC2InstanceVolumeType:
    Type: String
    Default: "gp2"
  EC2InstanceVolumeSize:
    Type: String
    Default: "30"
  SSHAccessSourceIP:
    Type: String

Resources:
# ------------------------------------------------------------#
#  IAM Role for EC2
# ------------------------------------------------------------# 
  EC2IAMRole:
    Type: "AWS::IAM::Role"
    Properties:
      RoleName: !Sub "${PJPrefix}-${EC2InstanceName}-role"
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - "ec2.amazonaws.com"
            Action:
              - "sts:AssumeRole"

      Path: "/"
      ManagedPolicyArns:
        - "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM"
        - "arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess"

  EC2InstanceProfile:
    Type: "AWS::IAM::InstanceProfile"
    Properties:
      Path: "/"
      Roles:
        - Ref: EC2IAMRole
      InstanceProfileName: !Sub "${PJPrefix}-${EC2InstanceName}-profile"

# ------------------------------------------------------------#
#  EC2Instance AZ:A
# ------------------------------------------------------------#
  EC2Instance01:
    Type: "AWS::EC2::Instance"
    Properties:
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-${EC2InstanceName}-01"
      ImageId: !Ref EC2InstanceAMI
      InstanceType: !Ref EC2InstanceInstanceType
      KeyName: !Ref KeyPairName
      IamInstanceProfile: !Ref EC2InstanceProfile
      DisableApiTermination: false
      EbsOptimized: false
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            DeleteOnTermination: true
            VolumeType: !Ref EC2InstanceVolumeType
            VolumeSize: !Ref EC2InstanceVolumeSize
      SecurityGroupIds:
        - !Ref ManagedSecurityGroup
        - !Ref WebSecurityGroup
      SubnetId: {"Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-a"}
      UserData: !Base64 | 
        #! /bin/bash
        yum update -y

# ------------------------------------------------------------#
#  EC2Instance AZ:C
# ------------------------------------------------------------#
  EC2Instance02:
    Type: "AWS::EC2::Instance"
    Properties:
      Tags:
        - Key: Name
          Value: !Sub "${PJPrefix}-${EC2InstanceName}-02"
      ImageId: !Ref EC2InstanceAMI
      InstanceType: !Ref EC2InstanceInstanceType
      KeyName: !Ref KeyPairName
      IamInstanceProfile: !Ref EC2InstanceProfile
      DisableApiTermination: false
      EbsOptimized: false
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            DeleteOnTermination: true
            VolumeType: !Ref EC2InstanceVolumeType
            VolumeSize: !Ref EC2InstanceVolumeSize
      SecurityGroupIds:
        - !Ref ManagedSecurityGroup
        - !Ref WebSecurityGroup
      SubnetId: {"Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-c"}
      UserData: !Base64 | 
        #! /bin/bash
        yum update -y


# ------------------------------------------------------------#
#  SecurityGroup for Managed
# ------------------------------------------------------------#
  ManagedSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      VpcId: {"Fn::ImportValue": !Sub "${PJPrefix}-vpc"}
      GroupName: !Sub "${PJPrefix}-managed-sg"
      GroupDescription: "-"
      Tags:
        - Key: "Name"
          Value: !Sub "${PJPrefix}-managed-sg"

# Rule
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: !Ref SSHAccessSourceIP

# ------------------------------------------------------------#
#  SecurityGroup for ELB
# ------------------------------------------------------------#
  ELBSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-vpc" }
      GroupName: !Sub "${PJPrefix}-elb-sg"
      GroupDescription: "-"
      Tags:
        - Key: "Name"
          Value: !Sub "${PJPrefix}-elb-sg"

# Rule
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: "0.0.0.0/0"

        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: "0.0.0.0/0"

# ------------------------------------------------------------#
#  SecurityGroup for Web
# ------------------------------------------------------------#
  WebSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      VpcId: { "Fn::ImportValue": !Sub "${PJPrefix}-vpc" }
      GroupName: !Sub "${PJPrefix}-web-sg"
      GroupDescription: "-"
      Tags:
        - Key: "Name"
          Value: !Sub "${PJPrefix}-web-sg"

# Rule
  WebSecurityGroupIngress:
    Type: "AWS::EC2::SecurityGroupIngress"
    Properties:
      IpProtocol: tcp
      FromPort: 80
      ToPort: 80
      SourceSecurityGroupId: !GetAtt [ ELBSecurityGroup, GroupId ]
      GroupId: !GetAtt [ WebSecurityGroup, GroupId ]

# ------------------------------------------------------------#
#  ElasticIP for EC2Instance01
# ------------------------------------------------------------# 
  ElasticIP01:
    Type: "AWS::EC2::EIP"
    Properties:
      Domain: vpc

  ElasticIPAssociate:
    Type: AWS::EC2::EIPAssociation
    Properties:
      AllocationId: !GetAtt ElasticIP01.AllocationId
      InstanceId: !Ref EC2Instance01

# ------------------------------------------------------------#
#  ElasticIP for EC2Instance02
# ------------------------------------------------------------# 
  ElasticIP02:
    Type: "AWS::EC2::EIP"
    Properties:
      Domain: vpc

  ElasticIPAssociate:
    Type: AWS::EC2::EIPAssociation
    Properties:
      AllocationId: !GetAtt ElasticIP02.AllocationId
      InstanceId: !Ref EC2Instance02

# ------------------------------------------------------------#
#  InternetELB
# ------------------------------------------------------------#
  InternetELB:
    Type: "AWS::ElasticLoadBalancing::LoadBalancer"
    Properties:
      LoadBalancerName: !Sub "${PJPrefix}-${InternetELBName}-elb"
      Scheme: "internet-facing"
      CrossZone: true
      HealthCheck:
        Target: "TCP:80"
        HealthyThreshold: 2
        UnhealthyThreshold: 2
        Interval: 30
        Timeout: 5
      ConnectionDrainingPolicy:
        Enabled: true
        Timeout: 300
      Listeners:
        - LoadBalancerPort: 80
          InstancePort: 80
          Protocol: HTTP
      Instances:
        - !Ref EC2Instance01
        - !Ref EC2Instance02
      SecurityGroups:
        - !Ref ELBSecurityGroup
      Subnets:
        - { "Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-a" }
        - { "Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-c" }

# ------------------------------------------------------------#
# Output Parameters
# ------------------------------------------------------------#
Outputs:
#InternetELB
  ELBDNSName:
    Value: !GetAtt InternetELB.DNSName
    Export:
      Name: !Sub "${PJPrefix}-${InternetELBName}-elb-dnsname"

#EC2Instance01
  EC2InstanceID:
    Value: !Ref EC2Instance01
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-01-id"

  EC2InstancePrivateIp:
    Value: !GetAtt EC2Instance01.PrivateIp
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-01-private-ip"

  EC2Instance01EIP:
    Value: !GetAtt EC2Instance01.PublicIp
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-01-eip"

  EC2InstanceRoleName:
    Value: !Sub "${PJPrefix}-${EC2InstanceName}-01-role"
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-01-role-name"

#EC2Instance02
  EC2InstanceID:
    Value: !Ref EC2Instance02
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-02-id"

  EC2InstancePrivateIp:
    Value: !GetAtt EC2Instance02.PrivateIp
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-02-private-ip"

  EC2Instance02EIP:
    Value: !GetAtt EC2Instance02.PublicIp
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-02-eip"

  EC2InstanceRoleName:
    Value: !Sub "${PJPrefix}-${EC2InstanceName}-02-role"
    Export:
      Name: !Sub "${PJPrefix}-${EC2InstanceName}-02-role-name"

$ ssh ec2-user@* -i ~/.ssh/*.pem

OK, Nice

CodeDeployでECSにデプロイしようとした時にDeployment group’s ECS service must be configured for a CODE_DEPLOY deployment controller

CodeDeployでECSにデプロイしようとして、CodeDeployのdeploymentgroup作成時に”Deployment group’s ECS service must be configured for a CODE_DEPLOY deployment controller”と表示された時

### ECSでCodeDeployを使う前準備
1. IAMでECSのCodeDeploy IAM roleを作成する
Use casesで”CodeDeploy”を選択し、”CodeDeploy – ECS”を選ぶ
2. Permission: AWSCodeDeployRoleForECS
3. Role Name: ecsCodeDeployRole

これでCodeDeployでDeployment groupを作成しようとすると…

「Deployment group’s ECS service must be configured for a CODE_DEPLOY deployment controller.」と表示される

どうやら、cloudformationでfargateを作成しようとしたときに、Blue/Greenではなく、rolling updateになっていた模様
ecs-fargate-01.yml

# ------------------------------------------------------------#
#  ECS Service
# ------------------------------------------------------------#
  ECSService:
    Type: AWS::ECS::Service
    DependsOn: ALBListener
    Properties:
      Cluster: !Ref ECSCluster
      DesiredCount: !Ref ECSTaskDesiredCount
      LaunchType: FARGATE
      LoadBalancers:
        -
          TargetGroupArn: !Ref TargetGroup
          ContainerPort: 80
          ContainerName: !Sub "${PJPrefix}-${ECSContainerName}"
      NetworkConfiguration:
       AwsvpcConfiguration:
           AssignPublicIp: ENABLED
           SecurityGroups:
             - !Ref ECSSecurityGroup
           Subnets:
             - { "Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-a" }
             - { "Fn::ImportValue": !Sub "${PJPrefix}-public-subnet-c" }
      ServiceName: !Sub "${PJPrefix}-${ECSServiceName}"
      TaskDefinition: !Ref ECSTaskDefinition

DeploymentControllerを修正して再度、cloudformationで作成する

  ECSService:
    Type: AWS::ECS::Service
    DependsOn: ALBListener
    Properties:
      Cluster: !Ref ECSCluster
      DesiredCount: !Ref ECSTaskDesiredCount
      DeploymentController:
        Type: CODE_DEPLOY

これでOK
でも、CloudFormation上のエラーログを見るのと、アプリ開発でエラーログ見るのだと、勝手がずいぶん違うな…