のほほんおじさんのアウトプット

インフラエンジニア。 マリノス・美術館・編集工学・AWS・ウイスキー・いぶりがっこ・タルタルが好きです。好き勝手やっていこうかなって気持ちです。

CloudFormation 最低限読み書きできますレベルになるまでの話

CloudFormationは納品物に丁度いい

  • 転職して1ヶ月。AWSを利用する仕事になった今、CloudFormationは納品物として非常にわかりやすいものなので、とりあえず何でもかんでもCloudFormation化することを考える必要が出てきました。
  • 右も左もわからない中、CloudFormationを最低限読み書きできるようにはなったかなと思えたので、それまでの試行錯誤で学んだことをここに記録しておきます。

俺的基本方針

  • テンプレート作成の方針を決める
    • CloudFormationを実行したら全てが出来上がっている、というのはまずないと思った
    • 実務上、既存のネットワークがあった上でそこにCloudFormationで追加していくみたいな流れが自然だと思った
    • なので、まずは既存環境にリソースを追加するためのCloudFormationってことでテンプレートを作っていくことにした

既存環境にリソースを追加するためには

  • どうやらCloudFormationには、自分でネットワークを指定したり元になるAMI(AmazonMachineImage)を指定したりできるみたい
    • この指定するっていうのが、項目があって空欄があって、という時の空欄のことで、その空欄をいかに出すかを理解しようと決めたことでCloudFormationの理解がだいぶ進んだ気がした

f:id:kothiba:20190502233113p:plain

いかにリソースを指定するか

  • で、上記のような見た目にするために必要なのは以下の2つでした
    • parameter
    • metadata

parameter

  • これは項目をつくるもの
    AWSTemplateFormatVersion: 2010-09-09
    Parameters:
      PJPrefix:
        Type: String
      VpcId:
        Type: AWS::EC2::VPC::Id
        Default: ""
      SubnetId:
        Type: AWS::EC2::Subnet::Id
        Default: ""
      KeyPairName:
        Type: AWS::EC2::KeyPair::KeyName
        Default: ""
      EC2InstanceName:
        Type: String
        Default: ""
      EC2InstanceAMI:
        Type: String
        Default: ""
      EC2InstanceInstanceType:
        Type: String
        Default: "t2.micro"
      EC2InstanceVolumeType:
        Type: String
        Default: "gp2"
      EC2InstanceVolumeSize:
        Type: String
        Default: "30"
      SSHAccessSourceIP:
        Type: String

metadata

  • これはparameterとセットなのかなくらいにしか意識していなかったですが、よくよく調べてみると、このmetadataの順序でparameterが表示されるという仕組みでした。metadata大事!
   Metadata:
   "AWS::CloudFormation::Interface":
     ParameterGroups:
       - Label:
           default: "Project Name Prefix"
         Parameters:
           - PJPrefix
       - Label:
           default: "Network Configuration"
         Parameters:
           - VpcId
           - SubnetId
       - Label:
           default: "EC2Instance Configuration"
         Parameters:
           - KeyPairName
           - EC2InstanceName
           - EC2InstanceAMI
           - EC2InstanceInstanceType
           - EC2InstanceVolumeType
           - EC2InstanceVolumeSize
           - SSHAccessSourceIP
     ParameterLabels:
       VpcId:
         default: "VpcId"
       SubnetId:
         default: "SubnetId"
       KeyPairName:
         default: "KeyPairName"
       EC2InstanceName:
         default: "EC2 Name"
       EC2InstanceAMI:
         default: "EC2 AMI"
       EC2InstanceInstanceType:
         default: "EC2 InstanceType"
       EC2InstanceVolumeType:
         default: "EC2 VolumeType"
       EC2InstanceVolumeSize:
         default: "EC2 VolumeSize"
       SSHAccessSourceIP:
         default: "SSH AccessSourceIP"

必要なリソースができたらあとは定義していくのみ

  • 個人的に作る必要はあったのは、ECSでした
    • しかもFargateだった

その他、組み込み関数について

Fn::Join

  • 特定の区切り文字で区切って文字列をつなげる
  • AWSのリソースはいい感じに文字を繋げないと定義できないことがあるのでそういった時に利用する
  • 個人的な感覚としては、Outputsでエクスポートした値を利用するときに以下のようにImportValueと一緒に使うことが多い気がする
  - Fn::Join:
  - ""
  - - Fn::ImportValue: !Sub s3-${Env}-DataBucketArn
    - /*

Fn::Sub

  • 変数と普通の文字列を接続する
  • これはCloudFormationで作成したリソースを一意の名前にしたいときに変数を頭に付けるみたいな手法がよく使われていて、その際に利用する
    Profile:
        Type: AWS::IAM::InstanceProfile
        Properties:
          Path: "/"
          Roles: 
            - !Ref IAMRole  
          InstanceProfileName: !Sub "${PJPrefix}-${EC2InstanceName}-profile"
  • {PJPrefix}と${EC2InstanceName}に入れた値をここでprofileという文字列に繋げている

Fn::Ref

  • 値を参照する
  • これは一番わかり易いものかもしれない
    PublicSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
        SubnetId: !Ref PublicSubnet
        RouteTableId: !Ref PublicRouteTable
  • subnetのidはpublicsubnetとして定義したサブネットのidだよってことで!Ref(Fn::Refの短縮形)を利用している

とりあえずは

  • これくらいの知識でも十分にCloudFormationを読み書きできるようになったと感じています。
  • というか一ヶ月丸々と調査と勉強の時間を与えてもらえたのが大きい。そして業務の一環としてやっていると妥協もできないので、良い緊張感となってより身についた
  • あとはアウトプットとして、1日の成果をWikiにアップするという自分の中での縛りを作っていたのでアウトプットまでを一連の流れに組み込めたのはとても良かった