ハンズオン(簡易版): CFnテンプレート入門(EC2::VolumeAttachment)

事前作業2.6. CFnリソースファイルの作成 (EC2::Instance タグ名指定(スタック名) AssociatePublicIpAddress Instance0)

手順の目的 [why]

リソース(Instance0)のCloudFormationリソースファイルを作成します。

設定値の指定

設定値の指定

手順に必要な設定値を変数に格納をします。

0. リージョンの指定

リージョンを指定します。

環境変数の設定:

export AWS_DEFAULT_REGION='ap-northeast-1'

1. CloudFormationリソース名

CloudFormationリソース名を指定します。

変数の設定:

TEMPLATE_CFN_RESOURCE_NAME='Instance0'

2. リソースファイル用ディレクトリ

リソースファイル用ディレクトリを指定します。

変数の設定:

DIR_TEMPLATE_CFN_RESOURCE="${HOME}/environment/conf-handson-cli-cfn-ec2-VolumeAttachment/resources"

ディレクトリが存在することを確認します。

コマンド:

ls -d ${DIR_TEMPLATE_CFN_RESOURCE}

結果(例:存在する場合):

${HOME}/environment/conf-handson-cli-cfn-ec2-VolumeAttachment/resources

存在しない場合は作成します。

コマンド:

mkdir -p ${DIR_TEMPLATE_CFN_RESOURCE}

結果(例):

(出力なし)

3. リソースファイル名

リソースファイル名を指定します。

変数の設定:

FILE_TEMPLATE_CFN_RESOURCE="${DIR_TEMPLATE_CFN_RESOURCE}/${TEMPLATE_CFN_RESOURCE_NAME}.txt" \
  && echo ${FILE_TEMPLATE_CFN_RESOURCE}

結果(例):

${HOME}/environment/conf-handson-cli-cfn-ec2-VolumeAttachment/resources/Instance0.txt

4. 起動時に利用するAMIイメージ名

起動時に利用するAMIイメージ名の指定します。

変数の設定:

EC2_INSTANCE_IMAGE_NAME="amzn2-ami-hvm-2.0.20210721.2-x86_64-gp2"

5. サブネットのタグ名

サブネットのタグ名の指定します。

変数の設定:

TEMPLATE_CFN_RESOURCE_NAME_EC2_SUBNET='Subnet0'

6. セキュリティグループ名

セキュリティグループ名の指定します。

変数の設定:

TEMPLATE_CFN_RESOURCE_NAME_EC2_SECURITY_GROUP='SecurityGroup0'

7. EC2インスタンスのインスタンスタイプ

EC2インスタンスのインスタンスタイプの指定します。

変数の設定:

EC2_INSTANCE_TYPE="t2.micro"

8. ユーザデータファイル

ユーザデータファイルの指定します。

変数の設定:

USER_DATA_NAME='handson-cli-cfn-ec2-VolumeAttachment-ssh_port-userdata'

変数の設定:

DIR_USER_DATA="${HOME}/environment/conf-handson-cli-cfn-ec2-VolumeAttachment"

変数の設定:

FILE_USER_DATA="${DIR_USER_DATA}/${USER_DATA_NAME}.bash" \
  && echo ${FILE_USER_DATA}

結果(例):

${HOME}/environment/conf-handson-cli-cfn-ec2-VolumeAttachment/handson-cli-cfn-ec2-VolumeAttachment-ssh_port-userdata.bash

設定値の確認

各変数に正しい設定値が格納されていることを確認しながら保存します。

変数の確認:

cat << END

  # 0. AWS_DEFAULT_REGION:"ap-northeast-1"
       AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION}"

  # 1. TEMPLATE_CFN_RESOURCE_NAME:"Instance0"
       TEMPLATE_CFN_RESOURCE_NAME="${TEMPLATE_CFN_RESOURCE_NAME}"
  # 2. DIR_TEMPLATE_CFN_RESOURCE:"${HOME}/environment/conf-handson-cli-cfn-ec2-VolumeAttachment/resources"
       DIR_TEMPLATE_CFN_RESOURCE="${DIR_TEMPLATE_CFN_RESOURCE}"
  # 3. FILE_TEMPLATE_CFN_RESOURCE:"${HOME}/environment/conf-handson-cli-cfn-ec2-VolumeAttachment/resources/Instance0.txt"
       FILE_TEMPLATE_CFN_RESOURCE="${FILE_TEMPLATE_CFN_RESOURCE}"
  # 4. EC2_INSTANCE_IMAGE_NAME:"amzn2-ami-hvm-2.0.20210721.2-x86_64-gp2"
       EC2_INSTANCE_IMAGE_NAME="${EC2_INSTANCE_IMAGE_NAME}"
  # 5. TEMPLATE_CFN_RESOURCE_NAME_EC2_SUBNET:"Subnet0"
       TEMPLATE_CFN_RESOURCE_NAME_EC2_SUBNET="${TEMPLATE_CFN_RESOURCE_NAME_EC2_SUBNET}"
  # 6. TEMPLATE_CFN_RESOURCE_NAME_EC2_SECURITY_GROUP:"SecurityGroup0"
       TEMPLATE_CFN_RESOURCE_NAME_EC2_SECURITY_GROUP="${TEMPLATE_CFN_RESOURCE_NAME_EC2_SECURITY_GROUP}"
  # 7. EC2_INSTANCE_TYPE:"t2.micro"
       EC2_INSTANCE_TYPE="${EC2_INSTANCE_TYPE}"
  # 8. FILE_USER_DATA:"${HOME}/environment/conf-handson-cli-cfn-ec2-VolumeAttachment/handson-cli-cfn-ec2-VolumeAttachment-ssh_port-userdata.bash"
       FILE_USER_DATA="${FILE_USER_DATA}"

