1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

AWS CloudFormationでCloudFormationエンドポイントを使用したEC2インスタンスを構築しよう

Last updated at Posted at 2020-10-28

はじめに

AWS CloudFormationを利用してVPC構築のテンプレートのサンプルです。

テンプレートの概要が分からない場合は、はじめてのAWS CloudFormationテンプレートを理解するを参考にしてください。

コードはGitHubにもあります。

今回は、akane というシステムの all 環境を想定しています。
同じ構成で違う環境を作成する場合は、{環境名}-parameters.jsonを別途作成します。

ディレクトリ構成
akane-cfn (システム)
  ├─ network (スタック)
  |   ├─ network.yml (CFnテンプレート)
  |   └─ all-parameters.json (all 環境のパラメータ)
  ├─ instance-cfn (スタック)
  |   ├─ instance-cfn.yml (CFnテンプレート)
  |   └─ all-parameters.json (all 環境のパラメータ)
  └─ endpoint-cfn (スタック)
      ├─ endpoint-cfn.yml (CFnテンプレート)
      └─ all-parameters.json (all 環境のパラメータ)

AWS リソース構築内容

  1. networkスタック

    • VPC (10.0.0.0/16)
    • Publicサブネット (10.0.1.0/24)
    • インターネットゲートウェイ
    • Publicルートテーブル
    • Elastic IP (NATゲートウェイ用)
  2. instance-cfnスタック

    • セキュリティグループ (インスタンス)
    • IAMロール (AdministratorAccess)
    • インスタンスプロファイル
    • EC2インスタンス (Amazon Linux 2)
  3. endpoint-cfnスタック

    • セキュリティグループ (エンドポイント:アウトバウンドにインスタンスのセキュリティグループを指定)
    • セキュリティグループのアウトバウンド (インスタンス:アウトバウンドにエンドポイントのセキュリティグループを指定)
    • CloudFormationエンドポイント (Interface)

実行環境の準備

AWS CloudFormationを動かすためのAWS CLIの設定を参考にしてください。

AWS リソース構築手順

  1. 事前にEC2のキーペアを作成し、akane-cfn/instance-cfn/all-parameters.jsonKeyNameに記載します。

  2. 下記を実行してスタックを作成

    ./create_stacks.sh
    
  3. 下記を実行してスタックを削除

    ./delete_stacks.sh
    

構築テンプレート

1. networkスタック

network.yml
AWSTemplateFormatVersion: 2010-09-09
Description: Network For Akane

# Metadata:

