AWS CDKで付与された共通タグの一部を無効にしたい - Tags クラス

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

こんにちは、近藤(りょう)です!

環境タグやシステム名タグなど、AWS上のリソースへ共通のタグを付与したい場面はよくあります。 AWS CDK では Tags.of(<SCOPE>).add(Key, Value) を利用することで、リソースに共通タグを一括で付与できます。

ところが、aws-cdk-lib をアップデートしたタイミングで、共通タグの影響によりリソースの置換(Replacement)が発生する事態が起きました。

本記事では、この事象が発生した時の内容と原因、そして対策をした「CDKで共通タグ付与を維持したまま特定リソースへのタグだけを無効化する方法」について解説します。

前提

本記事で扱う環境や構成の前提は以下の通りです。

  • CDKの実装言語は TypeScript
  • CDKは Service Catalogの製品を作成
  • 利用者が Service Catalogの製品をプロビジョニングすると Cost Anomaly Monitor(コストモニター)を自動作成
  • aws-cdk-lib は v2.147.0 から v2.214.0 にアップデート
  • 共通タグは Tags.of(<SCOPE>).add('Owner', 'Service Catalog') で付与

※本記事では「Service Catalogの細かい仕組みの解説」は対象外とし、タグ起因の Replacement 回避に内容を絞って解説します。

AWS::CE::AnomalyMonitor について

AWS::CE::AnomalyMonitor のドキュメントによると、以下のプロパティは変更時に必ず置換(Replacement)が発生します。

プロパティ 更新時の挙動
MonitorDimension Replacement
MonitorSpecification Replacement
MonitorType Replacement
ResourceTags Replacement

■ 参考ドキュメント docs.aws.amazon.com

今回の事象では、aws-cdk-lib を v2.147.0 → v2.214.0 にアップデートした際に ResourceTags がテンプレートへ追加されました。 その結果、CloudFormation で更新があったと判定され、置換(Replacement)が発生しました。 置換(Replacement)は「新しいリソースを作成してから既存リソースを削除する」動作となります。

Tags.of() について

CDKでタグを制御する際に利用するのが Tags.of(<SCOPE>) です。
この仕組みでは、あるコンストラクトに付与したタグが配下(子コンストラクト)のリソースにも継承されます。

  • Tags.of(<SCOPE>).add()
    指定されたコンストラクトおよびそのすべての子に新しいタグを適用します。
  • Tags.of(<SCOPE>).remove()
    子コンストラクトがそれ自体に適用したタグなど、特定のコンストラクトおよびその子からタグを削除します。

■ 参考ドキュメント docs.aws.amazon.com

Tags.of() の使用例

実際に addremove を使ったサンプルコードを示します。

■ CDK プロジェクトのフォルダ構成(TypeScript) 例

cdk-tag-sample/
├── bin/
│   └── tag-sample.ts        # Appエントリーポイント(共通タグをadd)
├── lib/
│   └── tag-sample-stack.ts  # Stack内でremoveを適用
├── test/
│   └── tag-sample.test.ts   # (任意) CDKのユニットテスト
├── cdk.json
├── package.json
├── tsconfig.json
└── README.md

■ bin/tag-sample.ts(App のエントリーポイント)

import * as cdk from 'aws-cdk-lib';
import { TagSampleStack } from '../lib/tag-sample-stack';

const app = new cdk.App();

// App 全体に共通タグを付与(全スタック共通で付与したいケース)
cdk.Tags.of(app).add('Owner', 'me');
cdk.Tags.of(app).add('Environment', 'Prod');

new TagSampleStack(app, 'TagSampleStack', {});

lib/tag-sample-stack.ts(Stack内で一部リソースだけ remove する)

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as sns from 'aws-cdk-lib/aws-sns';

export class TagSampleStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    //
    // App で付与した共通タグは Stack 配下のリソース全てに継承される
    //

    // 例:共通タグをそのまま持つ S3 バケット
    const bucket = new s3.Bucket(this, 'SampleBucket', {
      versioned: true,
    });

    // 例:共通タグは継承するが Owner タグだけ除外したい SNS トピック
    const topic = new sns.Topic(this, 'SampleTopic', {});

    // 特定リソースから "Owner" タグだけ無効化
    cdk.Tags.of(topic).remove('Owner');
  }
}

その他のファイルは、割愛します。

v2.147.0 から v2.214.0 にアップデートした時の cdk synth の差分

cdk synth を比較すると、v2.214.0 では ResourceTags に追加されていることが確認できます。

■ before: v2.147.0

{
  "Type": "AWS::CE::AnomalyMonitor",
  "Properties": {
    "MonitorDimension": "SERVICE",
    "MonitorName": "cost-alarm",
    "MonitorType": "DIMENSIONAL"
  }
}

