Claude Media
Claude Codeでコスト配分タグを整える — default_tagsと未タグ洗い出し

Claude Codeでコスト配分タグを整える — default_tagsと未タグ洗い出し

AWSのコスト配分タグをTerraformのdefault_tagsで一括付与し、未タグやEnv/envの表記ゆれをClaude Codeに読み取り専用で集計させる手順です。権限設定とplan差分の確かめ方を示します。

AWSのコスト配分タグを整える作業は、Claude Codeに「タグを読むコマンドだけ」を許可して現状を集計させ、直し方はdefault_tagsの差分とplanで確かめる流れにすると、人が見る場所を絞れます。タグの付け外しをCLIで直接やらせず、Terraformのコードに戻して直す。この分担が本記事の設計です。

先に落とし穴を1つ。タグ付きリソースを一覧するAPIは、タグが1つもないリソースを返しません。未タグには2種類あり、拾う道具が違います。以下、その切り分けから順に進めます。

「未タグ」には2種類あり、拾う道具が違う

くらべる

未タグの2種類と、見つける手段

Resource Explorerで拾う

タグが1つもない

resourcegroupstaggingapiのGetResourcesは、タグ付き、または過去にタグ付きだったリソースを返します。タグなしのリソースは返りません。公式の注記は、Resource Explorerのクエリtag:noneを使うよう案内しています。

GetResourcesで拾う

タグはあるが必須キーがない

EnvironmentやCostCenterなどのキーだけが欠けているリソースは、GetResourcesの結果に現れます。返ってきたタグの一覧から、必須キーを持たないものを選び出します。

GetResourcesは、指定した1つのリージョンの結果を返します。複数リージョンを使っているアカウントでは、リージョンごとに繰り返します。結果は複数ページに分かれますが、AWS CLIは既定でページを順に取得します。

Resource Explorerを使う前提

tag:noneのクエリは、Resource Explorerのビューを通して検索されます。ビューを明示しなければ、呼び出したリージョンの既定ビューが使われます。ビューが未設定のアカウントでは、先に設定が要ります。設定は書き込みを伴うので、Claudeに任せず人が行う作業として切り分けます。

default_tagsの仕様を先に押さえる

洗い出した結果をTerraformに戻す前に、AWSプロバイダーのdefault_tagsが何をするかを確認します。

provider "aws" {
  region = "ap-northeast-1"
 
  default_tags {
    tags = {
      Environment = "production"
      Owner       = "platform-team"
      CostCenter  = "cc-1001"
    }
  }
}

プロバイダーのドキュメントとタグ付けガイドから、運用に効く点を並べます。

default_tagsで押さえる仕様

  • 付与先は、tags引数を持つAWSリソース全般です。例外としてaws_autoscaling_groupは対象外と明記されています
  • リソース側のtagsに同じキーを書くと、その値が優先されます。上書きはできても、特定のリソースだけ除外はできません
  • 結合後のタグは読み取り専用のtags_all属性で確認できます。tagsにはリソースに直接書いた分だけが入ります
  • 環境変数TF_AWS_DEFAULT_TAGS_<キー名>でも渡せます。同じキーがプロバイダー設定にもあれば、設定側が優先されます

もう1点、ガイドには次の記述があります。Terraformが管理するタグ以外、つまりTerraformの外から付いたaws:で始まらないタグは、次回の実行で削除が提案されます。コンソールで手付けしたenvタグが残っているリソースにdefault_tagsを入れると、planにそのタグの削除が出る可能性があります。後述のplan確認で最初に見るのはここです。

外部のシステムが付けるタグを残したいときは、プロバイダーのignore_tagsが使えます。keysで完全一致、key_prefixesで前方一致を指定できます。

Claude Codeに渡す権限

読み取りに使うコマンドだけをallowに並べ、タグの書き込み系は明示的にdenyします。プロジェクトの.claude/settings.jsonに置く例です。

{
  "permissions": {
    "allow": [
      "Bash(aws resourcegroupstaggingapi get-resources *)",
      "Bash(aws resource-explorer-2 search *)",
      "Bash(jq *)",
      "Bash(terraform validate *)",
      "Bash(terraform plan *)"
    ],
    "ask": [
      "Bash(terraform apply *)"
    ],
    "deny": [
      "Bash(aws resourcegroupstaggingapi tag-resources *)",
      "Bash(aws resourcegroupstaggingapi untag-resources *)",
      "Bash(aws ec2 create-tags *)",
      "Bash(aws ec2 delete-tags *)",
      "Bash(terraform destroy *)"
    ]
  }
}