Parameters:
  SystemName:
    Type: String
    AllowedPattern: '[a-zA-Z0-9-]*'
  EnvType:
    Description: Environment type.
    Type: String
    AllowedValues: [all, dev, stg, prod]
    ConstraintDescription: must specify all, dev, stg, or prod.
  VPCCidrBlock:
    Type: String
    AllowedPattern: (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/16
  PublicSubnet:
    Type: String
    AllowedPattern: (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/24

Mappings: 
  AzMap: 
    ap-northeast-1:
      1st: ap-northeast-1a
      2nd: ap-northeast-1c
      3rd: ap-northeast-1d

# Conditions

# Transform

Resources:
  # VPC作成
  akaneVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VPCCidrBlock
      EnableDnsSupport: true
      EnableDnsHostnames: true
      InstanceTenancy: default
      Tags:
        - Key: Name
          Value: !Sub
          - ${SystemName}-${EnvType}-vpc
          - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
        - Key: SystemName
          Value: !Ref SystemName
        - Key: EnvType
          Value: !Ref EnvType

###########################################################################
# Publicサブネット関連
###########################################################################
  # Publicサブネット作成
  akanePublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref akaneVPC
      CidrBlock: !Ref PublicSubnet
      AvailabilityZone: !FindInMap [AzMap, !Ref AWS::Region, 1st]
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub
          - ${SystemName}-${EnvType}-public-subnet
          - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
        - Key: SystemName
          Value: !Ref SystemName
        - Key: EnvType
          Value: !Ref EnvType
  # インターネットゲートウェイ作成
  akaneInternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub
          - ${SystemName}-${EnvType}-igw
          - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
        - Key: SystemName
          Value: !Ref SystemName
        - Key: EnvType
          Value: !Ref EnvType
  # インターネットゲートウェイをVPCにアタッチ
  akaneVPCGatewayAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref akaneVPC
      InternetGatewayId: !Ref akaneInternetGateway
  # Publicルートテーブル作成
  akanePublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref akaneVPC
      Tags:
        - Key: Name
          Value: !Sub
          - ${SystemName}-${EnvType}-public-route
          - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
        - Key: SystemName
          Value: !Ref SystemName
        - Key: EnvType
          Value: !Ref EnvType
  # Publicルートテーブル サブネット関連付け
  akanePublicSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref akanePublicSubnet
      RouteTableId: !Ref akanePublicRouteTable
  # Publicルートテーブル ルートにインターネットゲートウェイを関連付ける
  akanePublicRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref akanePublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref akaneInternetGateway

Outputs:
  akaneVPC:
    Value: !Ref akaneVPC
    Export:
      Name: !Sub
        - ${SystemName}-${EnvType}-vpc
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
  akanePublicSubnet:
    Value: !Ref akanePublicSubnet
    Export:
      Name: !Sub
        - ${SystemName}-${EnvType}-public-subnet
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
  akanePublicRouteTable:
    Value: !Ref akanePublicRouteTable
    Export:
      Name: !Sub
        - ${SystemName}-${EnvType}-public-route-table
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
all-parameters.json
{
    "Parameters": [
        {
            "ParameterKey": "SystemName",
            "ParameterValue": "akane-cfn"
        },
        {
            "ParameterKey": "EnvType",
            "ParameterValue": "all"
        },
        {
            "ParameterKey": "VPCCidrBlock",
            "ParameterValue": "10.10.0.0/16"
        },
        {
            "ParameterKey": "PublicSubnet",
            "ParameterValue": "10.10.1.0/24"
        }
    ]
}

2. EC2インスタススタック

instance-cfn.yml
AWSTemplateFormatVersion: 2010-09-09
Description: Instance For Akane

# Metadata:

Parameters:
  SystemName:
    Type: String
    AllowedPattern: '[a-zA-Z0-9-]*'
  EnvType:
    Description: Environment type.
    Type: String
    AllowedValues: [all, dev, stg, prod]
    ConstraintDescription: must specify all, dev, stg, or prod.
  ImageId:
    Type: String
    AllowedPattern: 'ami-[a-zA-Z0-9-]*'
  InstanceType:
    Type: String
    AllowedPattern: '^[a-z][1-9][.][a-z0-9]+$'
  KeyName:
    Type: String

Mappings: 
  AzMap: 
    ap-northeast-1:
      1st: ap-northeast-1a
      2nd: ap-northeast-1c
      3rd: ap-northeast-1d

# Conditions

# Transform

Resources:
  # セキュリティグループ(EC2インスタンス)作成
  akaneSecurityGroupInstance:
    Type: AWS::EC2::SecurityGroup
    Properties: 
      GroupDescription: !Sub
        - ${SystemName}-${EnvType}-sg-instance
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      GroupName: !Sub
        - ${SystemName}-${EnvType}-sg-instance
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      SecurityGroupIngress: 
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0
      SecurityGroupEgress: 
        - IpProtocol: tcp
          FromPort: 0
          ToPort: 65535
          CidrIp: 0.0.0.0/0
      Tags: 
        - Key: Name
          Value: !Sub
            - ${SystemName}-${EnvType}-sg-instance
            - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
        - Key: SystemName
          Value: !Ref SystemName
        - Key: EnvType
          Value: !Ref EnvType
      VpcId:
        Fn::ImportValue: !Sub
          - ${SystemName}-${EnvType}-vpc
          - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}

  # IAMロールを作成
  akaneRole:
    Type: AWS::IAM::Role
    Properties: 
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - sts:AssumeRole
      Description: !Sub
        - ${SystemName}-${EnvType}-role
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      ManagedPolicyArns: 
        - arn:aws:iam::aws:policy/AdministratorAccess
      RoleName: !Sub
        - ${SystemName}-${EnvType}-role
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      Tags: 
        - Key: Name
          Value: !Sub
          - ${SystemName}-${EnvType}-role
          - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
        - Key: SystemName
          Value: !Ref SystemName
        - Key: EnvType
          Value: !Ref EnvType

  # インスタンスプロファイル作成
  akaneInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: "/"
      Roles:
      - !Ref akaneRole

  # EC2インスタンス作成
  akaneInstance:
    Type: AWS::EC2::Instance
    Properties: 
      AvailabilityZone: !FindInMap [AzMap, !Ref AWS::Region, 1st]
      DisableApiTermination: false
      IamInstanceProfile:
        !Ref akaneInstanceProfile      
      ImageId: !Ref ImageId
      InstanceInitiatedShutdownBehavior: stop
      InstanceType: !Ref InstanceType
      KeyName: !Ref KeyName
      Monitoring: false
      SecurityGroupIds: 
        - !Ref akaneSecurityGroupInstance
      SubnetId:
        Fn::ImportValue: !Sub
          - ${SystemName}-${EnvType}-public-subnet
          - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      Tags: 
        - Key: Name
          Value: !Sub
          - ${SystemName}-${EnvType}-instance
          - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
        - Key: SystemName
          Value: !Ref SystemName
        - Key: EnvType
          Value: !Ref EnvType
      Tenancy: default
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash
          yum update -y
          yum install git -y

          cd /home/ec2-user

          # ec2-user bashrc設定
          cat <<EOS | sudo tee -a /home/ec2-user/.bashrc
          SERVERNAME=${SystemName}-${EnvType}
          export PS1='\[\e[1;36m\][\e[1;33m\]\u@\[\e[1;32m\]\${!SERVERNAME} \w\[\e[1;36m\]]$\[\e[00m\] '
          export AWS_DEFAULT_REGION=${AWS::Region}
          EOS

          # root bashrc設定
          cat <<EOS | sudo tee /root/.bashrc
          export PS1="\[\e[1;36m\][\u@\h \W]\$\[\e[00m\] "
          EOS

  # EIP作成およびEC2インスタンスへのアタッチ
  akaneEIP:
    Type: AWS::EC2::EIP
    Properties: 
      Domain: vpc
      InstanceId: !Ref akaneInstance
      Tags: 
        - Key: Name
          Value: !Sub
          - ${SystemName}-${EnvType}-eip
          - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
        - Key: SystemName
          Value: !Ref SystemName
        - Key: EnvType
          Value: !Ref EnvType

Outputs:
  akaneSecurityGroupInstance:
    Value: !Ref akaneSecurityGroupInstance
    Export:
      Name: !Sub
        - ${SystemName}-${EnvType}-sg-instance
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}

