Terraformで一度作成したAWSリソースを後からコードをモジュール化して再度適用したらdestory&createされるよ

最近はAWSリソース構築にterraformを使っているのですが、一からコードを書くのは面倒なのでterraformingを使ってコード生成しております。(他にterraformerとかもあったりしますね)

github.com

terraformingは便利なツールですが、万能ではなく、機械的に抽出されるためかそのままだとコードは冗長なものになりがちです。 私の普段見ているシステムのように作成するリソース数が多いとファイルの行数がトンデモないことになるんじゃないかなと思います。

terraformingで抽出したコードを、そのまま適用してしまってあとで悲しみに暮れている私と同じ運命を辿らないように、terraformingで抽出したコードはしっかりモジュール化を検討した上で適用することをおすすめします(マジで)

流派的にはterraformの機能であるWorkspacesを使ってコード量を少なくするという案もありますが、今回はモジュール化でやってます

それでは本題ですが、あとからterraformコードをモジュール化しようとするとどうなるのかっていうところを実際にやってみました(地獄)

環境

  • Terraform v0.12.0
  • provider.aws v2.12.0

前提

  • terraform実行環境は構築済み
  • cloudwatch agentがインストールされているec2構築済み

検証作業

今回はcloudwatch alarmを作成するケースで試してみる

ディレクトリ構成はこんな感じ

# tree
.
├── cloudwatch
│    ├── backend.tf
│    ├── main.tf
│    ├── provider.tf
│    └── versions.tf
├── module
│   └── cloudwatch_alarm
│       ├── java_process.tf
│       └── variables.tf

まずは愚直にモジュール使ってないmain.tfファイルを作成(内容はjava process監視)

resource "aws_cloudwatch_metric_alarm" "java-process-down" {
    alarm_name          = "java-process-down"
    comparison_operator = "LessThanThreshold"
    evaluation_periods  = "1"
    metric_name         = "procstat_lookup_pid_count"
    namespace           = "CWAgent"
    period              = "60"
    statistic           = "Average"
    threshold           = "1.0"
    alarm_description   = ""
    ok_actions          = ["arn:aws:sns:ap-northeast-1:XXXXXXXXXXXX:test-cloudwatch-to-slack-alert"]
    alarm_actions       = ["arn:aws:sns:ap-northeast-1:XXXXXXXXXXXX:test-cloudwatch-to-slack-alert"]
    dimensions = {
        exe = "java"
        InstanceId = "i-XXXXXXXXXXXXXXX"
        ImageId = "ami-XXXXXXXXXXXXXXXXX"
        pid_finder = "native"
        InstanceType = "t2.micro"
    }
}

terraform apply

# tf apply --auto-approve
aws_cloudwatch_metric_alarm.java-process-down: Creating...
aws_cloudwatch_metric_alarm.java-process-down: Creation complete after 0s [id=java-process-down]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

これでcloudwatch alarmが作成されましたね。 続いて先ほど作成したmain.tfをモジュール呼び出すようにして同じ内容になるように修正します

module "cloudwatch_alarm_java_process-down" {
    source = "../module/cloudwatch_alarm/"
    instance_id = "i-XXXXXXXXXXXXXXX"
    image_id = "ami-XXXXXXXXXXXXXXXXX"
    instance_type = "t2.micro"
    slack_notice = "arn:aws:sns:ap-northeast-1:XXXXXXXXXXXX:test-cloudwatch-to-slack-alert"
}

モジュールの呼び出し先であるjava_process.tfも作成する

resource "aws_cloudwatch_metric_alarm" "java-process-down" {
    alarm_name          = "java-process-down"
    comparison_operator = "LessThanThreshold"
    evaluation_periods  = "1"
    metric_name         = "procstat_lookup_pid_count"
    namespace           = "CWAgent"
    period              = "60"
    statistic           = "Average"
    threshold           = "1.0"
    alarm_description   = ""
    ok_actions          = ["${var.slack_notice}"]
    alarm_actions       = ["${var.slack_notice}"]
    dimensions = {
        exe = "java"
        InstanceId = "${var.instance_id}"
        ImageId = "${var.image_id}"
        pid_finder = "native"
        InstanceType = "${var.instance_type}"
    }
}

モジュールをインストールするために terraform init してから、再び terraform apply

# tf apply --auto-approve
aws_cloudwatch_metric_alarm.java-process-down: Refreshing state... [id=java-process-down]
aws_cloudwatch_metric_alarm.java-process-down: Destroying... [id=java-process-down]
module.cloudwatch_alarm_java_process-down.aws_cloudwatch_metric_alarm.java-process-down: Creating...
module.cloudwatch_alarm_java_process-down.aws_cloudwatch_metric_alarm.java-process-down: Creation complete after 0s [id=java-process-down]
aws_cloudwatch_metric_alarm.java-process-down: Destruction complete after 0s

Apply complete! Resources: 1 added, 0 changed, 1 destroyed.

コマンドラインでは Apply complete! と表示されてましたが、マネジメントコンソールから確認してみると作成されていない...

f:id:namio6243:20200619235928p:plain

もう一度 terraform apply

# tf apply --auto-approve
module.cloudwatch_alarm_java_process-down.aws_cloudwatch_metric_alarm.java-process-down: Refreshing state... [id=java-process-down]
module.cloudwatch_alarm_java_process-down.aws_cloudwatch_metric_alarm.java-process-down: Creating...
module.cloudwatch_alarm_java_process-down.aws_cloudwatch_metric_alarm.java-process-down: Creation complete after 0s [id=java-process-down]

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.

今度は作成されました。 terraform を使ってると割とよくあるんですけど、コマンドライン上は成功してもAWS上では作成されない事象。再度applyすると作成はされるが、何が原因かはよく分かっておらず...

とりあえず、一度作成したリソースをtfファイルをモジュールを利用するように修正すると、リソース自体が再作成されるので、本番環境だと慎重にやる必要がある

あと、おまけで試してみたけど main.tfファイルを単純に分割するのはリソース再作成にはならない模様(tfファイルが冗長になったら分割したくなるので)

まとめ

  • Terraform は、一度作成したリソースを後からコードをモジュール化(処理の共通化)をして再適用するとリソース再作成(destroy&create)になるので本番環境では注意してやること
  • terraform apply がコマンドライン上は成功してもAWS上には作成されないケースがある(私の環境のせい...?)、その場合は再度 terraform apply すればOKみたい
  • tfファイル分割するのは問題なく可能(リソース再作成はされない)