Occasional blog posts from a random systems engineer

Ignoring unwanted Terraform provider attribute changes

· Read in about 3 min · (562 Words)

The Problem!

I’ve ocassionally found Terraform providers that take an attribute and manipulate it in a way that.. isn’t favourable.

This means that, following runs see a change in the attribute (compared) to the attribute passed and want to modify it.

For example!…

The Docker Terraform provider (by kreuzwerker :D https://registry.terraform.io/providers/kreuzwerker/docker/latest/docs) manipulates the “image” attribute to the SHA digest of the image…

This means, that if I create:

resource "docker_container" "my_important_container" {
    ...
    image = "base-image-for-my-important-container:v2.20.2"
    ...
}

A terraform refresh later, the image attribute will be been read as a SHA digest. This means that next time Terraform runs, it will want to re-create the container, changing the image from the SHA digest to the original named image.

For some resources, this is probably fine, it maybe an update-in-place and nothing changes.. but in this case, it forces a re-creation and, for a container, this means an outage. (Yes, yes, this is an awful way of deploying containers.. but it’s for Homelab use and sshh!… See what I was dealing with here (https://github.com/MatthewJohn/vault-nomad-consul-terraform) and then complain!).

Ignorance is Bliss

Of course, the way most would achive this is to simply ignore changes to this attribute:

resource "docker_container" "my_important_container" {
  ...
  image = "base-image-for-my-important-container:v2.20.2"
  ...

  lifecycle {
    ignore_changes = [image]
  }
}

Of course this would work.. at least to the point of stopping the re-creation of the resource and shutting Terraform up…

But suppose I make a change:

diff
-   image = "base-image-for-my-important-container:v2.20.2"
+   image = "base-image-for-my-important-container:v2.20.3"

Well, of course, all hell has broken loose.. I’ve made the change to deploy to new version and I’ve run Terraform and it’s showing all good!.. But it hasn’t, has it.. we told it to ignore it and it has done just that. Only, we forgot about that 3 years later and our pipeline showed green showing Terraform had plan/applied, so all good.

Listeening to the ignored

A little way around this that I like to use is to simply create a null_resource, triggered by attribute value that we really care about and configure a replace based on this trigger.

E.g.:

locals {
  image = "base-image-for-my-important-container:v2.20.2"
}

# Handle changes to image, which are ignored by the container resource
resource "null_resource" "container_image" {
  triggers = {
    image = local.image
  }
}

resource "docker_container" "my_important_container" {
  ...
  
  image = local.image
  ...

  lifecycle {
    ignore_changes = [image]

    # When container image name changes
    replace_triggered_by = [
      null_resource.container_image,
    ]
  }
}

And ta-da.. as long as the image name doesn’t change, the trigger doesn’t get rebuilt. The docker_container sits happily with some garbage/impossible-to-really-verify/i-would-never-pass-this-in SHA digest, which Terraform is ignoring. As soon as the image changes, whilst the docker_container resource ignores the attribute change, the null_resource certainly doesn’t and that re-creation then triggers a container re-creation.

Improvements?

Honestly, perhaps the image attribute of the docker_container could been set to null_resource.container_image.triggers.image, but I’ve always taken the image in as a variable, which has reduced the duplication for me.

Having said this, I like to use attributes where ownership lies.. I don’t think the null_resource is a “provider” of the image, more an additional consumer. So perhaps a local is fine.

And, honestly, I should have created a PR with the provider to handle resolving an image name to a SHA digest and then comparing it before forcing a re-creation.. but hey, sometimes we don’t have time to fix every little thing!

Comments