AWS のタグは「キー」と「値」の組で、リソースに目印を付ける仕組みです。AWS CLI では aws ec2 create-tags --resources ID --tags Key=Env,Value=prod のように付けます。 ただし、タグを付けるコマンドの名前と引数の書き方はサービスごとに違います。EC2 は create-tags、S3 は put-bucket-tagging、Lambda や DynamoDB は tag-resource、RDS は add-tags-to-resource です。

この記事では、サービスごとのコマンドと引数の形を1枚の表にまとめ、付与・確認・削除を実行した出力つきで載せます。あわせて、タグでリソースを検索する2つの方法、タグが付いていないリソースを洗い出すワンライナー、S3 のバケットタグが全置換になる動き、命名ルールと上限、コスト配分タグ向けのキー設計、boto3 での一括付与コードを紹介します。

確認環境:Ubuntu 24.04、AWS CLI 1.46.1、Python 3.11、boto3 1.43.103、jq 1.7。AWS の API をローカルで再現するツール(moto 5.2.3)で実行しました。掲載するコマンドは接続先の指定を外し、実際の AWS でそのまま使える形にしています。

AWS タグ付けコマンドの早見表(サービス別)

サービス 付ける 確認する 外す リソースの指定 --tags の形
EC2(インスタンス・ボリュームなど) ec2 create-tags ec2 describe-tags ec2 delete-tags --resources ID ... リスト形
S3 バケット s3api put-bucket-tagging s3api get-bucket-tagging s3api delete-bucket-tagging --bucket 名前 --tagging 'TagSet=[...]'
S3 オブジェクト s3api put-object-tagging s3api get-object-tagging s3api delete-object-tagging --bucket 名前 --key キー --tagging 'TagSet=[...]'
Lambda lambda tag-resource lambda list-tags lambda untag-resource --resource ARN マップ形
DynamoDB dynamodb tag-resource dynamodb list-tags-of-resource dynamodb untag-resource --resource-arn ARN リスト形
RDS rds add-tags-to-resource rds list-tags-for-resource rds remove-tags-from-resource --resource-name ARN リスト形
IAM ロール iam tag-role iam list-role-tags iam untag-role --role-name 名前 リスト形
IAM ユーザー iam tag-user iam list-user-tags iam untag-user --user-name 名前 リスト形
SQS sqs tag-queue sqs list-queue-tags sqs untag-queue --queue-url URL マップ形
SNS sns tag-resource sns list-tags-for-resource sns untag-resource --resource-arn ARN リスト形
CloudWatch Logs logs tag-resource logs list-tags-for-resource logs untag-resource --resource-arn ARN マップ形
CloudWatch アラーム cloudwatch tag-resource cloudwatch list-tags-for-resource cloudwatch untag-resource --resource-arn ARN リスト形
SSM パラメータ ssm add-tags-to-resource ssm list-tags-for-resource ssm remove-tags-from-resource --resource-type Parameter --resource-id 名前 リスト形
Secrets Manager secretsmanager tag-resource secretsmanager describe-secret secretsmanager untag-resource --secret-id 名前 リスト形
サービス横断 resourcegroupstaggingapi tag-resources resourcegroupstaggingapi get-resources resourcegroupstaggingapi untag-resources --resource-arn-list ARN ... マップ形

--tags の書き方は2種類あります。

形 書き方 JSON で書くと
リスト形 --tags Key=Env,Value=prod Key=Project,Value=shop [{"Key":"Env","Value":"prod"},{"Key":"Project","Value":"shop"}]
マップ形 --tags Env=prod,Project=shop {"Env":"prod","Project":"shop"}

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「AWSタグの付け方|AWS CLIでタグ付け・検索・一覧取得と命名ルール」

タグの基本ルールと上限

公式ドキュメント(Tagging AWS resources のベストプラクティス)で確認した共通のルールです。

項目 上限・ルール
1リソースあたりのタグ数 ユーザーが付けるタグは50個まで(aws: で始まるタグは数に含めない)
キーの長さ 1〜128文字(UTF-8 の Unicode 文字)
値の長さ 0〜256文字(空の値も可)
使える文字 文字、数字、空白と、_ . : / = + - @(サービスによって違いがある)
大文字と小文字 区別される。Env と env は別のキー
キーの重複 1リソースにつき同じキーは1つだけ。同じキーで付け直すと値が上書きされる
aws: で始まるキー AWS が使う予約済みの接頭辞。編集も削除もできない
書いてよい内容 個人を特定できる情報や機密情報は入れない

大文字と小文字が区別されることは、手元でも確かめられます。

$ aws ec2 create-tags --resources i-e55ad0fe52e456b92 --tags Key=env,Value=prod Key=Env,Value=dev Key=Memo,Value=

