Use and declare block as variable in Terraform 0.12

842 views
Skip to first unread message

Antoine Rouaze

unread,
Sep 12, 2019, 5:32:41 PM9/12/19
to Terraform
Hi everyone,

I was migrating some Terraform 0.11 infrastructure to the 0.12 when I get stuck on a specific issue. I have a few buckets which are using the same lifecycle rule. To be a bit DRY, I set the lifecycle rule from a variable. Here is an example in Terraform 0.11:

variable "lifecycle_rule_60_days" {
  type = "map"

  default = {
    id      = "whole-bucket"
    prefix  = ""
    enabled = true

    expiration = [{
      days = 60
    }]
  }
}

resource "aws_s3_bucket" "bucket_a" {
  bucket = "bucket_a"
  lifecycle_rule = ["${var.lifecycle_rule_60_days}"]
}

resource "aws_s3_bucket" "bucket_b" {
  bucket   = "bucket_b"
  lifecycle_rule = ["${var.lifecycle_rule_60_days}"]
}

So I tried to convert that in Terraform 0.12, and the only "clean" way I found to it was that:

variable "lifecycle_rule_60_days" {
  type = object({
    id      = string
    prefix  = string
    enabled = bool
    expiration = list(object({
      days = number
    }))
  })

  default = {
    id      = "whole-bucket"
    prefix  = ""
    enabled = true

    expiration = [{
      days = 60
    }]
  }
}

resource "aws_s3_bucket" "bucket_a" {
  bucket = "bucket_a"

  dynamic "lifecycle_rule" {
    for_each = [var.lifecycle_rule_60_days]
    content {
      id      = lifecycle_rule.value.id
      prefix  = lifecycle_rule.value.prefix
      enabled = lifecycle_rule.value.enabled
      dynamic "expiration" {
        for_each = lifecycle_rule.value.expiration
        content {
          days = expiration.value.days
        }
      }
    }
  }
}

resource "aws_s3_bucket" "bucket_b" {
  bucket = "bucket_b"
  dynamic "lifecycle_rule" {
    for_each = [var.lifecycle_rule_60_days]
    content {
      id      = lifecycle_rule.value.id
      prefix  = lifecycle_rule.value.prefix
      enabled = lifecycle_rule.value.enabled
      dynamic "expiration" {
        for_each = lifecycle_rule.value.expiration
        content {
          days = expiration.value.days
        }
      }
    }
  }
}

That's not really DRY at all. I know, "lifecycle_rule" is a resource block, not an argument in Terraform point of view, but do you if there is a way to declare resource as a variable? Like doing something like that:

variable "lifecycle_rule_60_days" {
  type = object({
    id      = string
    prefix  = string
    enabled = bool
    expiration = list(object({
      days = number
    }))
  })

  default = {
    id      = "whole-bucket"
    prefix  = ""
    enabled = true

    expiration = [{
      days = 60
    }]
  }
}

resource "aws_s3_bucket" "bucket_a" {
  bucket = "bucket_a"

  lifecycle_rule = var.lifecycle_rule_60_days
}

resource "aws_s3_bucket" "bucket_b" {
  bucket = "bucket_b"
  lifecycle_rule = var.lifecycle_rule_60_days
}

Via a module maybe?

Thank you!
Antoine Rouaze

Giovanni Tirloni

unread,
Sep 12, 2019, 10:53:33 PM9/12/19
to terrafo...@googlegroups.com
What if you flip the logic and instead loop over a list of bucket names using count/length? Then specify the lifecycle block just once.

--
This mailing list is governed under the HashiCorp Community Guidelines - https://www.hashicorp.com/community-guidelines.html. Behavior in violation of those guidelines may result in your removal from this mailing list.
 
GitHub Issues: https://github.com/hashicorp/terraform/issues
IRC: #terraform-tool on Freenode
---
You received this message because you are subscribed to the Google Groups "Terraform" group.
To unsubscribe from this group and stop receiving emails from it, send an email to terraform-too...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/terraform-tool/c2984491-4bd5-447c-9cef-a0805f18e4d5%40googlegroups.com.

Antoine Rouaze

unread,
Sep 16, 2019, 5:41:49 PM9/16/19
to Terraform
It's a good idea, but I try to use less as possible the count. For example, if I have 10 buckets in my list and if I want to delete one in the middle of the list, Terraform is going to destroy/create every bucket after the one I want to be deleted (disclaimer, I didn't test).

I came up with a solution by using a module. Might not the best solution though, but here are the sources if you're interested: https://gist.github.com/Erouan50/d01c1fd7b51808b81dbbb6dc5e3b877a

Best,
Antoine Rouaze


On Thursday, September 12, 2019 at 10:53:33 PM UTC-4, Giovanni Tirloni wrote:
What if you flip the logic and instead loop over a list of bucket names using count/length? Then specify the lifecycle block just once.

To unsubscribe from this group and stop receiving emails from it, send an email to terrafo...@googlegroups.com.

Giovanni Tirloni

unread,
Sep 16, 2019, 8:53:09 PM9/16/19
to terrafo...@googlegroups.com
Very good point! I feel like there should be a better solution for the
"modify middle of the list -> destroy everything" problem... but I
don't know about one. It's a major hassle.

Thanks for sharing the code.
> To unsubscribe from this group and stop receiving emails from it, send an email to terraform-too...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/terraform-tool/afd71d69-259e-40af-bdf8-48687cd82c24%40googlegroups.com.

Stuart Clark

unread,
Sep 17, 2019, 2:17:53 AM9/17/19
to terrafo...@googlegroups.com, Giovanni Tirloni
On 17/09/2019 01:52, Giovanni Tirloni wrote:
> Very good point! I feel like there should be a better solution for the
> "modify middle of the list -> destroy everything" problem... but I
> don't know about one. It's a major hassle.

Rather than using count, use for_each.

Terraform then names things by the key of each map entry you pass in
rather than a number. If things change it will then only remove the
exact entry, not everything & recreate.
--
Stuart Clark

Antoine Rouaze

unread,
Sep 17, 2019, 10:13:33 AM9/17/19
to Terraform
Hum didn't know it was possible to do a for_each on the resource. With a map, it does the job, but it's not very easy to read: 

locals {
  lifecycle_rule = [{
    id      = "whole-bucket"
    prefix  = ""
    enabled = true

    expiration = [
      {
        days = 60
    }]
  }]
  buckets = {
    bucket_a = {
      lifecycle_rule = local.lifecycle_rule
    },
    bucket_b = {
      lifecycle_rule = local.lifecycle_rule
    }
  }
}

resource "aws_s3_bucket" "buckets" {
  for_each = toset(keys(local.buckets))
  bucket   = each.key
  dynamic "lifecycle_rule" {
    for_each = local.buckets[each.value].lifecycle_rule
    content {
      id      = lifecycle_rule.value.id
      prefix  = lifecycle_rule.value.prefix
      enabled = lifecycle_rule.value.enabled
      dynamic "expiration" {
        for_each = lifecycle_rule.value.expiration
        content {
          days = expiration.value.days
        }
      }
    }
  }
}

I'll stick with the modules for now, but big thanks for the tips!

Best,
Antoine Rouaze
Reply all
Reply to author
Forward
0 new messages