END

下段の変数が入っていない、もしくは上段と同等の値が入っていない場合は、それぞれの手順番号に戻って変数の設定を行います。

処理の実行

イメージIDを取得します。

コマンド:

EC2_INSTANCE_IMAGE_ID=$( \
  aws ec2 describe-images \
    --filters Name=name,Values="${EC2_INSTANCE_IMAGE_NAME}" \
    --query 'Images[].ImageId' \
    --output text \
) \
  && echo ${EC2_INSTANCE_IMAGE_ID}

結果(例):

ami-09ebacdc178ae23b7

CFnリソースファイルを作成します。

変数の確認:

cat << END

  # FILE_TEMPLATE_CFN_RESOURCE:"${HOME}/environment/conf-handson-cli-cfn-ec2-VolumeAttachment/resources/Instance0.txt"
    FILE_TEMPLATE_CFN_RESOURCE="${FILE_TEMPLATE_CFN_RESOURCE}"
  # TEMPLATE_CFN_RESOURCE_NAME:"Instance0"
    TEMPLATE_CFN_RESOURCE_NAME="${TEMPLATE_CFN_RESOURCE_NAME}"
  # EC2_INSTANCE_IMAGE_ID:"ami-09ebacdc178ae23b7"
    EC2_INSTANCE_IMAGE_ID="${EC2_INSTANCE_IMAGE_ID}"
  # EC2_INSTANCE_TYPE:"t2.micro"
    EC2_INSTANCE_TYPE="${EC2_INSTANCE_TYPE}"
  # TEMPLATE_CFN_RESOURCE_NAME_EC2_SUBNET:"Subnet0"
    TEMPLATE_CFN_RESOURCE_NAME_EC2_SUBNET="${TEMPLATE_CFN_RESOURCE_NAME_EC2_SUBNET}"
  # TEMPLATE_CFN_RESOURCE_NAME_EC2_SECURITY_GROUP:"SecurityGroup0"
    TEMPLATE_CFN_RESOURCE_NAME_EC2_SECURITY_GROUP="${TEMPLATE_CFN_RESOURCE_NAME_EC2_SECURITY_GROUP}"

END

コマンド:

cat << EOF > ${FILE_TEMPLATE_CFN_RESOURCE}
  ${TEMPLATE_CFN_RESOURCE_NAME}:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: ${EC2_INSTANCE_IMAGE_ID}
      InstanceType: ${EC2_INSTANCE_TYPE}
      Tags:
        - Key: Name
          Value: !Ref AWS::StackName
      NetworkInterfaces:
        - DeviceIndex: "0"
          SubnetId: !Ref ${TEMPLATE_CFN_RESOURCE_NAME_EC2_SUBNET}
          GroupSet:
          - !Ref ${TEMPLATE_CFN_RESOURCE_NAME_EC2_SECURITY_GROUP}
          AssociatePublicIpAddress: "true"
EOF
cat ${FILE_TEMPLATE_CFN_RESOURCE}

結果(例):

Instance0:
  Type: AWS::EC2::Instance
    Properties:
      ImageId: ami-09ebacdc178ae23b7
      InstanceType: t2.micro
      Tags:
        - Key: Name
          Value: !Ref AWS::StackName
      NetworkInterfaces:
        - DeviceIndex: "0"
          SubnetId: !Ref Subnet0
          GroupSet:
          - !Ref SecurityGroup0
          AssociatePublicIpAddress: "true"

ユーザーデーターを追記します。

変数の確認:

cat << END

  # FILE_TEMPLATE_CFN_RESOURCE:"${HOME}/environment/conf-handson-cli-cfn-ec2-VolumeAttachment/resources/Instance0.txt"
    FILE_TEMPLATE_CFN_RESOURCE="${FILE_TEMPLATE_CFN_RESOURCE}"
  # FILE_USER_DATA:"${HOME}/environment/conf-handson-cli-cfn-ec2-VolumeAttachment/handson-cli-cfn-ec2-VolumeAttachment-ssh_port-userdata.bash"
    FILE_USER_DATA="${FILE_USER_DATA}"

END

コマンド:

if [ ! $( grep 'UserData:' ${FILE_TEMPLATE_CFN_RESOURCE}) ];then
cat << EOF >> ${FILE_TEMPLATE_CFN_RESOURCE}
      UserData: !Base64 |
EOF
fi

cat ${FILE_USER_DATA} \
  | sed '/^$/d' \
  | sed "s/^/        /" \
  >> ${FILE_TEMPLATE_CFN_RESOURCE}