$ aws ec2 describe-tags --filters Name=resource-id,Values=i-e55ad0fe52e456b92 --query 'Tags[].[Key,Value]' --output text
env prod
Env dev
Memo    

env と Env が別々のタグとして登録され、Memo は値が空のタグになりました。表記がそろっていないと、検索やコスト集計で別のタグとして数えられます。 命名ルールを先に決めておくことが大切です(後述)。

EC2:タグを付ける・確認する・外す

付ける(create-tags)

--resources に ID を複数並べると、インスタンスとボリュームのように種類の違うリソースにも1回でまとめて付けられます。

$ aws ec2 create-tags --resources i-da0ee0aaf52d8284e vol-948f81833904581f5 \
    --tags Key=Env,Value=prod Key=Project,Value=shop Key=Owner,Value=team-web

$ aws ec2 create-tags --resources i-a7050b2ba7e73da43 --tags Key=Env,Value=dev Key=Project,Value=shop

成功すると何も出力されません。値に空白を含むときは全体を引用符で囲み、カンマや等号を含むときは JSON で書きます。

$ aws ec2 create-tags --resources i-da0ee0aaf52d8284e --tags 'Key=Name,Value=web 01'

$ aws ec2 create-tags --resources i-da0ee0aaf52d8284e --tags '[{"Key":"Note","Value":"a,b=c"}]'

確認する(describe-tags)

$ aws ec2 describe-tags --filters Name=resource-id,Values=i-da0ee0aaf52d8284e --query 'Tags[].[Key,Value]' --output text
Env prod
Project shop
Owner   team-web
Name    web 01
Note    a,b=c

--query を外すと、リソースの種類も含む JSON で返ります(一部を抜粋)。

$ aws ec2 describe-tags --filters Name=resource-id,Values=i-da0ee0aaf52d8284e
{
    "Tags": [
        {
            "Key": "Env",
            "ResourceId": "i-da0ee0aaf52d8284e",
            "ResourceType": "instance",
            "Value": "prod"
        },
…(省略)…
    ]
}

値を変える

同じキーで create-tags を実行すると、値が上書きされます。ほかのタグはそのまま残ります。

$ aws ec2 create-tags --resources i-da0ee0aaf52d8284e --tags Key=Env,Value=stg

$ aws ec2 describe-tags --filters Name=resource-id,Values=i-da0ee0aaf52d8284e Name=key,Values=Env --query 'Tags[].Value' --output text
stg

外す(delete-tags)

$ aws ec2 delete-tags --resources i-da0ee0aaf52d8284e --tags Key=Note

$ aws ec2 delete-tags --resources i-da0ee0aaf52d8284e --tags Key=Owner,Value=someone-else

$ aws ec2 describe-tags --filters Name=resource-id,Values=i-da0ee0aaf52d8284e --query 'Tags[].[Key,Value]' --output text
Env prod
Project shop
Owner   team-web
Name    web 01

Key=Note はキーだけの指定なので削除されました。一方、Key=Owner,Value=someone-else は値が一致しなかったので削除されず、Owner が残っています。公式リファレンスでは次のように説明されています。

--tags の指定 動き
Key=Note 値に関係なく、そのキーのタグを削除する
Key=Owner,Value=team-web キーと値の両方が一致したときだけ削除する
Key=Memo,Value= 値が空文字のタグだけを削除する
--tags を省略 そのリソースのユーザー定義タグをすべて削除する(aws: で始まるタグは残る)

--tags を省くと全部のタグが対象になるので、外すキーは必ず明示します。

作成と同時に付ける(--tag-specifications)

作ってから付けるより、作成時に付けるほうが、タグの付け漏れを防げます。

$ aws ec2 run-instances --image-id ami-03cf127a --instance-type t3.micro \
    --tag-specifications 'ResourceType=instance,Tags=[{Key=Env,Value=prod},{Key=Project,Value=blog}]' \
                         'ResourceType=volume,Tags=[{Key=Env,Value=prod}]' \
    --query 'Instances[0].Tags'
[
    {
        "Key": "Env",
        "Value": "prod"
    },
    {
        "Key": "Project",
        "Value": "blog"
    }
]

ほかのサービスも、作成コマンドに --tags を付けられるものが多くあります。

サービス 作成時にタグを付ける例
Lambda aws lambda create-function ... --tags Env=prod,Project=shop
RDS aws rds create-db-instance ... --tags Key=Env,Value=prod
SNS aws sns create-topic --name 名前 --tags Key=Env,Value=prod
SSM パラメータ aws ssm put-parameter --name 名前 --value 値 --type String --tags Key=Env,Value=prod
Secrets Manager aws secretsmanager create-secret --name 名前 --secret-string 値 --tags Key=Env,Value=prod
CloudWatch アラーム aws cloudwatch put-metric-alarm ... --tags Key=Env,Value=prod

