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

EBSのNameタグにEC2のNameの値を自動で付ける。

More than 3 years have passed since last update.

目的

EC2と一緒に作成されるEBSにはタグ付けが自動で行えないので基本何もタグがつかないEBSが大量にできることになる。するとボリュームの一覧を見た時に何処にアタッチされてるボリュームなのかわからないくて不便。

何より個人的に嫌なのは、Cost Allocation Tagによる請求レポート分析から漏れて案件毎の請求額の把握が面倒なことになるのが嫌(プロジェクト単位でアカウント分けるてのもありだけどConsolidatedBillingて増やせるとはいえ20アカウント上限があるしAWSクレジットが適用されると請求書がゼロとかにされて実態把握が面倒だし結局BillingReportの詳細CSVから自分の好みに応じて集計するのが痒いところに手が届いて気持ち良いのよねー)なので。

だから、タグは可能な限り漏れ無く付けておきたい→自動化したい。

要件

以下の理由により各EC2インスタンス自身で付けさせるのはやりたくない。

  • 絶対設定漏れがでる。(userdataで必ずこれ実行すること!とかルール決めてもどうせ忘れる)
  • インスタンス起動後に別途EBSをアタッチした場合にそいつに付けるタイミングがないのでやっぱ漏れる。
  • EC2には第三者(発注元や外注先など)もログインorコードデプロイ可能なので、与えるIAM権限は最小にしたい。(特定インスタンス(リソースIDじゃなく自身のインスタンスに対してとかファジーな感じ)や特定タグに対してのみに絞って権限を与えるということが出来ればいいが、そこまで細かい制御をIAMでは出来ないので)

実装

要件を踏まえて、管理用のインスタンスのcron実行により、タグ無し(or EC2と不一致)EBSを見つけて勝手にタグを付け直す形で実装してみました。

IAM設定

まず管理用インスタンスのIAMRole(or IAMUser)に最低限以下の様なポリシーを追加しておます。

ManageVolumeTags-20150130T131540
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ec2:CreateTags",
        "ec2:DescribeInstances",
        "ec2:DescribeRegions",
        "ec2:DescribeVolumes"
      ],
      "Resource": "*"
    }
  ]
}

スクリプト

で、管理用インスタンス上で以下のスクリプトをcronで実行しとけば大体よい。

tag-to-ebs.sh
#!/bin/bash
volumeNamesByInstances() {
  region="$1"
  aws ec2 describe-instances --region "$region" --no-paginate --filters Name=tag-key,Values=Name |
  jq -r '.Reservations[].Instances[]|"\(.BlockDeviceMappings[].Ebs.VolumeId) \(.Tags[]|select(.Key=="Name").Value)"' |
  sort
}

volumeNamesByVolumes() {
  region="$1"
  aws ec2 describe-volumes --region "$region" --no-paginate |
  jq -r '.Volumes[]|"\(.VolumeId) \(.Tags[]?|select(.Key=="Name").Value)"' |
  sort
}

regions() {
  az=$(curl -sL http://169.254.169.254/latest/meta-data/placement/availability-zone)
  region="${az%[a-z]}"
  aws ec2 describe-regions --region "$region" | jq -r '.Regions[].RegionName'
}

for region in $(regions); do
  # EC2とEBSでタグの値に差異のある部分だけを更新する
  diff <(volumeNamesByVolumes "$region") <(volumeNamesByInstances "$region") | grep '^> ' |
  while read _ volumeId name; do
    [[ $name =~ ^- ]] && continue # skip dangerous
    aws ec2 create-tags --region "$region" --resources "$volumeId" --tags "Key=Name,Value=$name"
  done
done

実行間隔について

cronの実行間隔は、AWSの課金が1時間単位なのでその前にはタグを付けておきたいがそんなに急がなくてもよい、逆に短すぎてもAWSのAPIサーバに負荷かけると悪いかなーって気持ちと、そもそも現実問題としてdescribe系のAPIの結果が実は数分くらいキャッシュされてるらしくそれが原因でEC2側のタグを変えてもdescribeのレスポンスは暫く変わらないこともよくあるので頻繁にチェックする意味が無いので、僕は15分間間隔くらいで実行させている。

おまけ

僕の要件には合わないけど、EC2自身にタグ付けさせたい人にはクラスメソッドさんのこの記事が参考になると思う。>EC2インスタンスのEBSルートボリュームに自動でNameタグを付与する | Developers.IO

Why do not you register as a user and use Qiita more conveniently?
  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