Help us understand the problem. What is going on with this article?

CloudFormation超入門

What's CloudFormation?

AWSのクラウドリソースをコードで表現してプロビジョニングできるようにする仕組み。
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/Welcome.html

要するに Infrastructure as Code(IaC)。記述形式は JSON or YAML。
似たようなサービスには terraform, cdk などがある。

利点

  • インフラストラクチャをコードとして扱える
  • コードとして扱えればメンテナンス性が向上する
  • ちょっと頑張ればデプロイを自動化できる

主なテンプレート解説

超入門なので一部割愛。
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/template-anatomy.html

# フォーマットのバージョン(yyyy-mm-dd)を指定する
AWSTemplateFormatVersion: "version date"

# Stack 作成・更新時に受け取る値
Parameters:
  set of parameters

# リソースの定義(EC2,RDS...etc
Resources:
  set of resources

# リソースの生成結果等をエクスポートする
Outputs:
  set of outputs

Sample

事前準備

あとで動作確認するためにEC2にSSHしたいので KeyPair を事前に作っておくこと。
https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/cli-services-ec2-keypairs.html

Stack作るときにも参照する。

やること

  • VPC作成
  • VPC内にEC2を立ち上げてインターネットに接続できるようにする
  • EC2に強めの Instance Profile をアタッチする
  • なんとなくS3

モノリシックな定義

※!Ref と Ref: が混在してるけど同じなので気にしない。

main.yaml
AWSTemplateFormatVersion: "2010-09-09"

Parameters:
  KeyPairParam:
    Type: AWS::EC2::KeyPair::KeyName
    Default: thorikoshi

  ImageId:
    Type: AWS::EC2::Image::Id
    Default: ami-0064e711cbc7a825e

  InstanceTypeParam:
    Type: String
    Default: t2.micro
    AllowedValues:
      - t2.micro
      - m1.small
      - m1.large
    Description: Enter t2.micro, m1.small, or m1.large. Default is t2.micro.

  S3BucketNameParam:
    Type: String
    Default: thorikoshi

Resources:
  MyVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: "10.0.0.0/16"
      InstanceTenancy: "default"
      Tags:
        - Key: Name
          Value: MyVPC

  MyInternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: "MyInternetGateway"

  MyVpcGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId: !Ref MyInternetGateway
      VpcId: !Ref MyVPC

  MyRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref MyVPC
      Tags:
        - Key: Name
          Value: "MyRouteTable"

  MyRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref MyRouteTable
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId: !Ref MyInternetGateway

  MySubnet:
    Type: AWS::EC2::Subnet
    Properties:
      MapPublicIpOnLaunch: True
      AvailabilityZone: "ap-northeast-1a"
      CidrBlock: "10.0.1.0/24"
      VpcId: !Ref MyVPC
      Tags:
        - Key: Name
          Value: MySubnet

  MySubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref MyRouteTable
      SubnetId: !Ref MySubnet

  MySecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: MySecurityGroup
      GroupName: MySecurityGroup
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0
      SecurityGroupEgress:
        - IpProtocol: -1
          FromPort: -1
          ToPort: -1
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: MySecurityGroup
      VpcId: !Ref MyVPC

  MyRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: MyRoleToInstanceProfile
      AssumeRolePolicyDocument:
        Statement:
          - Effect: "Allow"
            Action: "sts:AssumeRole"
            Principal:
              Service:
                - "ec2.amazonaws.com"
      Policies:
        - PolicyName: MyPolicy
          PolicyDocument:
            Statement:
              - Sid: MyUserStmt
                Effect: Allow
                NotAction: "iam:*"
                Resource: "*"

  MyAccessPolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: MyAccessPolicy
      PolicyDocument:
        Statement:
          - Sid: MyRoleStmt
            Effect: Allow
            Action: "*"
            Resource: "*"
      Roles:
        - Ref: MyRole

  MyInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: "/"
      Roles:
        - Ref: MyRole

  MyInstance:
    Type: AWS::EC2::Instance
    Properties:
      IamInstanceProfile: !Ref MyInstanceProfile
      ImageId:
        Ref: ImageId
      InstanceType:
        Ref: InstanceTypeParam
      SecurityGroupIds:
        - Ref: MySecurityGroup
      SubnetId: !Ref MySubnet
      KeyName:
        Ref: KeyPairParam
      Tags:
        - Key: Name
          Value: MyInstance

  MyEIP:
    Type: AWS::EC2::EIP
    Properties:
      InstanceId: !Ref MyInstance

  MyS3:
    Type: AWS::S3::Bucket
    Properties:
      BucketName:
        Ref: S3BucketNameParam

Stack の作成

$ aws cloudformation create-stack \
    --stack-name {{ Your stack name }} --template-body file://$PWD/main.yaml \
    --parameters ParameterKey=S3BucketNameParam,ParameterValue={{ Your  bucket name }}
    --capabilities CAPABILITY_NAMED_IAM

もしくは

aws cloudformation deploy \
    --template $PWD/main.yaml \
    --stack-name {{ Your stack name }} \
    --capabilities CAPABILITY_NAMED_IAM

もしくは Web コンソールから...。

動作確認

$ ssh -i /path/to/secret-key.pem ec2-user@{{ Created EIP }}

$ aws s3 ls
// Output your backet name

Stack 間のリソース参照

Cross Stack Reference

Outputs で Export した結果を参照する。
以下は Export した IAM Role を使用して Instance Profile を定義する Stack 間参照

IAM Role の Stack

role.yaml
AWSTemplateFormatVersion: "2010-09-09"

Resources:
  MyRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: MyRole
      AssumeRolePolicyDocument:
        Statement:
          - Effect: "Allow"
            Action: "sts:AssumeRole"
            Principal:
              Service:
                - "ec2.amazonaws.com"
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonRoute53FullAccess
        - arn:aws:iam::aws:policy/AmazonS3FullAccess
  MyFullAccessPolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: MyFullAccessPolicy
      PolicyDocument:
        Statement:
          - Sid: MyRoleStmt
            Effect: Allow
            Action: "*"
            Resource: "*"
      Roles:
        - Ref: MyRole

# Instance Profile の Name を Export
Outputs:
  MyRoleName:
    Description: The name of My Role
    Value:
      Ref: MyRole
    Export:
      Name: MyRole:NAME

Instance Profile の Stack

ec2.yaml
AWSTemplateFormatVersion: "2010-09-09"

Resources:
  # !importValue KeyName で参照
  MyProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: "/"
      Roles:
        - !ImportValue MyRole:NAME

Note

試してないがパラメータストアにあらかじめ登録された値を参照するという作戦もあるらしい。
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/dynamic-references.html

テンプレートの再利用

AWS::CloudFormation::Stack

AWS::CloudFormation::Stack というリソース定義がある。
https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-properties-stack.html

S3にテンプレート配置しておいてそれを Stack の定義として再利用できる仕組み。
分割した Stack の統合が可能になる。

あと始末

作成した Stack はいらないなら消しましょう(差もないと請求が来るでしょう)。

$ aws cloudformation delete-stack --stack-name {{ Your stack name }}

終わり

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away