S3:put-bucket-tagging は既存のタグを全置換する

S3 のバケットタグは、ほかのサービスと動きが違います。put-bucket-tagging は「追加」ではなく「置き換え」です。

$ aws s3api get-bucket-tagging --bucket rjc-tag-demo

An error occurred (NoSuchTagSet) when calling the GetBucketTagging operation: The TagSet does not exist

$ aws s3api put-bucket-tagging --bucket rjc-tag-demo --tagging 'TagSet=[{Key=Env,Value=prod},{Key=Project,Value=shop}]'

$ aws s3api get-bucket-tagging --bucket rjc-tag-demo
{
    "TagSet": [
        {
            "Key": "Env",
            "Value": "prod"
        },
        {
            "Key": "Project",
            "Value": "shop"
        }
    ]
}

タグが1つも無いバケットでは、get-bucket-tagging が NoSuchTagSet を返します。ここに Owner を足すつもりで、Owner だけを指定して実行します。

$ aws s3api put-bucket-tagging --bucket rjc-tag-demo --tagging 'TagSet=[{Key=Owner,Value=team-web}]'

$ aws s3api get-bucket-tagging --bucket rjc-tag-demo
{
    "TagSet": [
        {
            "Key": "Owner",
            "Value": "team-web"
        }
    ]
}

Env と Project が消え、Owner だけになりました。 公式リファレンスにも「バケットにすでにあるタグを上書きする。既存のタグの一覧に追加する用途には使えない」と書かれています。

既存のタグを残して追加する書き方

いまのタグを読み、追加分と合成してから書き戻します。次のスクリプトは jq でキーごとにまとめ、同じキーがあれば追加分の値を優先します。

merge_bucket_tags.sh:

BUCKET=rjc-tag-demo
CURRENT=$(aws s3api get-bucket-tagging --bucket "$BUCKET" --query 'TagSet' --output json 2>/dev/null || echo '[]')
MERGED=$(jq -c --argjson add '[{"Key":"Env","Value":"prod"},{"Key":"Project","Value":"shop"}]' \
  '{TagSet: ((. + $add) | group_by(.Key) | map(last))}' <<<"$CURRENT")
echo "$MERGED"
aws s3api put-bucket-tagging --bucket "$BUCKET" --tagging "$MERGED"
$ bash merge_bucket_tags.sh
{"TagSet":[{"Key":"Env","Value":"prod"},{"Key":"Owner","Value":"team-web"},{"Key":"Project","Value":"shop"}]}

$ aws s3api get-bucket-tagging --bucket rjc-tag-demo --query 'TagSet[].[Key,Value]' --output text
Env prod
Owner   team-web
Project shop

Owner を残したまま Env と Project が加わりました。タグが無いバケットでは get-bucket-tagging がエラーを返すので、|| echo '[]' で空の配列に置き換えています。

オブジェクト単位のタグも同じ形で扱えます。

$ aws s3api put-object-tagging --bucket rjc-tag-demo --key a.txt --tagging 'TagSet=[{Key=Class,Value=public}]'

$ aws s3api get-object-tagging --bucket rjc-tag-demo --key a.txt --query 'TagSet[].[Key,Value]' --output text
Class   public

Lambda・DynamoDB・RDS・IAM:コマンドと引数の違い

Lambda(マップ形・ARN 指定)

$ aws lambda tag-resource --resource arn:aws:lambda:ap-northeast-1:123456789012:function:resize \
    --tags Owner=team-web,CostCenter=cc-1001

$ aws lambda list-tags --resource arn:aws:lambda:ap-northeast-1:123456789012:function:resize
{
    "Tags": {
        "Env": "prod",
        "Project": "shop",
        "Owner": "team-web",
        "CostCenter": "cc-1001"
    }
}

$ aws lambda untag-resource --resource arn:aws:lambda:ap-northeast-1:123456789012:function:resize --tag-keys CostCenter

Env と Project は、関数の作成時に --tags Env=prod,Project=shop で付けたものです。tag-resource は追加の動きなので、既存のタグは残ります。

DynamoDB(リスト形・ARN 指定)

$ aws dynamodb tag-resource --resource-arn arn:aws:dynamodb:ap-northeast-1:123456789012:table/Orders \
    --tags Key=Env,Value=prod Key=Project,Value=shop

$ aws dynamodb list-tags-of-resource --resource-arn arn:aws:dynamodb:ap-northeast-1:123456789012:table/Orders
{
    "Tags": [
        {
            "Key": "Env",
            "Value": "prod"
        },
        {
            "Key": "Project",
            "Value": "shop"
        }
    ]
}