権限設定のページに書かれた、この構成で効く規則を3つ挙げます。

  1. *はサブコマンドの後ろに置きます。末尾に空白付きの*だけがある規則は、引数なしのコマンドにも一致します。
  2. パイプでつないだ複合コマンドは、各部分がそれぞれ許可されている必要があります。jqは組み込みの読み取り専用コマンドの例に挙がっていないので、上のように個別に許可しています。
  3. 許可ルールは、コマンドの文字列で照合されます。--profileと--regionは、サブコマンドの後ろに付けるようClaudeに書かせます。aws --profile audit resourcegroupstaggingapi ...の形では、上のルールに一致しません。

denyルールは、絶対パスでの呼び出しやsh -cの中身までは止めません。ページの表もBash(rm *)を例に同じ限界を示しています。書き込みを確実に防ぐには、Claudeが使う認証情報を、タグの読み取りとplanに必要な範囲のIAM権限に絞ります。ルールは振る舞いを整える層、IAMは最終防壁と役割を分けます。サンドボックスで範囲を絞る方法は、allowWriteとdenyWriteの記事にあります。

タグの現状を集計する

ここからは、Claude Codeに出させる集計の中身です。まず、使われているタグキーを頻度順に並べます。aws:で始まるキーはAWSが管理するので除きます。

aws resourcegroupstaggingapi get-resources \
  --region ap-northeast-1 --profile audit --output json \
  | jq '[.ResourceTagMappingList[].Tags[].Key
         | select(startswith("aws:") | not)]
        | group_by(.) | map({key: .[0], count: length})
        | sort_by(-.count)'

Environmentが120件、envが35件、environmentが8件といった結果になれば、表記ゆれが見えます。キーは文字列として別物なので、集計でも別の行になります。どれを正とするかは人が決め、決めた名前をClaudeに伝えます。

次に、必須キーEnvironmentを持たないリソースのARNを出します。

aws resourcegroupstaggingapi get-resources \
  --region ap-northeast-1 --profile audit --output json \
  | jq -r '.ResourceTagMappingList[]
           | select(([.Tags[].Key] | index("Environment")) == null)
           | .ResourceARN'

タグが1つもないリソースは、Resource Explorerで拾います。

aws resource-explorer-2 search \
  --region ap-northeast-1 --profile audit \
  --query-string "tag:none"

2つの結果を合わせれば、「キーが足りない」と「タグが1つもない」の両方を網羅できます。ARNからリソースタイプとサービスが分かるので、Claudeには次のように指示すると、Terraform側の作業単位に落とせます。

上の2つの出力から、リソースを種類ごとに分類してください。
- 対応する aws_* リソースの種類
- Terraform管理かどうか(terraform state list の結果と突き合わせる)
- 管理外のものは、候補として一覧にするだけで、タグ付けはしない

Terraformの管理下にあるかは、terraform state listで確かめられます。このコマンドは上の許可リストに入れていないので、使うなら実行前にallowへ追加します。

直す場所はTerraformに集める

集計の結果を、直し方ごとに3つに分けます。

作業単位

直し方の3分類

  • 共通キーが欠けている

    default_tagsに追加します。リソースごとのtagsに同じキーが書かれていれば、重複を取り除きます。

  • 表記ゆれがある

    正とするキーに統一します。リソース側に古い表記のtagsが書かれていれば、書き換えます。

  • Terraform管理外

    コードに取り込むか、タグ付けを別の手順に回します。取り込みはTerraform importの記事の流れで進めます。

CLAUDE.mdには、正とするキーの決まりを短く書いておくと、Claudeが新しいリソースを書くときにも守ります。

## AWSタグの規約
- 共通タグは provider の default_tags に置く。リソースの tags には書かない
- キーは Environment / Owner / CostCenter の3つ。env や environment は使わない
- リソース固有の値だけ tags に書く(値を上書きする場合を含む)
- terraform apply は実行しない。plan の結果を報告して止まる

planの差分で確かめる

default_tagsを足して表記を統一したら、terraform planを実行させて、差分を次の順に読ませます。

手順

