Terraform output doesn't fill some new fields after first run

I have resource of provider terraform-provider-yandex/yandex/resource_yandex_mdb_mongodb_cluster.go at master · yandex-cloud/terraform-provider-yandex · GitHub

I am creating mongodb using this configuration

terraform {
  required_providers {
    yandex = {
      source = "yandex-cloud/yandex"
    }
  }
}
resource "yandex_vpc_subnet" "foo" {
  zone           = "ru-central1-b"
  network_id     = "enpcb11h7qovqqv74ua6"
  v4_cidr_blocks = ["10.7.0.0/24"]
}
resource "yandex_mdb_mongodb_cluster" "mongodb_cluster" {
  name               = "terraform_test"
  description        = "my super cluster"
  environment        = "PRODUCTION"
  network_id         = "enpcb11h7qovqqv74ua6"
  security_group_ids = []
  cluster_config {
    version = "5.0"
  }
  database {
    name = "my_test_db"
  }
  user {
    name     = "user"
    password = "12345678"
  }
  resources_mongod {
    resource_preset_id = "s3-c2-m8"
    disk_type_id       = "network-ssd"
    disk_size          = 11
  }

  host {
    zone_id    = "ru-central1-b"
    subnet_id  = yandex_vpc_subnet.foo.id
    type       = "MONGOD"
  }
}

output "cluster" {
  value     = yandex_mdb_mongodb_cluster.mongodb_cluster
  sensitive = true
}

And after creation I need one host more, so I change my config to

....
resource "yandex_mdb_mongodb_cluster" "mongodb_cluster" {
  ....
+ host {
+   zone_id    = "ru-central1-b"
+   subnet_id  = yandex_vpc_subnet.foo.id
+  type       = "MONGOD"
+  }
}
...

And after terraform apply, which works as I expected, I try to use output like this

$ terraform output -json | jq .cluster.value.host
[
  {
    "assign_public_ip": false,
    "health": "ALIVE",
    "name": "rc1b-o6csqicb9gls6b36.mdb.yandexcloud.net",
    "role": "PRIMARY",
    "shard_name": "rs01",
    "subnet_id": "e2lgv2uejni50klfbgir",
    "type": "MONGOD",
    "zone_id": "ru-central1-b"
  },
  {
    "assign_public_ip": null,
    "health": null,
    "name": null,
    "role": null,
    "shard_name": null,
    "subnet_id": "e2lgv2uejni50klfbgir",
    "type": "MONGOD",
    "zone_id": "ru-central1-b"
  }
]

but I expected output without null’s.

If i run apply again then I got what I expected.

$ terraform output -json | jq .cluster.value.host
[
  {
    "assign_public_ip": false,
    "health": "ALIVE",
    "name": "rc1b-nso3tp5k9308d933.mdb.yandexcloud.net",
    "role": "PRIMARY",
    "shard_name": "rs01",
    "subnet_id": "e2lgv2uejni50klfbgir",
    "type": "MONGOD",
    "zone_id": "ru-central1-b"
  },
  {
    "assign_public_ip": false,
    "health": "ALIVE",
    "name": "rc1b-9bhqdxj47o80jczi.mdb.yandexcloud.net",
    "role": "SECONDARY",
    "shard_name": "rs01",
    "subnet_id": "e2lgv2uejni50klfbgir",
    "type": "MONGOD",
    "zone_id": "ru-central1-b"
  }
]

So what I do wrong ?

It’s difficult to say, from only experience with Terraform in general. This looks like behaviour that might be very specific to terraform-provider-yandex. You might have better luck asking directly in that provider’s issue tracker.

I am the developer of this provider. I have investigated this behavior, but have not found the reason. So I decided this is either a bug in sdk2, or I don’t understand something

Ah, I see. At the end of your resource’s update function, you are calling its read function. It seems most likely that, immediately after submitting the modification to the remote API, the values read back from the API are missing some information - and then, after some time has passed, the API stabilises and returns the expected data to be filled in.

I have already tested this assumption. I have enabled debugging logs and I have checked API requests and responses .