$ aws dynamodb untag-resource --resource-arn arn:aws:dynamodb:ap-northeast-1:123456789012:table/Orders --tag-keys Project

RDS(リスト形・--resource-name に ARN)

$ aws rds add-tags-to-resource --resource-name arn:aws:rds:ap-northeast-1:123456789012:db:shop-db \
    --tags Key=Project,Value=shop Key=Owner,Value=team-web

$ aws rds list-tags-for-resource --resource-name arn:aws:rds:ap-northeast-1:123456789012:db:shop-db
{
    "TagList": [
        {
            "Key": "Env",
            "Value": "prod"
        },
        {
            "Key": "Project",
            "Value": "shop"
        },
        {
            "Key": "Owner",
            "Value": "team-web"
        }
    ]
}

$ aws rds remove-tags-from-resource --resource-name arn:aws:rds:ap-northeast-1:123456789012:db:shop-db --tag-keys Owner

RDS は、オプション名が --resource-name でも渡すのは ARN です。応答のキー名も Tags ではなく TagList になります。

IAM(リスト形・名前指定)

$ aws iam tag-role --role-name tag-role --tags Key=Env,Value=prod Key=Project,Value=shop

$ aws iam list-role-tags --role-name tag-role
{
    "Tags": [
        {
            "Key": "Env",
            "Value": "prod"
        },
        {
            "Key": "Project",
            "Value": "shop"
        }
    ]
}

$ aws iam untag-role --role-name tag-role --tag-keys Project

$ aws iam list-role-tags --role-name tag-role --query 'Tags[].[Key,Value]' --output text
Env prod

IAM は ARN ではなく名前で指定し、ロールとユーザーでコマンドが分かれています(tag-role / tag-user)。

リスト形とマップ形を取り違えるとどうなるか

リスト形のサービスにマップ形で書くと、送信前の検査で止まります。

$ aws dynamodb tag-resource --resource-arn arn:aws:dynamodb:ap-northeast-1:123456789012:table/Orders --tags Env=prod
Parameter validation failed:
Missing required parameter in Tags[0]: "Key"
Missing required parameter in Tags[0]: "Value"
Unknown parameter in Tags[0]: "Env", must be one of: Key, Value

逆に、マップ形のサービスにリスト形で書くと、エラーにならずに意図と違うタグが付きます。

$ aws lambda tag-resource --resource arn:aws:lambda:ap-northeast-1:123456789012:function:resize --tags Key=Env,Value=prod

$ aws lambda list-tags --resource arn:aws:lambda:ap-northeast-1:123456789012:function:resize --query Tags
{
    "Env": "prod",
    "Project": "shop",
    "Owner": "team-web",
    "Key": "Env",
    "Value": "prod"
}

Key というキーに Env、Value というキーに prod という2つのタグが付きました。Lambda・SQS・CloudWatch Logs・Resource Groups Tagging API はマップ形なので、キー=値,キー=値 で書きます。付けたあとは必ず確認コマンドで結果を見ておくと安心です。

タグでリソースを検索する

方法1:サービスのコマンドに --filters を付ける

EC2 の describe 系コマンドは、Name=tag:キー,Values=値 でタグによる絞り込みができます。

$ aws ec2 describe-instances --filters "Name=tag:Env,Values=prod" \
    --query 'Reservations[].Instances[].InstanceId' --output text
i-da0ee0aaf52d8284e i-b006b2173fbe1f1aa

$ aws ec2 describe-instances --filters "Name=tag:Env,Values=prod,stg" "Name=tag:Project,Values=shop" \
    --query 'Reservations[].Instances[].[InstanceId,Tags[?Key==`Env`]|[0].Value,Tags[?Key==`Project`]|[0].Value]' --output text
i-da0ee0aaf52d8284e prod    shop

$ aws ec2 describe-volumes --filters Name=tag:Env,Values=prod --query 'Volumes[].VolumeId' --output text
vol-948f81833904581f5   vol-8f5c58c1238d67cd1
フィルターの書き方 意味
Name=tag:Env,Values=prod Env が prod のもの
Name=tag:Env,Values=prod,stg Env が prod または stg(同じフィルター内の値は OR)
フィルターを2つ並べる 両方を満たすもの(フィルター同士は AND)
Name=tag-key,Values=Owner Owner というキーが付いているもの(値は問わない)
Name=tag:Project,Values=sh* 値が sh で始まるもの(ワイルドカード)
$ aws ec2 describe-instances --filters "Name=tag-key,Values=Owner" --query 'Reservations[].Instances[].InstanceId' --output text
i-da0ee0aaf52d8284e

