一括処理により複数の AWS リソースへのタグ付けをする方法を検討してみた

記事タイトルとURLをコピーする

みなさん、こんにちは。
AWS CLI が好きなテクニカルサポート課の市野です。

いただいたお問い合わせで Excel ファイルや CSV ファイルとして管理している一覧表から複数のリソースへ一括でのタグ付けができないか、というご質問がありました。

残念ながら現時点において AWS ではファイルインポートによる一括のタグ付けができないため、CSV ファイルを繰り返し読み込みながら AWS CLI でリソースへのタグ付けを極力一度の実行で行える方法をいくつか検討してみました。

クリックで目次が表示されます。

前提

以下の説明では、AWS アカウント ID に相当する部分のみ伏字としていますが、極力イメージがしやすいように各種 AWS リソースの ID はそのまま使用しています。
当該のリソースはすでに削除済みですのでご心配なく。

また、処理の際に読み込んでいるテキストファイルはタブ区切りとなっているため、本来は TSV ファイルやタブ区切りテキストと記載した方が正確と思いますが、より一般的に伝わりやすいよう CSV ファイルとして呼称を統一させていただきました。

以降の説明箇所で、CSV レイアウトの説明のために1行目にタイトル行を含んでいますが、ループ処理の中では、タイトル行の削除や無視といった考慮をしていませんので、実際にお使いいただく際にはご注意ください。

考えられるアプローチ

AWS CLI を用いる手法として、大きく 2 パターンが考えられるかと思います。

  1. Resource Groups Tagging API を用いた汎用的なタグ付与
  2. タグ付けしたい対象の AWS サービスネイティブの API でのタグ付与

検討1. Resource Groups Tagging API の利用

概要

Resource Groups Tagging API を AWS CLI から扱うために、resourcegroupstaggingapi tag-resources サブコマンドが用意されています。

サブコマンドの詳細は AWS CLI Command Reference を参照いただきたいですが、--resource-arn-list オプションの引数に対象となるリソースの ARN、--tags オプションの引数に付与したいタグ文字列を付与し実行することでタグ付けを行うことが可能です。

docs.aws.amazon.com

注意点

以下 AWS ドキュメントにも記載のある通り、Resource Groups Tagging API によるタグ付けに対応していない AWS サービスがあります。

そのような AWS サービスのリソースに対するタグ付けを行うには、上記の手法では対応ができず、後述のタグ付けしたい対象の AWS サービスネイティブ(固有)の API でのタグ付与をせざるを得ない点に注意が必要です。
また、Resource Groups Tagging API によるタグ付けに対応している AWS サービスの一覧も下記ドキュメントにありますので、必要に応じて適宜ご参照ください。

docs.aws.amazon.com

やってみる

パターン1:EC2 インスタンスごとに別々のタグを付与する

CSV ファイルの例

一つ目のフィールドにタグ付け対象となる ARN、二つ目のフィールドにタグ名、三つ目のフィールドにタグ値を記載した CSV ファイルを作成します。

cat << EOF > resource-list.csv
ResourceARN                                                             TagKey      TagValue
arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:instance/i-0919bdc9b3e0fccae    Environment Production
arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:instance/i-048bf759268a00246    Environment Staging
EOF
実行前確認

現時点でのタグの付与状況を確認します。
もともと Name タグのみ付与されている想定です。

aws ec2 describe-instances \
  --query 'Reservations[].Instances[].[InstanceId,Tags]'
[
    [
        "i-0919bdc9b3e0fccae",
        [
            {
                "Key": "Name",
                "Value": "test"
            }
        ]
    ],
    [
        "i-048bf759268a00246",
        [
            {
                "Key": "Name",
                "Value": "test02"
            }
        ]
    ]
]
実行処理
while read RESOURCE_LIST; do

  RESOURCE_ARN=$(echo ${RESOURCE_LIST} | awk -F ' ' '{print $1}')
  TAG_KEY=$(echo ${RESOURCE_LIST} | awk -F ' ' '{print $2}')
  TAG_VALUE=$(echo ${RESOURCE_LIST} | awk -F ' ' '{print $3}')

  aws resourcegroupstaggingapi tag-resources \
    --resource-arn-list ${RESOURCE_ARN} \
    --tags ${TAG_KEY}=${TAG_VALUE}

done < resource-list.csv
タグ付け結果の確認

CSV ファイルに記載した通り、

  • i-0919bdc9b3e0fccae には "Key": "Environment", "Value": "Production" が、
  • i-048bf759268a00246 には "Key": "Environment", "Value": "Staging"

が付与されていることが確認できました。

