Terraform使ってると一部リソース差分を無視したいことがあります。
たとえばAWSのami-idを共通の変数として定義していて、
そこから共通のLaunchConfigurationを使っているAutoScalingGroupが2系統あるような場合、
この状態でおもむろに共通のami-idを変更するとAutoScalingGroupが両方一括で変更されてしまいます。
テスト環境とかであれば両方まとめて変えちゃってもよいんですけど、
本番環境は安全に片系ずつterraform plan/applyしたいという精神衛生上の問題があるのです。
かと言ってLaunchConfigurationを別に管理するのはコピペで冗長になるので、
中間状態を別に管理したくないだけという需要。具体的にはこんなイメージ。
variable "base_amis" {
default = {
production = "ami-XXXXX"
}
....
}
resource "aws_launch_configuration" "as_conf" {
image_id = "${lookup(var.base_amis, "${var.env}")}"
...
lifecycle {
create_before_destroy = true
}
}
resource "aws_autoscaling_group" "app_1" {
name = "app-${var.env}-asg-1"
launch_configuration = "${aws_launch_configuration.as_conf.name}"
...
tag {
key = "launch_configuration"
value = "${aws_launch_configuration.as_conf.name}"
propagate_at_launch = true
}
}
resource "aws_autoscaling_group" "app_2" {
name = "app-${var.env}-asg-2"
launch_configuration = "${aws_launch_configuration.as_conf.name}"
...
tag {
key = "launch_configuration"
value = "${aws_launch_configuration.as_conf.name}"
propagate_at_launch = true
}
}
※補足: create_before_destroyのところは既知の問題で削除前に新しいリソースを生成する設定。以下のIssueを参考。
https://github.com/hashicorp/terraform/issues/1109
解決策としてバッドノウハウっぽいんですけど、
AutoScalingGroupの app_1
リソースだけ反映して、 app_2
リソースを無視したい場合はこんなかんじで
lifecycle ignore_changes
という指定を入れると、この状態でterraform plan/applyしても差分として検知されなくなります。(terraformのバージョンはv0.6.6で確認)
resource "aws_launch_configuration" "as_conf" {
image_id = "${lookup(var.base_amis, "${var.env}")}"
...
lifecycle {
create_before_destroy = true
}
}
resource "aws_autoscaling_group" "app_1" {
name = "app-${var.env}-asg-1"
launch_configuration = "${aws_launch_configuration.as_conf.name}"
...
tag {
key = "launch_configuration"
value = "${aws_launch_configuration.as_conf.name}"
propagate_at_launch = true
}
}
resource "aws_autoscaling_group" "app_2" {
name = "app-${var.env}-asg-2"
launch_configuration = "${aws_launch_configuration.as_conf.name}"
...
tag {
key = "launch_configuration"
value = "${aws_launch_configuration.as_conf.name}"
propagate_at_launch = true
}
lifecycle {
ignore_changes = ["launch_configuration", "tag"]
}
}
なんでバッドノウハウかというと、この状態でplanは成功しますが、applyすると必要な変更はされますが、古いLaunchConfigurationがapp_2に依存したままで削除エラーになります。
Error applying plan:
1 error(s) occurred:
* aws_launch_configuration.as_conf: ResourceInUse: Cannot delete launch configuration terraform-tumlsojicvb43ahrhvounyp77e because it is attached to AutoScalingGroup app-production-asg-2
status code: 400, request id: dd130591-d915-11e5-8c9b-8f4029a78f83
Terraform does not automatically rollback in the face of errors.
Instead, your Terraform state file has been partially updated with
any resources that successfully completed. Please address the error
above and apply again to incrementally change your infrastructure.
あんまりキレイな方法じゃなさそうに思いますが、全部一発でapplyせずに一部ずつapplyしたいという需要は実運用上あるように思うので、同じことに困っていた方に参考になればと思い共有しておきます。
(追記)
app_1をapply後に一時的にignoreしたapp_2を戻して再度applyしたらterraform applyの出力上は変更に成功しているが、実際のapp_2のAWSリソース上ではLaunchConfigurationの変更が反映されていないという事象が発生しました。直後にplanを実行しても差分が出ませんが、原因調査しているうちに再度planするとapp_2のLaunchConfigurationに差分が検出され、再度applyしたら正常に反映されました。
ignoreを使った場合の挙動が若干あやしいので、この方法を使う場合はapplyしたあとに念のためAWSコンソールからリソースへの反映がされているか確認したほうがよさそうです。