all-parameters.json
{
    "Parameters": [
        {
            "ParameterKey": "SystemName",
            "ParameterValue": "akane-cfn"
        },
        {
            "ParameterKey": "EnvType",
            "ParameterValue": "all"
        },
        {
            "ParameterKey": "ImageId",
            "ParameterValue": "ami-0ce107ae7af2e92b5"
        },
        {
            "ParameterKey": "InstanceType",
            "ParameterValue": "t2.micro"
        },
        {
            "ParameterKey": "KeyName",
            "ParameterValue": "akane"
        }
    ],
    "Capabilities": [
        "CAPABILITY_NAMED_IAM"
    ]
}

3. CloudFormationエンドポイントスタック

instance-cfn.yml
AWSTemplateFormatVersion: 2010-09-09
Description: Endpoint For Akane

# Metadata:

Parameters:
  SystemName:
    Type: String
    AllowedPattern: '[a-zA-Z0-9-]*'
  EnvType:
    Description: Environment type.
    Type: String
    AllowedValues: [all, dev, stg, prod]
    ConstraintDescription: must specify all, dev, stg, or prod.

Mappings: 
  AzMap: 
    ap-northeast-1:
      1st: ap-northeast-1a
      2nd: ap-northeast-1c
      3rd: ap-northeast-1d