$ aws ec2 describe-instances --filters "Name=tag:Project,Values=sh*" --query 'Reservations[].Instances[].InstanceId' --output text
i-da0ee0aaf52d8284e i-a7050b2ba7e73da43

あるキーが付いた EC2 リソースを種類をまたいで見たいときは、describe-tags が使えます。

$ aws ec2 describe-tags --filters Name=key,Values=Env --query 'Tags[].[ResourceType,ResourceId,Value]' --output text
instance    i-da0ee0aaf52d8284e prod
volume  vol-948f81833904581f5   prod
instance    i-a7050b2ba7e73da43 dev
instance    i-b006b2173fbe1f1aa prod
volume  vol-8f5c58c1238d67cd1   prod

方法2:resourcegroupstaggingapi get-resources でサービスを横断する

Resource Groups Tagging API を使うと、サービスをまたいで、同じタグが付いたリソースの ARN を1回で取得できます。

$ aws resourcegroupstaggingapi get-resources --tag-filters Key=Env,Values=prod \
    --query 'ResourceTagMappingList[].ResourceARN'
[
    "arn:aws:dynamodb:ap-northeast-1:123456789012:table/Orders",
    "arn:aws:sqs:ap-northeast-1:123456789012:tagq",
    "arn:aws:logs:ap-northeast-1:123456789012:log-group:/app/tag",
    "arn:aws:cloudwatch:ap-northeast-1:123456789012:alarm:tag-alarm",
    "arn:aws:s3:::rjc-tag-demo",
    "arn:aws:ssm:ap-northeast-1:123456789012:parameter/shop/key",
    "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:shop/key-lvIDxJ",
    "arn:aws:sns:ap-northeast-1:123456789012:tagt",
    "arn:aws:ec2:ap-northeast-1:123456789012:instance/i-da0ee0aaf52d8284e",
    "arn:aws:ec2:ap-northeast-1:123456789012:instance/i-b006b2173fbe1f1aa",
    "arn:aws:ec2:ap-northeast-1:123456789012:volume/vol-948f81833904581f5",
    "arn:aws:ec2:ap-northeast-1:123456789012:volume/vol-8f5c58c1238d67cd1",
    "arn:aws:lambda:ap-northeast-1:123456789012:function:resize",
    "arn:aws:rds:ap-northeast-1:123456789012:db:shop-db"
]

リソースの種類で絞るときは --resource-type-filters を足します。応答には、そのリソースのタグがすべて含まれます(一部を抜粋)。

