CloudFormationでリソース作ってみる

AWS

前提

VSCodeの拡張機能である「CloudFormation」が入っている事
任意のフォルダを用意、開いて、そこにymlファイルを用意。ここではcloudformation.ymlとする。

テンプレートについて

全体像

全体像はこれだが、Resourcesのみが必須項目。他は任意やオプションの部分である。

{
  "AWSTemplateFormatVersion" : "version date",

  "Description" : "JSON string",

  "Metadata" : {
    template metadata
  },

  "Parameters" : {
    set of parameters
  },
  
  "Rules" : {
    set of rules
  },

  "Mappings" : {
    set of mappings
  },

  "Conditions" : {
    set of conditions
  },

  "Transform" : {
    set of transforms
  },

  "Resources" : {
    set of resources
  },
  
  "Outputs" : {
    set of outputs
  }
}

Resourcesについて

こんな構成

Resources:
  Logical ID:
    Type: Resource type
    Properties:
      Set of properties

Logical ID(論理ID)
 任意の論理ID 例) mrb-ec2
リソースタイプ:
 AWS::EC2::Instance等。ドキュメントを参照
プロパティ:
 リソースの詳細情報。AZやセキュリティグループなど。ドキュメント参照。

ドキュメントはこちら。

AWS::EC2::Instance - AWS CloudFormation
Specifies an EC2 instance.

リソースタイプの一覧と、プロパティ内の必須項目が記載されている。

留意事項

以下の様にセキュリティグループを作成する際など、IpProtocolの前に-(ハイフン)を入れる必要があるが、これはAWSのドキュメントには載っていない。YAMLの構文の話で配列を示すものだから。
ドキュメント通りに記載してもエラーになる場合はYAMLの構文に関する記事をググるのが良さそう。

      SecurityGroupIngress:
        - IpProtocol: tcp
          CidrIp: 0.0.0.0/0
          FromPort: 22

リソース作成の流れ

前提事項の拡張機能が入っている場合、VSCode内で startと打ってtabキーを押すと全体の骨組みを
提示してくれる。

さらに、リソースの部分にec2と打つと候補が現れるので選択。

すると適当な論理IDによるプロパティの枠組みが現れる。

今回は以下のVPC作成をする

AWSTemplateFormatVersion: 2010-09-09
Resources: 
  myVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: my-vpc-fromCF

作成したファイルをCloudFormationのメニューでアップロード

スタック名を適当に指定する

スタックオプション、詳細オプションは変更無し
作成が進行中から完了と遷移する。

作成できた

タグ内にスタック名なども表示されている

スタック作成時の考慮事項

Ref関数

同一テンプレート内のみの機能。!Refと指定すると、定義済みの論理名を利用できる。

AWSTemplateFormatVersion: 2010-09-09
Resources: 
  MrbVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: mrb-vpc-cf

#パブリックサブネット
  Mrbpubsub1a:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: "ap-northeast-1a"
      VpcId: !Ref MrbVPC
      #組み込み関数RefでVPCの論理名を取得
      CidrBlock: 10.0.1.0/24
      Tags:
        - Key: Name
          Value: Mrb-pub-sub-1a

クロススタック構成

EC2、RDSなどを含むスタック、セキュリティグループ、IAMロールなどを含むスタック、VPC、サブネットなどを含むスタックはそれぞれ別々のスタックで管理する事が推奨されている。
スタックが別のスタックを参照した構成をクロススタック参照という。
既に作成したVPC、サブネット、セキュリティグループを含むスタックとは別にEC2のみのスタックを作成し、クロススタックの構成とする。

クロススタック参照

リソースを別のリソースで参照する場合、同一スタック内なら”Ref!” で完結するが、
別スタックに参照させる場合、”Outputs:“にどのリソースをどういう名前で参照させるかを定義する。
参照先では “ImportValue” で 定義した名前を指定する。

Network.yml

