CloudFormationでEC2インスタンスのルートボリュームにタグ付けする

CloudFormationでEC2インスタンスのルートボリュームにタグ付けする方法の紹介です。
2020.02.26

AWSを愛する皆さま、こんにちは。コンサルティング部の西野(@xiyegen)です。

CloudFormationでEC2インスタンスを作成する際はAWS::EC2::Instanceというリソースを指定します。
このAWS::EC2::Instanceにはルートボリュームのタグ付けを担うプロパティが存在しません。
それでもなんとかしてインスタンス作成時にこのタグ付けを実施できないか調べていたところ、公式のナレッジを発見しました。

AWS CloudFormation が作成したインスタンスからルートボリュームにタグを付ける方法を教えてください。

当該ページで紹介されているテンプレートが大きめだったので、あっさり味に加工した自作テンプレートとともに仕組みを紹介したいと思います。

目次

3行まとめ

テンプレート内で行われている処理は単純です。

  • UserDataによりインスタンスメタデータからリージョン情報およびインスタンスIDを取得する。
  • UserDataからAWS CLIを使用しタグ付け対象ボリュームのID取得&タグ付けを実行する(aws ec2 describe-instances&aws ec2 create-tags)。
  • 上記のコマンドを実行するためにec2:Describe*ec2:CreateTagsの権限をインスタンスへ付与する。

注意

AWSのリソースタグは、IAMによるアクセス制御やAmazon DLMによるスナップショットターゲットの指定など、様々な用途に用いられます。
リソースタグのユースケースによってはec2:CreateTags権限の付与が意図せぬ結果をまねくことがあります。あらかじめご留意ください。

テンプレート

前提条件

  • AWS CLIが必要なのでAmazon Linux 2の最新版AMI(2020/02/26時点)を使用しています。
  • 説明の簡便化のためテンプレートからParametersセクションを省いてあります。
  • ルートボリュームにNameタグMySweetRootVolumeを設定するテンプレートです。 *1

本体

AWSTemplateFormatVersion: 2010-09-09
Resources:
  LinuxInstance:
    Type: 'AWS::EC2::Instance'
    Properties:
      ImageId: ami-0af1df87db7b650f4
      InstanceType: t2.micro
      AvailabilityZone: ap-northeast-1a
      SubnetId: subnet-XXXXXXXXXXXXXXXXX
      IamInstanceProfile: !Ref InstanceProfile
      KeyName: wo-de-yaoshi
      UserData: 
        Fn::Base64: |
          #!/bin/sh
          AWS_AVAIL_ZONE=$(curl http://169.254.169.254/latest/meta-data/placement/availability-zone)
          AWS_REGION="`echo \"$AWS_AVAIL_ZONE\" | sed 's/[a-z]$//'`"
          AWS_INSTANCE_ID=$(curl http://169.254.169.254/latest/meta-data/instance-id)
          ROOT_VOLUME_IDS=$(aws ec2 describe-instances --region $AWS_REGION --instance-id $AWS_INSTANCE_ID --output text --query Reservations[0].Instances[0].BlockDeviceMappings[0].Ebs.VolumeId)
          aws ec2 create-tags --resources $ROOT_VOLUME_IDS --region $AWS_REGION --tags Key=Name,Value=MySweetRootVolume
      Tags:
        - Key: Name
          Value: MySweetInstance
      BlockDeviceMappings: 
      - DeviceName: "/dev/xvda"
        Ebs: 
          VolumeSize: 8
          VolumeType: "gp2"
  InstanceRole:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      Path: /
      Policies:
        -
          PolicyName: taginstancepolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              -
                Effect: Allow
                Action:
                  - 'ec2:Describe*'
                  - 'ec2:CreateTags'
                Resource: '*'
  InstanceProfile:
    Type: 'AWS::IAM::InstanceProfile'
    Properties:
      Path: /
      Roles:
       -
         !Ref InstanceRole

解説

UserData

UserData: 
        Fn::Base64: |
          #!/bin/sh
          AWS_AVAIL_ZONE=$(curl http://169.254.169.254/latest/meta-data/placement/availability-zone)
          AWS_REGION="`echo \"$AWS_AVAIL_ZONE\" | sed 's/[a-z]$//'`"
          AWS_INSTANCE_ID=$(curl http://169.254.169.254/latest/meta-data/instance-id)
          ROOT_VOLUME_IDS=$(aws ec2 describe-instances --region $AWS_REGION --instance-id $AWS_INSTANCE_ID --output text --query Reservations[0].Instances[0].BlockDeviceMappings[0].Ebs.VolumeId)
          aws ec2 create-tags --resources $ROOT_VOLUME_IDS --region $AWS_REGION --tags Key=Name,Value=MySweetRootVo

ルートボリュームへのタグ付けの肝となるのは本テンプレートのUserData部分です。

  1. 必要となるパラメータ($AWS_REGION$AWS_INSTANCE_ID)をインスタンスメタデータから取得
  2. aws ec2 describe-instancesでルートボリュームのIDを取得
  3. aws ec2 create-tagsでタグ付けを実行(Key=Name,Value=MySweetRootVolume)

という流れで処理を行っています。

IAMロールとインスタンスプロファイル

InstanceRole:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - ec2.amazonaws.com
            Action:
              - 'sts:AssumeRole'
      Path: /
      Policies:
        -
          PolicyName: taginstancepolicy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              -
                Effect: Allow
                Action:
                  - 'ec2:Describe*'
                  - 'ec2:CreateTags'
                Resource: '*'
  InstanceProfile:
    Type: 'AWS::IAM::InstanceProfile'
    Properties:
      Path: /
      Roles:
       -
         !Ref InstanceRole

ボリュームのID取得とタグ付けに必要なec2:Describe*ec2:CreateTagsの権限を持つポリシーを作成し、作成対象のインスタンスに付与しています。

やってみた

先述したテンプレートでスタックを作成します。


EC2インスタンスができています。
ルートボリュームを見てみると……。


しっかりNameタグを付与できています。

終わりに

このブログがほんの少しでも世界を良くできれば嬉しいです。
コンサルティング部の西野(@xiyegen)がお送りしました。

脚注

  1. aws ec2 create-tagsコマンドの--tagsオプションでKeyおよびValueを指定することにより任意のタグを付与できます。