aws ec2 describe-instances \
  --query 'Reservations[].Instances[].[InstanceId,Tags]'
[
    [
        "i-0919bdc9b3e0fccae",
        [
            {
                "Key": "Name",
                "Value": "test"
            },
            {
                "Key": "Environment",
                "Value": "Production"
            }
        ]
    ],
    [
        "i-048bf759268a00246",
        [
            {
                "Key": "Environment",
                "Value": "Staging"
            },
            {
                "Key": "Name",
                "Value": "test02"
            }
        ]
    ]
]

パターン2:複数の EC2 インスタンスごとに同一のタグを付与する

CSV ファイルの例

一つ目のフィールドにタグ付けしたい対象の ARN を配列として記載、二つ目のフィールドに タグ名=タグ値 形式を付与したいタグ分だけカンマ区切りで繋げた文字列として記載した CSV ファイルを作成します。

cat << EOF > resource-list.csv
ResourceArray                                                                                                                                                                                           TagList
"arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:instance/i-0919bdc9b3e0fccae","arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:volume/vol-0309d641ee71bebfd","arn:aws:rds:ap-northeast-1:xxxxxxxxxxxx:db:database-1"   Environment=Production,Cost=1234
EOF
--resource-arn-list オプションの引数で指定できるリソースの個数について

本エントリでは正しく動作する事例をさらっと書いてしまっているため唐突に複数のリソースを配列で指定していますが、AWS API TagResources アクションのリファレンスを確認すると最大 20 リソースまでの配列を引数に取ることができるとあります。

docs.aws.amazon.com

ResourceARNList
〜中略〜
Array Members: Minimum number of 1 item. Maximum number of 20 items.

実行前確認
EC2 インスタンスの確認

パターン1と同様に、Name タグのみ付与されている想定です。

aws ec2 describe-instances \
  --query 'Reservations[].Instances[].[InstanceId,Tags]'
[
    [
        "i-0919bdc9b3e0fccae",
        [
            {
                "Key": "Name",
                "Value": "test"
            }
        ]
    ]
]
EBS ボリュームの確認

EBS ボリュームは作成時にタグをつけておらず、何も返却されていない状態としています。

aws ec2 describe-volumes \
  --query 'Volumes[].[VolumeId,Tags]'
[
    [
        "vol-0309d641ee71bebfd",
        null
    ]
]
RDS DB インスタンスの確認

RDS インスタンスは作成時にタグをつけておらず、何も返却されていない状態としています。

aws rds describe-db-instances \
  --query 'DBInstances[].[DBInstanceArn,TagList]'
[
    [
        "arn:aws:rds:ap-northeast-1:xxxxxxxxxxxx:db:database-1",
        []
    ]
]
実行処理
while read RESOURCE_LIST; do

  RESOURCE_ARRAY=$(echo ${RESOURCE_LIST} | awk -F ' ' '{print $1}')
  TAG_LIST=$(echo ${RESOURCE_LIST} | awk -F ' ' '{print $2}')

  aws resourcegroupstaggingapi tag-resources \
    --resource-arn-list '['${RESOURCE_ARRAY}']' \
    --tags ${TAG_LIST}

done < resource-list.csv
タグ付け結果の確認
EC2 インスタンスの確認

CSV ファイルで指定した通り "Key": "Environment","Value": "Production""Key": "Cost","Value": "1234" が追加されていることが確認できます。

aws ec2 describe-instances \
  --query 'Reservations[].Instances[].[InstanceId,Tags]'
[
    [
        "i-0919bdc9b3e0fccae",
        [
            {
                "Key": "Name",
                "Value": "test"
            },
            {
                "Key": "Environment",
                "Value": "Production"
            },
            {
                "Key": "Cost",
                "Value": "1234"
            }
        ]
    ]
]
EBS ボリュームの確認

EBS ボリュームについても CSV ファイルで指定した通り "Key": "Environment","Value": "Production""Key": "Cost","Value": "1234" が追加されていることが確認できます。

aws ec2 describe-volumes \
  --query 'Volumes[].[VolumeId,Tags]'
[
    [
        "vol-0309d641ee71bebfd",
        [
            {
                "Key": "Environment",
                "Value": "Production"
            },
            {
                "Key": "Cost",
                "Value": "1234"
            }
        ]
    ]
]
RDS DB インスタンスの確認

RDS DB インスタンスについても CSV ファイルで指定した通り "Key": "Environment","Value": "Production""Key": "Cost","Value": "1234" が追加されていることが確認できます。

aws rds describe-db-instances \
  --query 'DBInstances[].[DBInstanceArn,TagList]'