$ aws resourcegroupstaggingapi get-resources --tag-filters Key=Env,Values=prod,dev --resource-type-filters ec2:instance
{
    "ResourceTagMappingList": [
        {
            "ResourceARN": "arn:aws:ec2:ap-northeast-1:123456789012:instance/i-da0ee0aaf52d8284e",
            "Tags": [
                {
                    "Key": "Env",
                    "Value": "prod"
                },
                {
                    "Key": "Project",
                    "Value": "shop"
                },
…(省略)…
オプション 書き方 補足(公式リファレンスより)
--tag-filters Key=Env,Values=prod,dev 同じキーの値は OR、フィルター同士は AND。キーは50個まで、1キーの値は20個まで
--tag-filters(キーのみ) Key=Project 値を問わず、そのキーが付いたリソース
--resource-type-filters ec2:instance s3 dynamodb サービス または サービス:リソース種別
--resources-per-page --resources-per-page 50 1ページの件数

get-resources が返すのは、指定したリージョンにある「タグが付いている、または以前付いていた」リソースです。別のリージョンを調べるときは --region を変えて実行します。

比べる点 サービスの --filters get-resources
対象 そのサービスのリソース 対応する複数のサービス
返る内容 リソースの詳細(状態、種類など) ARN とタグ
向いている使い方 状態と組み合わせた絞り込み、運用スクリプト タグの棚卸し、横断の一覧
一度もタグを付けていないリソース 取得できる 対象外

タグなしリソースを洗い出すワンライナー

タグが付いていないリソースは --filters では探せないので、--query で「そのキーが無いもの」を選びます。

$ aws ec2 describe-instances \
    --query 'Reservations[].Instances[?!not_null(Tags[?Key==`Env`].Value | [0])][].InstanceId' --output text
i-218af039af55059e1

not_null のかっこの中は Env タグの値を取り出す式で、タグが無ければ null になります。先頭の ! で反転させ、「値が無いもの」だけを残しています。タグが1つも付いていないインスタンスだけを探すなら、もっと短く書けます。

$ aws ec2 describe-instances --query 'Reservations[].Instances[?!Tags][].InstanceId' --output text
i-218af039af55059e1

必須にしたいタグを列に並べると、どこが欠けているかが一目で分かります。

$ aws ec2 describe-instances \
    --query 'Reservations[].Instances[].{Id:InstanceId,Env:Tags[?Key==`Env`]|[0].Value,Project:Tags[?Key==`Project`]|[0].Value,Owner:Tags[?Key==`Owner`]|[0].Value}' \
    --output table
--------------------------------------------------------
|                   DescribeInstances                  |
+------+-----------------------+-----------+-----------+
|  Env |          Id           |   Owner   |  Project  |
+------+-----------------------+-----------+-----------+
|  prod|  i-da0ee0aaf52d8284e  |  team-web |  shop     |
|  dev |  i-a7050b2ba7e73da43  |  None     |  shop     |
|  None|  i-218af039af55059e1  |  None     |  None     |
|  prod|  i-b006b2173fbe1f1aa  |  None     |  blog     |
+------+-----------------------+-----------+-----------+

None と表示された欄が、タグが付いていない箇所です。

複数サービスにまとめて付ける(tag-resources)

Resource Groups Tagging API の tag-resources は、ARN を並べて同じタグを一度に付けられます。

$ aws resourcegroupstaggingapi tag-resources \
    --resource-arn-list arn:aws:dynamodb:ap-northeast-1:123456789012:table/Orders \
                        arn:aws:lambda:ap-northeast-1:123456789012:function:resize \
    --tags Release=2026-10
{
    "FailedResourcesMap": {}
}

$ aws dynamodb list-tags-of-resource --resource-arn arn:aws:dynamodb:ap-northeast-1:123456789012:table/Orders \
    --query 'Tags[].[Key,Value]' --output text
Env prod
Release 2026-10

FailedResourcesMap が空なら、すべてのリソースに付いています。 付けられなかったリソースがあると、ARN ごとにエラーコードとメッセージが入ります。公式リファレンスによる注意点は次のとおりです。

項目 内容
1回で指定できるリソース数 20個まで
対象のリージョン 指定したリージョンのリソースだけ
必要な権限 tag:TagResources に加えて、各サービスのタグ付け権限(EC2 なら ec2:CreateTags)
対応サービス すべてのリソースが対応しているわけではない

外すときは untag-resources --resource-arn-list ARN ... --tag-keys Release です。

タグの命名ルールとキー設計

命名ルールの決め方

公式のベストプラクティスでは、次の点が挙げられています。

  • 大文字・小文字の書き方を1つに決め、すべてのリソースで同じにする(CostCenter と costcenter を混ぜない)。
  • 小文字とハイフンで区切る書き方や、組織名: のような接頭辞を付けて自分たちのタグだと分かるようにする書き方(例:anycompany:cost-center)が紹介されている。
  • 個人を特定できる情報や機密情報をタグに入れない。
  • タグは少なすぎるより、多めに付けておく。

どの書き方を選んでも、決めたルールを全員が同じように使うことがいちばんの要点です。

決めること 例A(パスカルケース) 例B(小文字とハイフン+接頭辞)
キーの書き方 CostCenter acme:cost-center
値の書き方 小文字とハイフン(team-web) 小文字とハイフン(team-web)
環境の値 prod / stg / dev の3つに固定 同じ
日付の値 2026-10-05(ISO 8601) 同じ
値が無いとき タグ自体を付けない 同じ

コスト配分タグ向けのキー設計例

「どの費用を、どの切り口で分けて見たいか」から逆算してキーを決めます。

キー 目的 値の例 必須
Project システムやサービスごとに費用を分ける shop blog 必須
Env 本番と検証の費用を分ける prod stg dev 必須
Owner 問い合わせ先のチームを示す team-web 必須
CostCenter 費用を負担する部門のコードで分ける cc-1001 必須
Service システム内の役割で分ける api batch web 任意
ManagedBy 作成手段を示す terraform cli console 任意
Name コンソールでの表示名 shop-prod-web-01 任意

値の種類が増え続けるもの(日時、ユーザー ID など)をコスト配分用のキーに使うと、集計の行が細かく分かれます。費用を分けたい切り口だけに絞るのがこつです。

コスト配分タグを有効にする

リソースにタグを付けただけでは、請求の集計には使われません。公式ドキュメントで確認した手順と注意点です。

項目 内容
有効化の場所 Billing and Cost Management コンソールの「Cost allocation tags」で、キーを選んで Activate
操作できるアカウント 組織の管理アカウント、または組織に属さない単独のアカウント
キーが一覧に出るまで タグをリソースに付けてから最大24時間
有効になるまで Activate してから最大24時間
さかのぼっての適用 されない。タグを付ける前に作られたリソースには適用されない
レポートでの表示 ユーザー定義のタグは user: の接頭辞が付く

CLI で有効にする場合は、Cost Explorer の update-cost-allocation-tags-status を使います。公式リファレンスの書式は次のとおりで、1回に20個までまとめて指定できます。

aws ce update-cost-allocation-tags-status \
  --cost-allocation-tags-status TagKey=CostCenter,Status=Active TagKey=Project,Status=Active

有効化はさかのぼって適用されないので、キーの設計とタグ付けは早い段階で済ませておくと、あとから集計できる期間が長くなります。

エラー文と直し方(実際の出力)

タグが50個を超えた

$ aws ec2 create-tags --resources i-218af039af55059e1 --tags Key=k0,Value=v Key=k1,Value=v …(51個)…

An error occurred (TagLimitExceeded) when calling the CreateTags operation: The maximum number of Tags for a resource has been reached.

IAM ロールでは、同じ内容が別のメッセージで返ります。

$ aws iam tag-role --role-name tag-role --tags Key=k0,Value=v Key=k1,Value=v …(51個)…

An error occurred (ValidationError) when calling the TagRole operation: 1 validation error detected: Value '[…(省略)…]' at 'tags' failed to satisfy constraint: Member must have length less than or equal to 50.

aws: で始まるキーを付けようとした

$ aws s3api put-bucket-tagging --bucket rjc-tag-demo --tagging 'TagSet=[{Key=aws:test,Value=x}]'

An error occurred (InvalidTag) when calling the PutBucketTagging operation: System tags cannot be added/updated by requester

エラーの早見表

メッセージ 原因 直し方
Unknown parameter in Tags[0]: "Env", must be one of: Key, Value リスト形のサービスにマップ形で書いた Key=Env,Value=prod に直す
TagLimitExceeded 1リソース50個の上限を超えた 使っていないタグを外す。キーを整理する
ValidationError ... Member must have length less than or equal to 50 同上(IAM) 同上
InvalidTag ... System tags cannot be added/updated by requester aws: で始まるキーを指定した 別の接頭辞にする
NoSuchTagSet ... The TagSet does not exist バケットにタグが1つも無い 異常ではない。空として扱う
エラーは出ないが Key Value というタグが付いた マップ形のサービスにリスト形で書いた Env=prod の形に直し、余分なタグを外す

boto3 で複数サービスに一括付与する

共通タグを EC2・S3・DynamoDB にまとめて付け、最後にタグで横断検索して確かめるスクリプトです。S3 は全置換なので、既存のタグを読んで合成してから書き戻しています。

tag_bulk.py:

"""共通タグを複数サービスのリソースへまとめて付ける。
使い方: python3 tag_bulk.py
"""
import boto3
from botocore.exceptions import ClientError

REGION = "ap-northeast-1"
COMMON = {"Project": "shop", "Env": "prod", "Owner": "team-web", "CostCenter": "cc-1001"}

ec2 = boto3.client("ec2", region_name=REGION)
s3 = boto3.client("s3", region_name=REGION)
ddb = boto3.client("dynamodb", region_name=REGION)
tagging = boto3.client("resourcegroupstaggingapi", region_name=REGION)

as_list = [{"Key": k, "Value": v} for k, v in COMMON.items()]

# 1. EC2: Env タグが無いインスタンスを探して、1回の呼び出しでまとめて付ける
ids = [
    i["InstanceId"]
    for page in ec2.get_paginator("describe_instances").paginate()
    for r in page["Reservations"]
    for i in r["Instances"]
    if "Env" not in {t["Key"] for t in i.get("Tags", [])}
]
if ids:
    ec2.create_tags(Resources=ids, Tags=as_list)
print("ec2      tagged:", ids)

# 2. S3: 全置換なので、既存タグを読んでから合成して書き戻す
def merge_bucket_tags(bucket: str, new_tags: dict) -> dict:
    try:
        current = {t["Key"]: t["Value"] for t in s3.get_bucket_tagging(Bucket=bucket)["TagSet"]}
    except ClientError as e:
        if e.response["Error"]["Code"] != "NoSuchTagSet":
            raise
        current = {}
    merged = {**current, **new_tags}
    s3.put_bucket_tagging(
        Bucket=bucket,
        Tagging={"TagSet": [{"Key": k, "Value": v} for k, v in merged.items()]},
    )
    return merged

print("s3       merged:", merge_bucket_tags("rjc-tag-demo", COMMON))

# 3. DynamoDB: ARN を指定して追加(既存タグは残る)
arn = ddb.describe_table(TableName="Orders")["Table"]["TableArn"]
ddb.tag_resource(ResourceArn=arn, Tags=as_list)
print("dynamodb tagged:", arn)

# 4. 結果をタグで横断検索して確かめる
found = []
for page in tagging.get_paginator("get_resources").paginate(
    TagFilters=[{"Key": "Project", "Values": ["shop"]}, {"Key": "Env", "Values": ["prod"]}]
):
    found += [m["ResourceARN"] for m in page["ResourceTagMappingList"]]
print(f"found {len(found)} resources")
for arn in sorted(found):
    print(" ", arn)
$ python3 tag_bulk.py
ec2      tagged: ['i-218af039af55059e1']
s3       merged: {'Env': 'prod', 'Owner': 'team-web', 'Project': 'shop', 'CostCenter': 'cc-1001'}
dynamodb tagged: arn:aws:dynamodb:ap-northeast-1:123456789012:table/Orders
found 9 resources
  arn:aws:dynamodb:ap-northeast-1:123456789012:table/Orders
  arn:aws:ec2:ap-northeast-1:123456789012:instance/i-218af039af55059e1
  arn:aws:ec2:ap-northeast-1:123456789012:instance/i-da0ee0aaf52d8284e
  arn:aws:ec2:ap-northeast-1:123456789012:volume/vol-948f81833904581f5
  arn:aws:lambda:ap-northeast-1:123456789012:function:resize
  arn:aws:rds:ap-northeast-1:123456789012:db:shop-db
  arn:aws:s3:::rjc-tag-demo
  arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:shop/key-lvIDxJ
  arn:aws:ssm:ap-northeast-1:123456789012:parameter/shop/key

タグが付いていなかったインスタンス(i-218af039af55059e1)に共通タグが付き、S3 は既存の Owner を残したまま4つのタグがそろいました。boto3 でも、EC2 と DynamoDB はリスト形、S3 は TagSet で包む形と、CLI と同じ違いがあります。

タグ運用のチェックリスト

確認すること 方法
キーの書き方(大文字・小文字、区切り)を決めた 命名ルールの表を1枚作る
必須のキーを決めた Project Env Owner CostCenter など
作成時にタグを付けている --tag-specifications や作成コマンドの --tags
タグなしのリソースを定期的に洗い出している --query のワンライナー、または boto3 のスクリプト
S3 バケットは読んでから合成して書いている get-bucket-tagging → 合成 → put-bucket-tagging
付けたあとに確認コマンドで結果を見ている サービス別の「確認する」コマンド
コスト配分タグを有効にした Billing and Cost Management コンソール、または aws ce update-cost-allocation-tags-status

よくある質問

Q. すでにあるタグの値を変えるには? A. 同じキーでもう一度付けます。EC2 の create-tags や Lambda の tag-resource で試すと、同じキーの値が上書きされ、ほかのタグは残りました。S3 の put-bucket-tagging だけは全体を置き換えるので、既存のタグも一緒に指定します。

Q. タグが付いていないリソースを探すには? A. --filters は「付いているもの」を探すオプションなので、--query を使います。EC2 なら Reservations[].Instances[?!Tags][].InstanceId でタグが1つも無いインスタンス、!not_null(...) を使った式で特定のキーが無いインスタンスを取り出せます。

Q. create-tags と tag-resource のどちらを使えばよいですか? A. サービスによって決まっています。EC2 は create-tags、Lambda・DynamoDB・SNS などは tag-resource、RDS と SSM は add-tags-to-resource、S3 は put-bucket-tagging です。冒頭の早見表で、サービスの行を確認してください。

Q. タグを付けたのに、コストの画面に出てきません。 A. コスト配分タグとして有効にする操作が別に必要です。公式ドキュメントによると、キーが一覧に現れるまで最大24時間、有効にしてから反映されるまでさらに最大24時間かかります。有効にする前の期間にはさかのぼって適用されません。

まとめ

  • タグ付けコマンドはサービスごとに違う。EC2 は create-tags、S3 は put-bucket-tagging、Lambda・DynamoDB は tag-resource、RDS は add-tags-to-resource、IAM は tag-role。
  • --tags にはリスト形(Key=Env,Value=prod)とマップ形(Env=prod)がある。取り違えると、エラーになるか、意図と違うタグが付く。
  • S3 の put-bucket-tagging は全置換。既存のタグを読んで合成してから書き戻す。
  • 検索は2通り。サービス内は --filters "Name=tag:Env,Values=prod"、サービス横断は resourcegroupstaggingapi get-resources。
  • タグなしのリソースは --query で洗い出す。?!Tags や !not_null(...) を使う。
  • キーは大文字・小文字まで区別される。命名ルールを先に決め、全員で同じ書き方にする。
  • コスト配分タグは有効化が必要で、さかのぼって適用されない。早めに設計して付けておく。

参考・出典

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「AWSタグの付け方|AWS CLIでタグ付け・検索・一覧取得と命名ルール」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?