tech.guitarrapc.cóm

Technical updates

AWSのmyApplicationをTerraformで構築する

AWSにはmyApplicationというサービスがあります。以前からTerraformでmyApplicationをどのように構築するのがいいかと悩んでいましたが、納得感のあるパターンが決まったのでメモです。

myApplicationとは

myApplicationは、AWSリソースをアプリケーション単位でグループ化して、リソースの状態やコストを網羅的に把握できるようにするサービスです。特にリージョンを跨いでリソース表示できるのがAWSにおいて貴重な機能です。 無料ですし、タグでリソースを紐づけるだけで使えるので使い勝手もいいです。

AWSコンソールにmyApplicationが表示される

myApplicationの画面は紐づけられているリソースやコストが表示されます。特にリソースは、東京リージョンで見ているのにGlobalとかも見えていますね。

myApplicationの表示

myApplicationのリソース

myApplicationとAWSリソースの関係

AWSリソースを特定のmyApplicationに関連付けるためには、myApplicationのリソースIDをAWSリソースのタグに設定する必要があります。このmyApplicationのリソースIDは次のような形式で、ランダムな文字列が含まれています。

# myApplicationのリソースIDパターン
arn:aws:resource-groups:{リージョン}:{アカウントID}:group/{myApplication名}/{ランダムな文字列}

# 例
arn:aws:resource-groups:ap-northeast-1:123456789012:group/foo/km3hcsjf2h5izwou4jxqdvt7cz

AWSリソースのタグに「キーにawsApplication、値にmyApplicationのリソースID」を設定するとそのリソースをmyApplicationに関連付けることができます。

awsApplication: <myApplicationのリソースID>

TerraformでmyApplicationを構築する

厄介なポイントはmyApplicationのリソースIDがランダムな文字列を含むため、myApplicationを作成した後にそのリソースIDを取得して、AWSリソースのタグに設定する必要がある点です。 Terraformで管理しやすい方法は2つ考えられます。

  • myApplicationを作成するtfstateとAWSリソースを作成するtfstateを分ける
  • myApplicationを作成してからdefault_tagsにmyApplicationのリソースIDをハードコードで設定する
  • awscc_resourcegroups_tag_sync_taskを使って自動同期させる

myApplicationを作成するtfstateとAWSリソースを作成するtfstateを分ける

tfstateを分けることで、myApplicationのリソースIDを取得してからAWSリソースのタグに設定できるのでランダムなIDを意識せずに済みます。 例えば次のようにmyApplicationを作成するtfstateとAWSリソースを作成するtfstateを分けて、myApplicationのリソースIDを取得してからAWSリソースのタグに設定します。

resource "aws_servicecatalogappregistry_application" "main" {
  name        = "foo"
  description = "Terraform managed."
}

outputs "my_application_id" {
  description = "myApplicationのリソースID"
  value = aws_servicecatalogappregistry_application.main.id
}

利用側で、ステートからmyApplicationのリソースIDを取得して、AWSリソースのタグに設定します。

data "tfe_outputs" "foo" {
  organization = "my-org"
  workspace = "my-workspace"
}

provider "aws" {
  region     = "ap-northeast-1"
  default_tags {
    tags = {
      awsApplication = data.tfe_outputs.foo.values.my_application_id
      # 他のタグ
    }
  }
}

myApplicationを作成してからdefault_tagsにmyApplicationのリソースIDをハードコードで設定する

default_tagsにmyApplicationのリソースIDをハードコードで設定します。タグは影響度が低いので、myApplicationのリソースIDは割り切ってしまっていい判断です。 設定がシンプルかつ、やっていることが一目瞭然で管理しやすいのがメリットです。 一方で、もしmyApplicationを作り直すときは、myApplicationのリソースIDを手動で変更する必要があります。

provider "aws" {
  region     = "ap-northeast-1"
  default_tags {
    tags = {
      awsApplication = "arn:aws:resource-groups:ap-northeast-1:123456789012:group/foo/km3hcsjf2h5izwou4jxqdvt7cz"
      # 他のタグ
    }
  }
}

awscc_resourcegroups_tag_sync_taskを使って自動同期させる

AWSコンソールからmyApplicationを作成するときAutomatically add resources using tagsというオプションがあり、これで作ると指定したタグを持つAWSリソースに対してmyApplicationのタグを自動的に設定してくれます。仕組み的には、EventBridgeとIAM Roleを組み合わせています。

Automatically add resources using tags

これと同じことをterraformで実現するのがawscc_resourcegroups_tag_sync_taskです。 残念ながらawsプロバイダーでは提供されておらず、awsccプロバイターを使う必要がありますが、人によってはこれも便利でしょう。ただし、TerraformリソースのデフォルトタグにmyApplicationのリソースIDを設定できないので、ignore_tagsを設定する必要があります。

# managedBy: terraformというタグを持つリソースをmyApplication fooに紐づける
resource "awscc_resourcegroups_tag_sync_task" "main" {
  group = "foo"
  role_arn = "IAM RoleのARN"
  tag_key = "managedBy"
  tag_value = "terraform"
}

# デフォルトタグにawsApplicationを設定できないので、ignore_tagsを設定する
provider "aws" {
  region     = "ap-northeast-1"
  ignore_tags = {
    keys = ["awsApplication"]
  }
}

まとめ

私はmyApplicationを作成してからdefault_tagsにmyApplicationのリソースIDをハードコードで設定する方法を主に使っています。 tfstateを参照する方法は、やりたいことに対して大げさすぎるんですよね。myApplicationを作るのは1回だけでしょうし、タグに紐づけたいだけなのでハードコードでも十分メンテできる判断です。

myApplicationのリソースIDからランダム文字列が消えれば扱いやすいのですが残念です。

参考