この記事は、Pulumi dotnet Advent Calendar 2019 の23日目です。
Terraform を使っていると、HCLで定義したリソースがHCLの定義とずれると差分として検出されました。
しかし、Pulumi では pulumi up
をしただけでは検出されないことに気づきます。
こういった IaC に期待するのは Configuration Drift の検出であり、Desired State への収束です。 そのDesired State は Code でしょうか? それとも?
Pulumi と Terraform の考え方の違いが垣間見えます。
目次
- 目次
- TL;DR
- 期待する挙動とは
- Pulumi のデフォルトの挙動
- Pulumi のデフォルトの挙動で困る状況
- pulumi refresh: Pulumi の State と実リソースの同期を行う
- デフォルト挙動は変更されないのか
TL;DR
特に terraform で慣れていて Pulumi を使う場合は、pulumi refresh
や pulumi up --refresh
を使うと違和感が少ないでしょう。
- Terraform は実行時にState と実リソースの差を同期する (デフォルトの動作)
- Pulumi は実行時に State と 実リソースの差を同期しない (デフォルトの動作)
- Pulumi は意図的に同期しないことをデフォルトにしている
- Pulumi でTerraform 同様にState と実リソースの差分を同期するには、
pulumi refresh
やpulumi up --refresh
を使う
期待する挙動とは
pulumi でも terraform でも期待する挙動は同じだと思います。
私が期待する挙動は、コードと実リソースの合致がされているか(Desired State にあるか) の保証です。
端的にいうと、コードの表現 = 実リソース
を期待します。
terraform はこれがデフォルト挙動で提供されており、Pulumiでは明示的に指定する必要があります。
Pulumi のデフォルトの挙動
再現 Issue を立ててあります。これに沿ってみてみます。
Pulumi could not detect Resource change without code change. · Issue #3664 · pulumi/pulumi
Pulumi で S3 Bucketを作成します。
new Pulumi.Aws.S3.Bucket("hogemoge", new Pulumi.Aws.S3.BucketArgs { Tags = new Dictionary<string, object> { { "foo", "bar" }, }, });
適用すると、Bucket が作成されます。
$ pulumi up Type Name Plan Info pulumi:pulumi:Stack aws-sandbox-master 'dotnet build -nologo .' completed successfully pulumi:pulumi:Stack aws-sandbox-master 2 messages Type Name Plan Info + └─ aws:s3:Bucket hogemoge create Resources: + 1 to create Do you want to perform this update? > yes no details Resources: + 1 created
意図通り、Bucket が生成されています。
Bucket のタグを変更してみましょう。仮にキーを foo から foo-1 にしますが、Valueの変更でも同じ挙動です。
Pulumi のコードには変更を加えず、pulumi up
をするとどうなるでしょうか?
結果は、「実リソースで生じた変更を検知しない」です。
$ pulumi up Previewing update (master): Type Name Plan Info pulumi:pulumi:Stack aws-sandbox-master 'dotnet build -nologo .' completed successfully pulumi:pulumi:Stack aws-sandbox-master 2 messages Resources: 1 unchanged
Terraform の挙動
terraform ではデフォルトでコードと実リソースの差分が検出されていると書きましたが、どういう挙動をするかおさらいしておきます。
terraform で S3 Bucket を作成します。
resource "aws_s3_bucket" "foo" { bucket = "foo-1234sdfgb" tags = { foo = "bar" } }
applyします。
$ terraform apply �Terraform v0.12.18 ------------------------------------------------------------------------ An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # aws_s3_bucket.foo will be created + resource "aws_s3_bucket" "foo" { + acceleration_status = (known after apply) + acl = "private" + arn = (known after apply) + bucket = "foo-1234sdfgb" + bucket_domain_name = (known after apply) + bucket_regional_domain_name = (known after apply) + force_destroy = false + hosted_zone_id = (known after apply) + id = (known after apply) + region = (known after apply) + request_payer = (known after apply) + tags = { + "foo" = "bar" } + website_domain = (known after apply) + website_endpoint = (known after apply) + versioning { + enabled = (known after apply) + mfa_delete = (known after apply) } } Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
期待通りリソースが作られます。Pulumi と一緒です。
Bucket のタグを変更してみましょう。仮にキーを foo から foo-1 にしますが、Valueの変更でも同じ挙動です。
コードに変更を加えずに、terraform plan
や terraform apply
をすると、コードと実リソースの変更が検出されます。
$ terraform apply �Terraform v0.12.18 Configuring remote state backend... Initializing Terraform configuration... ----------------------------------------------------- An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: ~ update in-place Terraform will perform the following actions: # aws_s3_bucket.foo will be updated in-place ~ resource "aws_s3_bucket" "foo" { acl = "private" arn = "arn:aws:s3:::foo-1234sdfgb" bucket = "foo-1234sdfgb" bucket_domain_name = "foo-1234sdfgb.s3.amazonaws.com" bucket_regional_domain_name = "foo-1234sdfgb.s3.ap-northeast-1.amazonaws.com" force_destroy = false hosted_zone_id = "Z2M4EHUR26P7ZW" id = "foo-1234sdfgb" region = "ap-northeast-1" request_payer = "BucketOwner" ~ tags = { + "foo" = "bar" - "foo-1" = "bar" -> null } versioning { enabled = false mfa_delete = false } } Apply complete! Resources: 0 added, 1 changed, 0 destroyed.
Pulumi のデフォルトの挙動で困る状況
Pulumi は自分の State と実リソースの差分を検知するのですが、pulumi up
をしても自分のstate を実リソースのstate と同期しないため、コードと実リソースで差分が生じていることを検知できません。
Terraform を使っていると「コードと実リソースの差分を検知する」ことに慣れているため、Pulumi でデフォルトで検知しないのは違和感を強く感じます。
私はそうなのですが、Pulumi で作ったリソースの変更はPulumi が検知しない限り気づけません。自分の興味の範疇外なので、それは Pulumi に気づいて教えてほしいと思います。いちいちPulumiが作ったリソースに変更があったことを、Webコンソールやaws cli で確認することはないでしょう。
pulumi refresh: Pulumi の State と実リソースの同期を行う
Pulumi の Stateと実リソースの同期を行うには、明示的に pulumi refresh
か pulumi up --refresh
(-r) を行う必要があります。
このコマンドで、Pulumi State が実リソースと同期されるので、次に pulumi preview
や pulumi up
をすると、コードと実リソースの差分が検出されます。
これを考慮すると、Pulumi コマンドは次の順で実行することになるでしょう。
$ pulumi refresh $ pulumi up
コマンド2つの実行は面倒です。pulumi up
時にリフレッシュさせるのが多いでしょう。
$ pulumi up --refresh
実際に、これに気づいて pulumi preview
をかけたところ、32 changes が生じて涙目になりました。
が、実際のところ、state の差分は コードで表現していないプロパティ でも生じます。
そのため、pulumi refresh
で state の同期時に変更が検出されても、空がデフォルトである場合、次に pulumi up
したときは検出されないのがほとんどです。
例えば、Route53 のレコードは多くのプロパティがありますが、これらは pulumi refresh
で差分として検出されても、pulumi up
では差分にならないでしょう。
正直、コードと実リソースに Drift が生じるのあり得ないので、pulumi refresh
もれなくかけていくのがいいでしょう。
デフォルト挙動は変更されないのか
Issue 立っているものの、コメントを見ていると中の人としてはダウンサイドが目につくのでやりたくないらしいです。
Consider automatically refreshing on preview/update/destroy · Issue #2247 · pulumi/pulumi
まじかよと思いつつも、How Pulumi Works を見ると、Provider から State への向きがないのでなるほどと思わなくもない。