cat ${FILE_TEMPLATE_CFN_RESOURCE}

結果(例):

Instance0:
  Type: AWS::EC2::Instance
  Properties:
    ImageId: ami-09ebacdc178ae23b7
    InstanceType: t3.micro
    Tags:
      - Key: Name
        Value: !Ref AWS::StackName
    NetworkInterfaces:
      - DeviceIndex: "0"
        SubnetId: !Ref Subnet0
        GroupSet:
        - !Ref SecurityGroup0
        AssociatePublicIpAddress: "true"
    UserData: !Base64 |
      #!/bin/bash
      # configure sshd
      cat << EOF >> /etc/ssh/sshd_config
      Port 22022
      EOF
      systemctl restart sshd.service
      # yum
      yum update -y
      #
      # logs agent
      #
      readonly EC2_METADATA_SECOND='900'
      readonly EC2_METADATA_TOKEN=$( \
        curl -s \
          -X PUT "http://169.254.169.254/latest/api/token" \
          -H "X-aws-ec2-metadata-token-ttl-seconds: ${EC2_METADATA_SECOND}" \
      )
      readonly EC2_METADATA_HEADER="X-aws-ec2-metadata-token: ${EC2_METADATA_TOKEN}"
      readonly EC2_REGION_NAME=$( \
        curl -s -H "${EC2_METADATA_HEADER}" \
          http://169.254.169.254/latest/meta-data/placement/availability-zone \
        | sed -e 's/[a-z]*$//' \
      )
      # install cloudwatch agent
      readonly URL_DOWNLOAD_LINK="https://s3.${EC2_REGION_NAME}.amazonaws.com/amazoncloudwatch-agent-${EC2_REGION_NAME}/amazon_linux/amd64/latest/amazon-cloudwatch-agent.rpm"
      cd /tmp/
      wget ${URL_DOWNLOAD_LINK}
      rpm -U ./amazon-cloudwatch-agent.rpm
      # setup cloudwatch agent
      readonly LOGS_CONF='/opt/aws/amazon-cloudwatch-agent/etc/logs.conf'
      # logs.conf
      cat << EOF > ${LOGS_CONF}
      {
        "agent": {
          "logfile": "/opt/aws/amazon-cloudwatch-agent/logs/amazon-cloudwatch-agent.log"
        },
        "logs": {
          "logs_collected": {
            "files": {
              "collect_list": [
                {
                  "file_path": "/var/log/secure",
                  "log_group_name": "/var/log/secure",
                  "timezone": "UTC"
                }
              ]
            }
          },
          "log_stream_name": "{instance_id}",
          "force_flush_interval" : 15
        }
      }
      EOF
      # start cloudwatch agent
      /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl \
        -a fetch-config \
        -m ec2 \
        -c file:${LOGS_CONF} \
        -s
      #
      # disk format (sdh)
      #
      DEVICE_FILE_NAME="sdh"
      MOUNT_POINT_NAME="/mnt-handson-cli-ebs"
      DISK_TYPE='xfs'
      DISK_TARGET="/dev/${DEVICE_FILE_NAME}"
      # format disk
      mkfs \
         -t ${DISK_TYPE} \
         ${DISK_TARGET}
      # make mount point
      mkdir ${MOUNT_POINT_NAME}
      # update fstab
      cp /etc/fstab /etc/fstab.$(date +%s)
      DEVICE_LINK_NAME=$( \
        file -s ${DISK_TARGET} \
        | sed "s/^.*\`//" \
        | sed "s/'$//" \
      )
      DISK_UUID=$( \
        sudo blkid \
          | grep /dev/${DEVICE_LINK_NAME} \
          | sed "s/^.*UUID=\"//" \
          | sed "s/\".*$//" \
      )
      DISK_TYPE_TARGET=$( \
        sudo blkid \
          | grep /dev/${DEVICE_LINK_NAME} \
          | sed "s/^.*TYPE=\"//" \
          | sed "s/\".*$//" \
      )
      if [ ! $( grep ${DISK_UUID} /etc/fstab ) ]; then
        sh -c "echo \"UUID=${DISK_UUID}     ${MOUNT_POINT_NAME} ${DISK_TYPE_TARGET} defaults,nofail  0  2\" >> /etc/fstab"
      fi
      # mount disk
      mount ${MOUNT_POINT_NAME}
      chown ec2-user:ec2-user ${MOUNT_POINT_NAME}

完了確認

本手順の主処理は、以下の完了条件を満たしたときに成功したものとします。

完了条件1: リソースファイル"${HOME}/environment/conf-handson-cli-cfn-ec2-VolumeAttachment/resources/Instance0.txt"が存在する。

「リソースファイル"${HOME}/environment/conf-handson-cli-cfn-ec2-VolumeAttachment/resources/Instance0.txt"が存在する。」ことを確認します。

コマンド:

ls ${FILE_TEMPLATE_CFN_RESOURCE}

結果(例):

${HOME}/environment/conf-handson-cli-cfn-ec2-VolumeAttachment/resources/Instance0.txt

手順の完了