1. はじめに
前回の記事でTerraformのcan関数について軽く触れていたので、今回はその紹介のための記事を投稿しようと思います。
使ってみた感想としては、汎用的なコードの書き方ができそうだなと思いました。
2. can関数について
公式ドキュメント:https://developer.hashicorp.com/terraform/language/functions/can
can関数で指定した式に対してboolに変換して値を返してくれるような関数になります。
公式ドキュメントから以下の例を参照します。
> local.foo
{
"bar" = "baz"
}
> can(local.foo.bar)
true
> can(local.foo.boop)
false
上記の例の場合、local.foo
ではbar
という変数が定義されているため
can(local.foo.bar)
では正しく値を取得できるので返り値がtrue
となります。
反対にcan(local.foo.boop)
とすると、boop
変数というのは定義されておらず、
正しく値を取得できずエラーとなるため、false
が返ってきます。
今回はこちらの関数を活用して、コードの作成を行っていきます。
3. 構成
3-1. 作成するリソース
S3バケットを2つ作成して、それぞれ別のキー値を持つタグの設定をしていこうと思います。
AのS3バケット
・Name:temp-bucket-a
・TempA:true
BのS3バケット
・Name:temp-bucket-b
・TempB:true
3-2. フォルダ構成
以下のようなフォルダ構成で実装していきます。
リソース名などは変数管理としたいため、Local Valuesを利用します。
参考:Local Values
Terraform/
└─ dev
├─ s3.tf
├─ locals.tf
└─ terraform.tf
4. 実装
4-1. terraform.tfとlocals.tf
can関数を利用することでどのくらいコードに差が出るのかを比較したいので、2通りのコードを紹介します。
terraform.tf
とlocals.tf
は以下のコードを共通で利用します。
# providerの定義
provider "aws" {
region = "ap-northeast-1"
}
terraform {
required_version = ">=1.0"
required_providers {
awscc = {
source = "hashicorp/awscc"
version = "0.45.0"
}
}
}
# 実行するAWSアカウント情報取得用
data "aws_caller_identity" "self" {}
# 実行するリージョン情報取得用
data "aws_region" "self" {}
locals {
s3_bucket = {
temp-bucket-a = {
tags = {
name = "temp-bucket-a"
temp-a = "true"
}
}
temp-bucket-b = {
tags = {
name = "temp-bucket-b"
temp-b = "true"
}
}
}
}
4-2. can関数を使わない場合
キーの異なるタグを付与する場合は、resourceを2つに分けて作成する必要があるかと思います。
resource "aws_s3_bucket" "temp-a" {
for_each = local.s3_bucket.temp-bucket-a
bucket = "temp-bucket-a-${data.aws_caller_identity.self.account_id}"
tags = {
Name = each.value.name
TempA = each.value.temp-a
}
}
resource "aws_s3_bucket" "temp-b" {
for_each = local.s3_bucket.temp-bucket-b
bucket = "temp-bucket-b-${data.aws_caller_identity.self.account_id}"
tags = {
Name = each.value.name
TempB = each.value.temp-b
}
}
上記の書き方でも問題なく実行はできますが、ほとんど同じコードを複数書く必要があるのであまりスマートなコードとは言えないような気がします。
そこでcan関数の出番です。
4-3. can関数を使う場合
can関数を使うことで、タグのキーが異なっていてもresource "aws_s3_bucket"
を一つ書いておけば、複数のバケットが作成できるようになります。
以下のようにコードを書き換えていきます。
resource "aws_s3_bucket" "this" {
for_each = local.s3_bucket
bucket = "${each.key}-${data.aws_caller_identity.self.account_id}"
tags = {
Name = each.value.tags.name
TempA = can(each.value.tags.temp-a) ? each.value.tags.temp-a : null
TempB = can(each.value.tags.temp-b) ? each.value.tags.temp-b : null
}
}
resource "aws_s3_bucket"
の記載が一つで済み、行数が格段に少なくなりました。
can関数を利用している箇所について、以下の通り簡単に解説します。
TempA = can(each.value.tags.temp-a) ? each.value.tags.temp-a : null
TempB = can(each.value.tags.temp-b) ? each.value.tags.temp-b : null
Local ValuesにTempAもしくはTempBのキーが存在するかどうかをcan関数の部分で確認しています。
AのS3バケットではtemp-a
という変数を定義しているのでその値を利用し、temp-b
という変数は定義していないためnull
を使用します。
BのS3バケットについてはAと逆の結果となります。
4-4. AWSマネジメントコンソールに展開
4-3のコードで実際にterraform apply
してみます。
PS C:\terraform\dev\s3_qiita> terraform apply
data.aws_region.self: Reading...
data.aws_caller_identity.self: Reading...
data.aws_region.self: Read complete after 0s [id=ap-northeast-1]
data.aws_caller_identity.self: Read complete after 0s [id={accountid}]
Terraform used the selected providers to generate the following execution plan. Resource actions are
indicated with the following symbols:
+ create
Terraform will perform the following actions:
# aws_s3_bucket.this["temp-bucket-a"] will be created
+ resource "aws_s3_bucket" "this" {
+ acceleration_status = (known after apply)
+ acl = (known after apply)
+ arn = (known after apply)
+ bucket = "temp-bucket-a-{accountid}"
+ bucket_domain_name = (known after apply)
+ bucket_prefix = (known after apply)
+ bucket_regional_domain_name = (known after apply)
+ force_destroy = false
+ hosted_zone_id = (known after apply)
+ id = (known after apply)
+ object_lock_enabled = (known after apply)
+ policy = (known after apply)
+ region = (known after apply)
+ request_payer = (known after apply)
+ tags = {
+ "Name" = "temp-bucket-a"
+ "TempA" = "true"
}
+ tags_all = {
+ "Name" = "temp-bucket-a"
+ "TempA" = "true"
}
~~~中略~~~
# aws_s3_bucket.this["temp-bucket-b"] will be created
+ resource "aws_s3_bucket" "this" {
+ acceleration_status = (known after apply)
+ acl = (known after apply)
+ arn = (known after apply)
+ bucket = "temp-bucket-b-{accountid}"
+ bucket_domain_name = (known after apply)
+ bucket_prefix = (known after apply)
+ bucket_regional_domain_name = (known after apply)
+ force_destroy = false
+ hosted_zone_id = (known after apply)
+ id = (known after apply)
+ object_lock_enabled = (known after apply)
+ policy = (known after apply)
+ region = (known after apply)
+ request_payer = (known after apply)
+ tags = {
+ "Name" = "temp-bucket-b"
+ "TempB" = "true"
}
+ tags_all = {
+ "Name" = "temp-bucket-b"
+ "TempB" = "true"
}
~~~中略~~~
Plan: 2 to add, 0 to change, 0 to destroy.
Do you want to perform these actions?
Terraform will perform the actions described above.
Only 'yes' will be accepted to approve.
Enter a value: yes
aws_s3_bucket.this["temp-bucket-b"]: Creating...
aws_s3_bucket.this["temp-bucket-a"]: Creating...
aws_s3_bucket.this["temp-bucket-b"]: Creation complete after 1s [id=temp-bucket-b-{accountid}]
aws_s3_bucket.this["temp-bucket-a"]: Creation complete after 1s [id=temp-bucket-a-{accountid}]
Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
問題なくコマンドが完了しました。
AWSコンソールの方も見てみましょう。
Aのバケット
Bのバケット
バケットが2つ作成され、タグもTerraformで設定した通りにそれぞれのバケットごとにキー値が設定できていることが確認できました。
5. おわりに
今回はcan関数を利用してコーディングする方法について記事にしてみました。
コードの内容によって出力結果を分岐させる方法はcan関数以外にもたくさんあると思うので、もしよりよい方法などあれば教えていただければと思います。
1つの方法としてこちらの記事が参考になれば幸いです。