#パブリックサブネット
  Mrbpubsub1a:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: "ap-northeast-1a"
      #組み込み関数RefでVPCの論理名を取得
      VpcId: !Ref MrbVPC
      CidrBlock: 10.0.1.0/24
      Tags:
        - Key: Name
          Value: Mrb-pub-sub-1a

Outputs:
  Subnet1:
    #定義済み論理ID
    Value: !Ref Mrbpubsub1a
    #別テンプレで参照する際の名前
    Export: 
      Name: Subnet1Name

・ec2.yml

AWSTemplateFormatVersion: 2010-09-09
Resources: 
  myEC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      KeyName: mrb-key
      #AMIのカタログから指定(64ビット(x86))
      ImageId: ami-079cd5448deeace01
      InstanceType: t2.micro
      Monitoring: false
      SecurityGroupIds:
        - !ImportValue SG1Name
      SubnetId: !ImportValue Subnet1Name
      Tags:
        - Key: Name
          Value: CF-web1

○補足
以下の様に、”GetAtt”を利用する事でも出力が可能。これはセキュリティグループに紐づくVPCのIDをSubnet1Name-VpcIDとして出力

Outputs:
  Subnet1:
    #定義済み論理ID
    Value: !GetAtt Mrbpubsub1a.VpcId
    #別テンプレで参照する際の名前
    Export: 
      Name: Subnet1Name-VpcID

GetAttで利用出来る戻り値は公式に記載されている

AWS::EC2::SecurityGroup - AWS CloudFormation
If you do not specify an egress rule, we add egress rules that allow IPv4 and IPv6 traffic on all ports and protocols to...

スタックを作成する

目指す構成

パブリックサブネット2つ、プライベートサブネット2つ
パブリックサブネット内にApacheとMysqlをインストールしたEC2設置
プライベートサブネットにシングル構成でMySQLのRDSを設置。
EC2からRDSにSSH接続できる状態。

作成したテンプレ

Network.yml

AWSTemplateFormatVersion: 2010-09-09
Resources: 
  MrbVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: mrb-vpc-cf

#インターネットゲートウェイ
  igwName:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: mrb-igw

#インターネットゲートウェイアタッチ
  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref MrbVPC
      InternetGatewayId: !Ref igwName

#ルートテーブル
  routeTableName:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref MrbVPC
      Tags:
        - Key: Name
          Value: mrb_rt

#ルートテーブルとサブネットの紐付け
  routeTableAssocName:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref Mrbpubsub1a
      RouteTableId: !Ref routeTableName

#ルートテーブル
  routeName:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref routeTableName
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref igwName

#パブリックサブネット
  Mrbpubsub1a:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: "ap-northeast-1a"
      VpcId: !Ref MrbVPC
      CidrBlock: 10.0.1.0/24
      Tags:
        - Key: Name
          Value: Mrb-pub-sub-1a
      MapPublicIpOnLaunch: "true" 

  Mrbpubsub1c:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: "ap-northeast-1c"
      VpcId: !Ref MrbVPC
      CidrBlock: 10.0.2.0/24
      Tags:
        - Key: Name
          Value: Mrb-pub-sub-1c
      MapPublicIpOnLaunch: "true"

#プライベートサブネット
  Mrbprisub1a:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: "ap-northeast-1a"
      VpcId: !Ref MrbVPC
      CidrBlock: 10.0.3.0/24
      Tags:
        - Key: Name
          Value: Mrb-pri-sub-1a

  Mrbprisub1c:
    Type: AWS::EC2::Subnet
    Properties:
      AvailabilityZone: "ap-northeast-1c"
      VpcId: !Ref MrbVPC
      CidrBlock: 10.0.4.0/24
      Tags:
        - Key: Name
          Value: Mrb-pri-sub-1c

#Webサーバ用のセキュリティグループ
  MrbWebSG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: mrb-web-sg
      GroupDescription: mrb-web-sg
      VpcId: !Ref MrbVPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          CidrIp: 0.0.0.0/0
          FromPort: 22
          ToPort: 22
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: SG-web