And when terraform requests host info from API, terraform gets the normal response

 "hosts": [
      {
        "name": "rc1b-pbs1b9x0gqr37weq.mdb.yandexcloud.net",
        "cluster_id": "c9q56rt76dlrpsm8otrm",
        "zone_id": "ru-central1-b",
        "resources": {
          "resource_preset_id": "s3-c2-m8",
          "disk_size": "11811160064",
          "disk_type_id": "network-ssd"
        },
        "role": "SECONDARY",
        "health": "ALIVE",
        "services": [
          {
            "type": "MONGOD",
            "health": "ALIVE"
          }
        ],
        "subnet_id": "e2lgv2uejni50klfbgir",
        "shard_name": "rs01",
        "type": "MONGOD"
      },
      {
        "name": "rc1b-rzrncg1o7irpe75c.mdb.yandexcloud.net",
        "cluster_id": "c9q56rt76dlrpsm8otrm",
        "zone_id": "ru-central1-b",
        "resources": {
          "resource_preset_id": "s3-c2-m8",
          "disk_size": "11811160064",
          "disk_type_id": "network-ssd"
        },
        "role": "PRIMARY",
        "health": "ALIVE",
        "services": [
          {
            "type": "MONGOD",
            "health": "ALIVE"
          }
        ],
        "subnet_id": "e2lgv2uejni50klfbgir",
        "shard_name": "rs01",
        "type": "MONGOD"
      }
    ]

and I checked terraform.tfstate, where the state of the resource is stored, it has data what I expect

$ cat terraform.tfstate | jq .resources[0].instances[0].attributes.host
[
  {
    "assign_public_ip": false,
    "health": "ALIVE",
    "name": "rc1b-rzrncg1o7irpe75c.mdb.yandexcloud.net",
    "role": "PRIMARY",
    "shard_name": "rs01",
    "subnet_id": "e2lgv2uejni50klfbgir",
    "type": "MONGOD",
    "zone_id": "ru-central1-b"
  },
  {
    "assign_public_ip": false,
    "health": "ALIVE",
    "name": "rc1b-pbs1b9x0gqr37weq.mdb.yandexcloud.net",
    "role": "SECONDARY",
    "shard_name": "rs01",
    "subnet_id": "e2lgv2uejni50klfbgir",
    "type": "MONGOD",
    "zone_id": "ru-central1-b"
  }
]

but in output don’t fill these fields.

And I have warn message after terraform apply , maybe it is related to this behavior

2023-07-10T14:29:24.980+0300 [WARN]  Provider "provider[\"registry.terraform.io/yandex-cloud/yandex\"]" produced an unexpected new value for yandex_mdb_mongodb_cluster.mongodb_cluster, but we are tolerating it because it is using the legacy plugin SDK.
    The following problems may be the cause of any confusing errors from downstream operations:
      - .health: was cty.StringVal("ALIVE"), but now cty.StringVal("DEGRADED")
      - .host[1].assign_public_ip: was null, but now cty.False
      - .host[1].health: was null, but now cty.StringVal("ALIVE")
      - .host[1].name: was null, but now cty.StringVal("rc1b-pbs1b9x0gqr37weq.mdb.yandexcloud.net")
      - .host[1].role: was null, but now cty.StringVal("SECONDARY")
      - .host[1].shard_name: was null, but now cty.StringVal("rs01")

Hi @Apelsin234,

What you are seeing is exactly the “confusing errors from downstream operations” that log entry is warning about. The resource planned those attributes as null, so that was recorded in the output, and it takes another plan+apply cycle to fix the error.

How can I fix this behavior(correct output after 2 apply)? These fields are Computed, so we don’t have information about host on plan step.

This is one of the known issues of the legacy SDK, and why Terraform must accept the invalid value and only warn about it in the logs. The new Plugin Framework solves these issues and makes dealing with other edge cases easier or even possible. In order to fix up the plan values in the SDK you will need to use a CustomizeDiff function and manually use SetNewComputed to ensure the computed values are unknown during the plan.

1 Like

Thank you for your answer and advice. But CustomizeDiff + SetNewComputed doesn’t work in my case because of SetNew* doesn’t support nested fields SetNew does not work on nested fields · Issue #459 · hashicorp/terraform-plugin-sdk · GitHub .

Perhaps I will find the strength to migrate to a new Plugin Framework