■ after: v2.214.0

{
  "Type": "AWS::CE::AnomalyMonitor",
  "Properties": {
    "MonitorDimension": "SERVICE",
    "MonitorName": "cost-alarm",
    "MonitorType": "DIMENSIONAL",
    "ResourceTags": [
      {
        "Key": "Owner",
        "Value": "Service Catalog"
      }
    ]
  }
}

Service Catalogでの更新処理の挙動(Service Catalogステータス: 汚染)

今回の事象は次のような流れで発生しました。

  1. CDK CDK v2.147.0 で作成したスタックを Service Catalogの製品として登録
  2. Service Catalogの製品をメンバーアカウントにプロビジョニング
  3. CDK v2.214.0 にバージョンアップ後、CDKをデプロイして Service Catalogの製品をリリース
  4. Service Catalogで既に プロビジョニングしてある製品を更新
  5. 共通タグの影響で 一部リソースで置換(Replacement) が発生
  6. プロビジョニングされた製品の更新が失敗(ステータス: 汚染)

以下、3~4のイメージとなります。

事象のイメージ

Service Catalogで「プランの作成」を行うと、Replacement: True と判定されます。

Service Catalogのプランの作成 - Replacement: True

その後、Service Catalog で対象のプロビジョニングされた製品を更新すると「汚染」のステータスとなります。

Service Catalog - ステータス: 汚染

置換(Replacement)が発生すると、新規作成 → 旧リソース削除の流れになりますが、今回は同名で既存リソースが存在するため、エラーとなりました。

Service Catalog でのエラーメッセージ

- AlreadyExists
- Requested update requires the creation of a new physical resource

CloudFormation でのエラーメッセージ

CloudFormation - エラーメッセージ

解決策:該当リソースのみタグを除外する

今回は既存リソースを置換を発生させずに運用を継続したかったため、問題となる CfnAnomalyMonitor だけタグを付与しないように remove() を追加しました。

Before: 全リソースに共通タグを付与(例:Tags.of(xxx).add('Owner', 'xxxx')
After: コントラスト内のTags.of(xxx).remove('Owner') を追記し、特定リソースのみ除外

これにより ResourceTags の差分がなくなり、置換(Replacement)も発生しなくなります。

v2.214.0 で特定リソースへ Tags.of(xxx).remove('Owner') を適用した場合の cdk synth 差分

Tags.of(xxx).remove('Owner') を追加したことで、AnomalyMonitor の ResourceTags がテンプレートから消えたことが確認できます。

■ after: v2.214.0

{
  "Type": "AWS::CE::AnomalyMonitor",
  "Properties": {
    "MonitorDimension": "SERVICE",
    "MonitorName": "cost-alarm",
    "MonitorType": "DIMENSIONAL"
  }
}

Service Catalog での更新処理の挙動(Service Catalogステータス: 使用可能)

CDK v2.214.0 にバージョンアップ + Tags.of(xxx).remove('Owner')を追記した後に CDKを再デプロイしました。 以下、対処後のイメージとなります。

対処のイメージ

Service Catalogで既存のプロビジョニングされている製品を「プランの作成」を行うと、Replacement: True が表示されないことを確認できました。

Service Catalog のプランの作成 - Replacement: True なし

Service Catalog のプロビジョニング製品を更新したところ、「汚染」状態が解消され、ステータスが「使用可能」となり更新が成功しました。(実際はTagsを無効化しているので更新されていません。)

Service Catalog - ステータス: 使用可能

補足:選択した理由

リソースを作り直すのが最もシンプルな対応ですが、本システムは数千単位で利用されており、削除 → 再作成を伴う更新はユーザー影響が非常に大きい状況でした。 そのため、該当リソースのみタグを除外する = 最もユーザー影響の少ない方法 という判断をし、現実的かつ運用継続性の高い対応を選択しました。


まとめ

  • CDKのバージョンアップにより ResourceTags が追加され、置換(Replacement)が発生する場合がある
  • CfnAnomalyMonitor はタグ追加でも置換(Replacement)が発生する仕様のため注意が必要
  • Tags.of(<SCOPE>).remove(Key) により、共通タグを維持したままピンポイントでタグ付与の回避可能

頻繁に遭遇する問題ではありませんがは高くありませんが、Tagsのトラブルシューティングの助けになれば幸いです。

近藤 諒都

(記事一覧)

カスタマーサクセス部CS5課

夜行性ではありません。朝活派です。

趣味:お酒、旅行、バスケ、掃除、家庭用パン作り(ピザも)など

2025 Japan AWS All Certifications Engineers