[
    [
        "arn:aws:rds:ap-northeast-1:xxxxxxxxxxxx:db:database-1",
        [
            {
                "Key": "Environment",
                "Value": "Production"
            },
            {
                "Key": "Cost",
                "Value": "1234"
            }
        ]
    ]
]

パターン3:対応していないサービスやリソースを含んでいる場合の処理

私が調査できている範囲では Resource Groups Tagging API でのタグ付けはできないが、当該のサービス固有の API であればタグ付けできる という AWS サービスやリソースの事例を確認できておらず、便宜上、元来タグ付与に対応していない IAM グループを例に取っていますのでご注意ください。

CSV ファイルの例

ファイルのフォーマットはパターン2と同一ですが、現時点でタグ付けに対応していない IAM グループ の ARN を含めています。

cat << EOF > resource-list.csv
ResourceArray                                                                                                                                                                                                                                                   TagList
"arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:instance/i-0919bdc9b3e0fccae","arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:volume/vol-0309d641ee71bebfd","arn:aws:rds:ap-northeast-1:xxxxxxxxxxxx:db:database-1","arn:aws:iam::xxxxxxxxxxxx:group/AdministratorAccessTest" Environment=Production,Cost=1234
EOF
実行処理
while read RESOURCE_LIST; do

  RESOURCE_ARRAY=$(echo ${RESOURCE_LIST} | awk -F ' ' '{print $1}')
  TAG_LIST=$(echo ${RESOURCE_LIST} | awk -F ' ' '{print $2}')

  aws resourcegroupstaggingapi tag-resources \
    --resource-arn-list '['${RESOURCE_ARRAY}']' \
    --tags ${TAG_LIST}

done < resource-list.csv
実行結果例(エラー発生例)

Unrecognized service or resource type for tagging(意訳:タグ付け用のサービスまたはリソース タイプが認識されません) というエラーが返却されることを確認できました。

{
    "FailedResourcesMap": {
        "arn:aws:iam::xxxxxxxxxxxx:group/AdministratorAccessTest": {
            "StatusCode": 400,
            "ErrorCode": "InvalidParameterException",
            "ErrorMessage": "Unrecognized service or resource type for tagging"
        }
    }
}

検討2. AWS サービスネイティブの API でのタグ付け

EC2 リソースであれば ec2 create-tags サブコマンド、RDS リソースであれば rds add-tags-to-resource サブコマンドなど、AWS サービス固有の API として用意されています。

前述の Resource Groups Tagging API に対応していない AWS サービスであれば、当該の AWS サービスに固有で用意されている API をご利用いただく必要がありますが、AWS サービス固有の API の方が処理が簡単だったり、できることが多い側面はあるかもしれません。

やってみる

EC2 サービス内の複数リソースに対するタグ付け

CSV ファイルの例

以下のリソースに、タグキー Environment 、値に Production を付与する想定としています。

  • EC2 インスタンス:i-0919bdc9b3e0fccae,i-000efe6f10a871ae3
  • EBS ボリューム:vol-0309d641ee71bebfd,vol-09aede73c0c8f9ed5
  • AMI イメージ:ami-0088e04c53373e6a3
cat << EOF > resource-list.csv
ResourceIDs                                                                                               TagKey      TagValue
i-0919bdc9b3e0fccae,i-000efe6f10a871ae3,vol-0309d641ee71bebfd,vol-09aede73c0c8f9ed5,ami-0088e04c53373e6a3 Environment Production
EOF
実行前確認
EC2 インスタンスの確認

Name タグのみが付与された状態です。

aws ec2 describe-instances \
  --query 'Reservations[].Instances[].[InstanceId,Tags]'
[
    [
        "i-0919bdc9b3e0fccae",
        [
            {
                "Key": "Name",
                "Value": "test"
            }
        ]
    ],
    [
        "i-000efe6f10a871ae3",
        [
            {
                "Key": "Name",
                "Value": "test02"
            }
        ]
    ]
]
EBS ボリュームの確認

EBS ボリュームにもタグが付与されていないことが確認できます。

aws ec2 describe-volumes \
  --query 'Volumes[].[VolumeId,Tags]'
[
    [
        "vol-0309d641ee71bebfd",
        null
    ],
    [
        "vol-09aede73c0c8f9ed5",
        null
    ]
]
AMI イメージの確認

AMI イメージにもタグが付与されていないことが確認できます。

aws ec2 describe-images \
  --image-ids ami-0088e04c53373e6a3 \
  --query 'Images[].[ImageId, Tags]'
[
    [
        "ami-0088e04c53373e6a3",
        null
    ]
]
実行処理

