AWS LoftでCloudFormation初心者がペアで構築をしてきた

こんにちは。
インフラ系の部署で既存インフラのコード化を進めています、戸田です。
5年待ったゲームの発売まであと64日で夜もねれません
先日AWS Loftにいってきましたので、そのことについて書こうと思います。

ペア構築とは

以前、ブログでも紹介されたペアプロをプログラミングではなく、ペアでAWSのインフラ構築をしてみようというものです。

ステータス

CloudFormation初心者の二人
戸田:AWS初心者 S3+CloudFrontの配信をしたことがある程度
ペア:2018年11月10日にソリューションアーキテクトを取得した。(※AWS Loftに行ったのは10月中旬)

ゴール

ALB+VPC+EC2を作成します。 構成に関しては、こんな感じです。

見た目簡単そうですね。 これをCloudformationを使って構築する予定です。

AWS Loftへ向かう

持ち物

  • PC
  • AWSのアカウントID(数字12桁)
  • 身分証明書(名刺×2枚 や 運転免許証)
  • クレジットカード

行き方

目黒駅の東口から徒歩1分のビルにあります。
アマゾン新目黒オフィス(目黒セントラルスクエア)に向かいます。

AWS Loftは、17階にありますので、17階までいくエレベーターの方にいきます。 (受付がありますが、スルーして大丈夫です!

AWS Loftの入場時間は、10時からですので、10時以降に警備員の方に 「AWS Loftにいきたいです」と説明すると通れます。

後は17階に行きます。

いやー、Amazon感あふれるエレベーターですね

入場

入場時に必要なものの紹介です。
初めての場合

  • AWSのアカウントID(数字12桁) 会社で使っているものがあればそれで大丈夫です
  • 身分証明書(名刺×2枚 や 運転免許証)

この時、名前やメールアドレスを登録します。
※会社のアカウントIDを設定した場合、会社の情報も記入します。

まだ二回目行ってないので、二回目以降で必要なものの詳細は分かりません。
他の方を記事を見ているとメールアドレスがあればよさそうですね。

カフェがありますが、クレジットカードのみの支払いとなります。

AWS Loftでペア作業

当日やったこと

  1. 現地集合
  2. CloudFormationに関する知識をBlackbeltを見て学ぶ
  3. サンプルを実際に動かしてみる
  4. VPCのCloudFormatinを作成
  5. お昼ごはん (お昼から戻ってくると結構席がいっぱい埋まってました。)
  6. 引き続きVPCのCloudFormatinを作成
  7. EC2のCloudFormationを作成
  8. クロススタック参照を使って、ymlファイルをそれぞれの用途ごとに分けて作成
  9. ALBのCloudFormationを作成。(ここで時間切れ)

実際作成したもの

ブログの最後に記載↓↓

感想

ペア作業

ペア作業に関しては、今回CloudFormationに関してほとんど知識のない二人でしたので、同じくらいのスピードで理解をすることができてよかったです。
たまにお互いの認識を合わせないと、間違った解釈のまま進んでいくので、疑問に思ったことはすぐ聞くようにしていました。
そのため、一人でやるよりも早く理解が進んだかもしれません。

コワーキングスペース

コワーキングスペースを初めて利用しましたが、とても作業がしやすい場所でした。
会社よりも集中できるというのは、本当でした。
作業するにあたり声は出して作業していましたが、他の利用者ももくもくとやって相談するといった形を取られていたので、問題はないと思います。
(うるさくない程度の声量にはしていたつもりです)

AWS Loft

AWS LoftにはAWSの社員(ソリューションアーキテクト持ち)の方がいらっしゃって、とても心強いと思いました。
今回は質問はしていませんが、AWS Loftに行った日は、Auroa/RDS MysqlのスペシャリストSAの方がいらっしゃいました。
(話を聞きたい領域のエンジニアさんがいらっしゃているかどうかはどこで確認できるんですかね?Twitter監視?ベストプラクティス教えてください。)

また何かAWS関連の作業をすることがあれば、行きたいと思います!

まとめ

AWS Loftでのペア作業はとてもいいものでした!!

  • ペアで行う分、理解が早い
  • 相談がすぐできる(ペアの方やAWSの方
  • 会社よりも集中ができる などたくさんいい点がありました!!

以上となります。最後までご覧いただきありがとうございました。

(AWS Loftでこんな感じのカフェラテ飲めます。)

作成したもの

事前にEIPとACMを取得します。
実行順番はVPC→EC2→S3→ALB
となります。

VPC

AWSTemplateFormatVersion: 2010-09-09
Description: Create VPC and Security Groups
Parameters:
  AZ1:
    Type: AWS::EC2::AvailabilityZone::Name
    Description: select availability zone
  AZ2:
    Type: AWS::EC2::AvailabilityZone::Name
    Description: select availability zone
  CidrCount:
    Type: Number
    Default: 32
    Description: The number of CIDRs to generate. Valid range is between 1 and 4096.
    MinValue: 1
    MaxValue: 32
  CidrBits:
    Type: Number
    Default: 7
    Description: The number of subnet bits for the CIDR. For example, CidrBits of "8" will create a CIDR with a mask of "/24".
    MinValue: 1
    MaxValue: 32
  ProjectName:
    Type: String
    Description: Project name for tag
  Stage:
    Type: String
    Description: Stage. for distinguish related Stack. (cf. test /development / production)
  ResourceName:
    Type: String
    Description: Prefix of resource name. Max 14 chars.
    MaxLength: 14
  VpcCidr:
    Type: String
    Description: VPC CIDR Block
    AllowedPattern: ^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$

Resources:
  VPC:
    Type: 'AWS::EC2::VPC'
    Properties:
      EnableDnsSupport: 'true'
      EnableDnsHostnames: 'true'
      CidrBlock: !Ref VpcCidr
      Tags:
      - Key: Project
        Value: !Ref ProjectName
      - Key: Name
        Value: !Sub "${ProjectName}-${Stage}"

  PublicSubnetA:
    Type: 'AWS::EC2::Subnet'
    Properties:
      VpcId: !Ref VPC
      CidrBlock: !Select [0, !Cidr [ !Ref VpcCidr, !Ref CidrCount, !Ref CidrBits]]
      AvailabilityZone: !Ref AZ1
      Tags:
      - Key: Project
        Value: !Ref ProjectName
      - Key: Name
        Value: !Sub "${ProjectName}-${Stage}-A"
  PublicSubnetC:
    Type: 'AWS::EC2::Subnet'
    Properties:
      VpcId: !Ref VPC
      CidrBlock: !Select [2, !Cidr [ !Ref VpcCidr, !Ref CidrCount, !Ref CidrBits]]
      AvailabilityZone: !Ref AZ2
      Tags:
      - Key: Project
        Value: !Ref ProjectName
      - Key: Name
        Value: !Sub "${ProjectName}-${Stage}-C"
  InternetGateway:
    Type: 'AWS::EC2::InternetGateway'
    Properties:
      Tags:
      - Key: Project
        Value: !Ref ProjectName
      - Key: Name
        Value: !Sub "${ProjectName}-${Stage}"
  VPCGatewayAttachment:
    Type: 'AWS::EC2::VPCGatewayAttachment'
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway
  PublicRouteTable:
    Type: 'AWS::EC2::RouteTable'
    Properties:
      VpcId: !Ref VPC
      Tags:
      - Key: Project
        Value: !Ref ProjectName
      - Key: Name
        Value: public

  PublicRoute:
    Type: 'AWS::EC2::Route'
    DependsOn: VPCGatewayAttachment
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  PublicSubnetRouteTableAssociationA:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      SubnetId: !Ref PublicSubnetA
      RouteTableId: !Ref PublicRouteTable

  PublicSubnetRouteTableAssociationC:
    Type: 'AWS::EC2::SubnetRouteTableAssociation'
    Properties:
      SubnetId: !Ref PublicSubnetC
      RouteTableId: !Ref PublicRouteTable

  PublicSubnetNetworkAclAssociationA:
    Type: 'AWS::EC2::SubnetNetworkAclAssociation'
    Properties:
      SubnetId: !Ref PublicSubnetA
      NetworkAclId: !GetAtt
        - VPC
        - DefaultNetworkAcl

  PublicSubnetNetworkAclAssociationC:
    Type: 'AWS::EC2::SubnetNetworkAclAssociation'
    Properties:
      SubnetId: !Ref PublicSubnetC
      NetworkAclId: !GetAtt
        - VPC
        - DefaultNetworkAcl

  ALBSecurityGroup:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      VpcId: !Ref VPC
      GroupDescription: "-"
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
  InstanceSecurityGroup:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      GroupDescription: Enable RDP access
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: '22'
          ToPort: '22'
          CidrIp: "自分がインターネットに出ていくグローバルIP"/32
        - IpProtocol: tcp
          FromPort: '80'
          ToPort: '80'
          SourceSecurityGroupId: !Ref ALBSecurityGroup
      Tags:
       - Key: Name
         Value: !Sub ${ProjectName} Instance
Outputs:
  VPCId:
    Description: VPC ID
    Value: !Ref VPC
    Export:
      Name: !Sub '${ResourceName}-VPCID'
  PublicSubnetA:
    Description: The subnet ID to use for public zone-a
    Value: !Ref PublicSubnetA
    Export:
      Name: !Sub '${ResourceName}-Subnet-A-ID'
  PublicSubnetC:
    Description: The subnet ID to use for public zone-c
    Value: !Ref PublicSubnetC
    Export:
      Name: !Sub '${ResourceName}-Subnet-C-ID'
  InstanceSecurityGroup:
    Description: The security group ID to use for server
    Value: !GetAtt
      - InstanceSecurityGroup
      - GroupId
    Export:
      Name: !Sub '${ResourceName}-SecurityGroupID'
  ALBSecurityGroup:
    Description: The security group ID to use for ALB
    Value: !GetAtt
      - ALBSecurityGroup
      - GroupId
    Export:
      Name: !Sub '${ResourceName}-alb-SecurityGroupID'

EC2

AWSTemplateFormatVersion: 2010-09-09
Description: Create EC2 Instance
Parameters:
  AMIID:
    Type: AWS::EC2::Image::Id
    Description: AMI ID for Springboard Instance
  AZ1:
    Type: AWS::EC2::AvailabilityZone::Name
    Description: select availability zone
  AZ2:
    Type: AWS::EC2::AvailabilityZone::Name
    Description: select availability zone
  InstanceType:
    Type: String
    Description: must be a valid EC2 instance type.
  KeyName:
    Description: Name of an existing EC2 KeyPair to enable RDP access to the instances
    Type: 'AWS::EC2::KeyPair::KeyName'
    Description: must be the name of an existing EC2 KeyPair.
  ProjectName:
    Type: String
    Description: Project name for tag
  ResourceName:
    Type: String
    Description: Prefix of resource name. Max 14 chars.
    MaxLength: 14
  Stage:
    Type: String
    Description: Stage. for distinguish related Stack. (cf. test /development / production)
  ElasticIP:
    Type: String
    Description: ElasticIP ID for Server

Resources:
  EC2Instance:
    Type: 'AWS::EC2::Instance'
    Properties:
      DisableApiTermination: false
      InstanceInitiatedShutdownBehavior: stop
      ImageId: !Ref AMIID
      InstanceType: !Ref InstanceType
      SubnetId:
        Fn::ImportValue: !Sub "${ResourceName}-Subnet-A-ID"
      SecurityGroupIds:
        - Fn::ImportValue: !Sub "${ResourceName}-SecurityGroupID"
      KeyName: !Ref KeyName
      Tags:
        - Key: Name
          Value: !Sub "${ProjectName}-${Stage}"
        - Key: Stage
          Value: !Ref Stage
        - Key: Project
          Value: !Ref ProjectName
  IPAssoc:
    Type: 'AWS::EC2::EIPAssociation'
    Properties:
      InstanceId: !Ref EC2Instance
      AllocationId: !Ref ElasticIP

Outputs:
  InstanceId:
    Description: InstanceId of the newly created EC2 instance
    Value: !Ref EC2Instance
    Export:
      Name: !Sub '${ResourceName}-ec2'

S3

AWSTemplateFormatVersion: "2010-09-09"
Description:
  Create S3 bucket 
Parameters:
  ProjectName:
    Type: String
    Description: Project name for tag
  ResourceName:
    Type: String
    Description: Prefix of resource name. Max 14 chars.
    MaxLength: 14
  Stage:
    Type: String
    Description: Stage. for distinguish related Stack. (cf. test /development / production)
Mappings:
    S3Config:
      ap-northeast-1:
        "AccountId": "アカウントIDを入力してください"

Resources:
  ELBLogBucket:
    Type: "AWS::S3::Bucket"
    Properties:
      BucketName: !Sub "${ProjectName}-${Stage}-alb"
  ELBLogBucketPolicy:
    Type: "AWS::S3::BucketPolicy"
    Properties:
      Bucket: !Sub "${ProjectName}-${Stage}-alb"
      PolicyDocument:
        Id: !Sub "AWSCFn-AccessLogs-Policy-${ProjectName}-${Stage}-alb-log"
        Version: "2012-10-17"
        Statement:
          - Sid: !Sub "AWSCFn-AccessLogs-Policy-${ProjectName}-${Stage}-alb-log"
            Effect: "Allow"
            Action:
              - "s3:PutObject"
            Resource: !Sub "arn:aws:s3:::${ELBLogBucket}/AWSLogs/${AWS::AccountId}/*"
            Principal:
              AWS: !FindInMap [ S3Config, !Ref "AWS::Region", AccountId ]
Outputs:
  ELBLogBucket:
    Value: !Ref ELBLogBucket
    Export:
      Name: !Sub '${ProjectName}-${Stage}-alb-log'

ALB

AWSTemplateFormatVersion: "2010-09-09"
Description:
  ALB Create
Parameters:
  AcmCertificateArn:
    Type: String
    Description: ACM Certificate ARN for ALB
    Default: ''
  ProjectName:
    Type: String
    Default: 'Sample'
    Description: Project name for tag
  ResourceName:
    Type: String
    Description: Prefix of resource name. Max 14 chars.
    MaxLength: 14
  Stage:
    Type: String
    Description: Stage. for distinguish related Stack. (cf. test /development / production)
Conditions:
  HasAcmCertificateArn: !Not [!Equals [!Ref AcmCertificateArn, '']]

Resources:
  TargetGroup:
    Type: 'AWS::ElasticLoadBalancingV2::TargetGroup'
    Properties:
      VpcId: { "Fn::ImportValue": !Sub '${ResourceName}-VPCID' }
      Name: !Sub "${ProjectName}-${Stage}"
      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 "${ProjectName}-${Stage}"
      Targets:
        - Id: { "Fn::ImportValue": !Sub '${ResourceName}-ec2' }
          Port: 80

  InternetALB:
    Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer'
    Properties:
      Name: !Sub "${ProjectName}-${Stage}"
      Tags:
        - Key: Name
          Value: !Sub "${ProjectName}-${Stage}"
      Scheme: "internet-facing"
      LoadBalancerAttributes:
        - Key: "deletion_protection.enabled"
          Value: false
        - Key: "idle_timeout.timeout_seconds"
          Value: 60
        - Key: "access_logs.s3.enabled"
          Value: true
        - Key: "access_logs.s3.bucket"
          Value:
            Fn::ImportValue: !Sub "${ProjectName}-${Stage}-alb-log"
      SecurityGroups:
        - Fn::ImportValue: !Sub "${ResourceName}-alb-SecurityGroupID"
      Subnets:
        - Fn::ImportValue: !Sub "${ResourceName}-Subnet-A-ID"
        - Fn::ImportValue: !Sub "${ResourceName}-Subnet-C-ID"

  ALBListener:
    Type: 'AWS::ElasticLoadBalancingV2::Listener'
    Condition: HasAcmCertificateArn
    Properties:
      DefaultActions:
        - TargetGroupArn: !Ref TargetGroup
          Type: forward
      LoadBalancerArn: !Ref InternetALB
      Port: 443
      Protocol: HTTPS
      Certificates:
        - CertificateArn: !Ref AcmCertificateArn