#DB用のセキュリティグループ
  DBsg:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: mrb-db-sg
      VpcId: !Ref MrbVPC
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: 3306
        ToPort: 3306
        SourceSecurityGroupId: !Ref MrbWebSG
      Tags:
        - Key: Name
          Value: mrb-db-sg

Outputs:
  Subnet1:
    Value: !Ref Mrbpubsub1a
    Export: 
      Name: Subnet1Name

  OutputPubSubID:
    Value: !Ref Mrbpubsub1a
    Export:
      Name: ExportPubSub1a

  WebSGID:
    Value: !Ref MrbWebSG
    Export:
      Name: ExportWebSGID

  DBSG:
    Value: !Ref DBsg
    Export: 
      Name: MrbDBSG

  PriSuba:
    Value: !Ref Mrbprisub1a
    Export:
      Name: ExportPriSub1a

  PriSubc:
    Value: !Ref Mrbprisub1c
    Export:
      Name: ExportPriSub1c

EC2.yml

AWSTemplateFormatVersion: 2010-09-09
Resources: 
  myEC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      KeyName: mrb-key
      #AMIのカタログから指定
      ImageId: ami-0adac58024a7f03bb
      InstanceType: t2.micro
      Monitoring: false
      Tags:
        - Key: Name
          Value: CF-web1
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash
          yum install -y httpd
          systemctl start httpd
          systemctl enable httpd
          sudo yum -y install mysql

RDS.yml

AWSTemplateFormatVersion: 2010-09-09

Parameters:
  DBInstanceClass:
    Description: RDS instance type
    Type: String
    Default: db.t2.micro
    AllowedValues:
      - db.t2.micro
      - db.t2.small
      - db.t2.medium
    ConstraintDescription: Must be a valid RDS instance type from the allowed list.
  MasterUsername:
    Description: The master username for the DB instance.
    Type: String
    Default: admin
  MasterUserPassword:
    Description: The password for the master username.
    Type: String
    NoEcho: True

Resources: 
  rdsDBInstance:
    Type: AWS::RDS::DBInstance
    Properties:
      Engine: Mysql
      AllocatedStorage: 20
      DBInstanceClass: !Ref DBInstanceClass
      AvailabilityZone: ap-northeast-1a
      BackupRetentionPeriod: 0
      DBInstanceIdentifier: mrb-db1
      DBName: db1
      MasterUsername: !Ref MasterUsername
      MasterUserPassword: !Ref MasterUserPassword
      MultiAZ: false
      PubliclyAccessible: false
      StorageEncrypted: false
      VPCSecurityGroups:
        - !ImportValue MrbDBSG
      Tags:
        - Key: Name
          Value: mrb-db
      DBSubnetGroupName: !Ref MyDBSubnetGroup
  MyDBSubnetGroup:
      Type: AWS::RDS::DBSubnetGroup
      Properties:
        DBSubnetGroupDescription: rdssubnetgp
        SubnetIds:
          - !ImportValue ExportPriSub1a
          - !ImportValue ExportPriSub1c
        Tags:
        - Key: keyname
          Value: value

思ったこと

・設定する上ではいかに適切なドキュメントに辿りつけるかが鍵。
 意外に苦戦する。最悪ブログを参考にする。
・ymlの構文的に少しでもタブがずれてるとエラーになってしまう。
・VSCodeの拡張機能で表示された文言が大文字小文字が公式と違っているケースもある。
・リソースのIDを必要とする場合、論理IDでも物理IDでも良い。論理を指定した場合は内部的に物理に置き換わる。
・何故かテンプレに問題無いのに取り込むとエラーになるケースが稀にある。一回スタックを削除して再度取り込むべし。
・手動で取り込むよりCLIでの操作が楽。VSCode上でテンプレを表示させつつ、VSCodeの下部にローカルPCのターミナルを表示させてCLIを実行するとやりやすい。
・RDSなどのリソース作成に時間がかかるものは別テンプレが良いかも。
・各種論理名は適当に付けると破滅する。
・謎のエラーだとか疑問はChatGPTへ。

タイトルとURLをコピーしました