plan結果の確認順

  1. 1

    作り直しや削除がないか

    リソースの置き換えや削除が1件でもあれば、そこで止めます。タグの変更だけなら、リソースはその場で更新されます。

  2. 2

    タグの削除が出ていないか

    Terraformの外から付いたタグは、削除が提案されます。envのような古い表記が消える差分は、意図どおりかを1件ずつ確認します。残したいタグはignore_tagsに回します。

  3. 3

    tags_allに共通キーが入っているか

    default_tagsの追加は、tags_allの変更として現れます。リソース側のtagsが空でも、tags_allに3つの共通キーが入っていれば意図どおりです。

  4. 4

    差分の件数が洗い出しと合うか

    集計で出した対象リソースの件数と、planの更新件数が大きくずれていないかを見ます。ずれが大きければ、管理外のリソースや別のリージョンが混ざっています。

適用は人が行います。上の設定でterraform applyはaskにしてあるので、Claudeが実行しようとすると確認が出ます。差分に作り直しや削除がないことを確かめてから承認します。

ドリフトを検出して分類する手順も、同じ考え方でplan差分を読みます。コンソールで付けたタグの食い違いは、ドリフトとして扱うこともできます。

コスト配分タグとして効かせる最後の一手

タグを揃えただけでは、請求の画面には現れません。AWSのドキュメントによると、コスト配分タグは次の手順を踏んでから使えます。

  • ユーザー定義タグのキーを、Billing and Cost Managementのコンソールで有効化します。有効化はキー単位で、同じキーの値すべてに効きます
  • 付与から有効化の候補に出るまで最大24時間、有効化されるまでさらに最大24時間かかります
  • コスト配分タグの管理画面を使えるのは、組織の管理アカウント、または組織に属さない単独のアカウントだけです

UpdateCostAllocationTagsStatusというAPIで一括有効化もできます。これは課金のレポートに影響する操作なので、Claudeの許可リストには入れず、人が実行します。

有効化の効果は、Cost Explorerなどの画面で確認できます。コスト配分レポートには、タグ付きと未タグのリソースの両方が含まれます。未タグ分の金額が減っていけば、整理が効いている目安になります。請求額の増減を使用タイプまで分解する方法は、Cost ExplorerのCSVで急増を調べる記事にあります。

よくあるつまずき

落とし穴

aws_autoscaling_groupだけタグが付かない

default_tagsはaws_autoscaling_groupを対象外としています。このリソースには、個別のtagsブロックで明示します。

落とし穴

特定のリソースだけ共通タグを外せない

default_tagsは上書きはできても、除外はできません。外したいリソースが多いなら、プロバイダーを別名(alias)で分け、そのプロバイダーにはタグを定義しないという構成を検討します。

落とし穴

get-resourcesの結果に、既知のリソースが出てこない

指定したリージョンのリソースしか返りません。また、タグが1つもないリソースは返りません。後者はResource Explorerのtag:noneで拾います。

未使用のリソースを見つけて消す作業は、タグの整理とは別の作業です。そちらは未使用リソースの洗い出し記事にまとめています。コスト配分タグでは「誰のコストか」を付け、未使用の洗い出しでは「使われているか」を見る。順序としては、タグを先に揃えておくと、後者の判断材料にも使えます。

まとめ

タグ整理の安全性は、Claudeに任せる範囲を「読む」までに絞ることで決まります。書き込み系のコマンドをdenyし、直し方はdefault_tagsとtagsの差分としてコードに集め、planで作り直しとタグ削除がないことを確かめてから適用する。この3点が揃えば、人が見るのはplanの差分1か所です。

最後に、有効化を忘れると請求画面には何も現れません。Terraformの適用後、最大で48時間ほど待ってから、Cost Explorerでタグ別の表示を確認します。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. code.claude.com/docs/en/permissions
  2. registry.terraform.io/providers/hashicorp/aws/latest/docs
  3. registry.terraform.io/providers/hashicorp/aws/latest/docs/guides/resource-tagging
  4. docs.aws.amazon.com/cli/latest/reference/resourcegroupstaggingapi/get-resource…
  5. docs.aws.amazon.com/cli/latest/reference/resource-explorer-2/search.html
  6. docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/cost-alloc-tags.html
  7. docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/activating-tags.html
この記事を共有:XはてブLinkedIn