# Conditions

# Transform

Resources:
  # セキュリティグループ(エンドポイント)作成
  akaneSecurityGroupEndpoint:
    Type: AWS::EC2::SecurityGroup
    Properties: 
      GroupDescription: !Sub
        - ${SystemName}-${EnvType}-sg-endpoint-cfn
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      GroupName: !Sub
        - ${SystemName}-${EnvType}-sg-endpoint-cfn
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      SecurityGroupIngress: 
        - IpProtocol: tcp
          FromPort: 0
          ToPort: 65535
          SourceSecurityGroupId:
            Fn::ImportValue: !Sub
              - ${SystemName}-${EnvType}-sg-instance
              - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      Tags: 
        - Key: Name
          Value: !Sub
            - ${SystemName}-${EnvType}-sg-endpoint-cfn
            - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
        - Key: SystemName
          Value: !Ref SystemName
        - Key: EnvType
          Value: !Ref EnvType
      VpcId:
        Fn::ImportValue: !Sub
          - ${SystemName}-${EnvType}-vpc
          - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}

  #VPCエンドポイント作成
  akaneCloudFormationEndpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      PrivateDnsEnabled: true
      SecurityGroupIds:
        - !Ref akaneSecurityGroupEndpoint
      ServiceName: !Sub com.amazonaws.${AWS::Region}.cloudformation
      SubnetIds:
        - Fn::ImportValue: !Sub
          - ${SystemName}-${EnvType}-public-subnet
          - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      VpcEndpointType: Interface
      VpcId:
        Fn::ImportValue: !Sub
          - ${SystemName}-${EnvType}-vpc
          - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}

  # セキュリティグループ(EC2インスタンス)のアウトバウンドルール作成
  akaneSecurityGroupEgressInstance:
    Type: AWS::EC2::SecurityGroupEgress
    Properties: 
      Description:  !Sub
        - ${SystemName}-${EnvType}-sg-instance-cfn
        - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}
      DestinationSecurityGroupId: !Ref akaneSecurityGroupEndpoint
      IpProtocol: -1
      GroupId:
        Fn::ImportValue: !Sub
          - ${SystemName}-${EnvType}-sg-instance
          - {SystemName: !Ref SystemName, EnvType: !Ref EnvType}

# Outputs:

all-parameters.json
{
    "Parameters": [
        {
            "ParameterKey": "SystemName",
            "ParameterValue": "akane-cfn"
        },
        {
            "ParameterKey": "EnvType",
            "ParameterValue": "all"
        }
    ]
}

4. 実行ファイル

create_stacks.sh
#!/bin/sh
SYSTEM_NAME=akane-cfn
ENV_TYPE=all

create_stack () {
    aws cloudformation create-stack \
    --stack-name ${SYSTEM_NAME}-${ENV_TYPE}-${STACK_NAME} \
    --template-body file://./${SYSTEM_NAME}/${STACK_NAME}/${STACK_NAME}.yml \
    --cli-input-json file://./${SYSTEM_NAME}/${STACK_NAME}/${ENV_TYPE}-parameters.json \
    --capabilities CAPABILITY_NAMED_IAM

    aws cloudformation wait stack-create-complete \
    --stack-name ${SYSTEM_NAME}-${ENV_TYPE}-${STACK_NAME}
}

create_stack network
create_stack instance-cfn '--capabilities CAPABILITY_NAMED_IAM'
create_stack endpoint-cfn
delete_stacks.sh
#!/bin/sh
SYSTEM_NAME=akane-cfn
ENV_TYPE=all

delete_stack () {
    STACK_NAME=$1
    aws cloudformation delete-stack \
    --stack-name ${SYSTEM_NAME}-${ENV_TYPE}-${STACK_NAME}

    aws cloudformation wait stack-delete-complete \
    --stack-name ${SYSTEM_NAME}-${ENV_TYPE}-${STACK_NAME}
}

delete_stack endpoint-cfn
delete_stack instance-cfn
delete_stack network
1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?