tech.guitarrapc.cóm

Technical updates

Pulumi のState と実リソースの差分を同期する

この記事は、Pulumi dotnet Advent Calendar 2019 の23日目です。

qiita.com

Terraform を使っていると、HCLで定義したリソースがHCLの定義とずれると差分として検出されました。 しかし、Pulumi では pulumi up をしただけでは検出されないことに気づきます。

こういった IaC に期待するのは Configuration Drift の検出であり、Desired State への収束です。 そのDesired State は Code でしょうか? それとも?

Pulumi と Terraform の考え方の違いが垣間見えます。

目次

TL;DR

特に terraform で慣れていて Pulumi を使う場合は、pulumi refreshpulumi up --refresh を使うと違和感が少ないでしょう。

  • Terraform は実行時にState と実リソースの差を同期する (デフォルトの動作)
  • Pulumi は実行時に State と 実リソースの差を同期しない (デフォルトの動作)
  • Pulumi は意図的に同期しないことをデフォルトにしている
  • Pulumi でTerraform 同様にState と実リソースの差分を同期するには、 pulumi refreshpulumi 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 planterraform 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 refreshpulumi up --refresh (-r) を行う必要があります。

このコマンドで、Pulumi State が実リソースと同期されるので、次に pulumi previewpulumi 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 への向きがないのでなるほどと思わなくもない。

How Pulumi Works | Pulumi