処理のポイントとしては、ec2 create-tags--resource オプションの引数には、リソース ID を space で区切って指定する仕様のため、sed で、カンマを半角スペースに置き換える処理を加えています。

while read RESOURCE_LIST; do

  RESOURCE_IDS=$(echo ${RESOURCE_LIST} | awk -F ' ' '{print $1}')
  TAG_KEY=$(echo ${RESOURCE_LIST} | awk -F ' ' '{print $2}')
  TAG_VALUE=$(echo ${RESOURCE_LIST} | awk -F ' ' '{print $3}')

  aws ec2 create-tags \
    --resource $(echo ${RESOURCE_IDS} | sed -e 's/,/ /g') \
    --tags Key=${TAG_KEY},Value=${TAG_VALUE}

done < resource-list.csv

docs.aws.amazon.com

Request Parameters
〜中略〜
ResourceId.N
The IDs of the resources, separated by spaces.

Constraints: Up to 1000 resource IDs. We recommend breaking up this request into smaller batches.

タグ付け結果の確認
EC2 インスタンスの確認

CSV ファイルで指定した通り、"Key": "Environment", "Value": "Production"が付与されていることが確認できます。

aws ec2 describe-instances \
  --query 'Reservations[].Instances[].[InstanceId,Tags]'
[
    [
        "i-0919bdc9b3e0fccae",
        [
            {
                "Key": "Name",
                "Value": "test"
            },
            {
                "Key": "Environment",
                "Value": "Production"
            }
        ]
    ],
    [
        "i-000efe6f10a871ae3",
        [
            {
                "Key": "Name",
                "Value": "test02"
            },
            {
                "Key": "Environment",
                "Value": "Production"
            }
        ]
    ]
]
EBS ボリュームの確認

EBS ボリュームにも、CSV ファイルで指定した通り、"Key": "Environment", "Value": "Production"が付与されていることが確認できます。

aws ec2 describe-volumes \
  --query 'Volumes[].[VolumeId,Tags]'
[
    [
        "vol-0309d641ee71bebfd",
        [
            {
                "Key": "Environment",
                "Value": "Production"
            }
        ]
    ],
    [
        "vol-09aede73c0c8f9ed5",
        [
            {
                "Key": "Environment",
                "Value": "Production"
            }
        ]
    ]
]
AMI イメージの確認

AMI イメージに対しても、CSV ファイルで指定した通り、"Key": "Environment", "Value": "Production"が付与されていることが確認できます。

aws ec2 describe-images \
  --image-ids ami-0088e04c53373e6a3 \
  --query 'Images[].[ImageId, Tags]'
[
    [
        "ami-0088e04c53373e6a3",
        [
            {
                "Key": "Environment",
                "Value": "Production"
            }
        ]
    ]
]

所感

Resource Groups Tagging API を用いる resourcegroupstaggingapi tag-resources サブコマンドであれば、対象となるリソースが複数の AWS サービスに跨っていても単一のコマンドを流用できる点に優位性があるかと考えられます。
反面、resourcegroupstaggingapi tag-resources サブコマンドの場合であれば、リソースを必ず ARN 形式である点があるため、ARN 文字列を取得しにくい EC2 サービスのリソースを対象とする場合に別途 ARN 文字列を組み立てる必要性がある点、また同一のタグを付与する複数のリソースがあった場合に、20個までのリソースに限られる点の制約があると感じています。

AWS サービスネイティブの API でのタグ付けの場合であれば、例えば EC2 リソースを例にとると、VPC や インスタンス、AMI や EBS ボリュームなど、リソースの種別が異なっていても EC2 サービスの範疇にあれば一括で指定できる点や、同一のタグを付与する場合であれば最大 1,000 個のリソースを対象として ID のみの列記で指定できる点(ARN 文字列を指定しなくても良い点)のメリットがあると感じています。
ただ、タグ付けしたい対象リソースがサービスをまたがる場合には、都度、対象となる AWS サービスネイティブの API を確認し、目的に応じた AWS CLI コマンドの組み立てが必要である点で汎用性に欠けるデメリットがあるかと考えられます。


本記事がどなたかのお役に立てば幸いです。

ではまた。

市野 和明 (記事一覧)

マネージドサービス部・テクニカルサポート課

お客様から寄せられたご質問や技術検証を通じて得られた気づきを投稿していきます。

情シスだった前職までの経験で、UI がコロコロ変わる AWS においては GUI で手順を残していると画面構成が変わってしまって後々まごつくことが多かった経験から、極力変わりにくい AWS CLI での記事が多めです。