Creating multiple rules in aws_ecr_lifecycle_policy

Hello, My requirement is to give flexibility of creating lifecycle rules while creating ECR Lifecycle Policy in AWS. I am allowing user to enter the list of Objects as below: Ultimate goal is to create multiple rules in same policy. If use “count”, terraform plan created 2 lifecyle policy statements to add and even apply runs successfully with only 1 rule added instead of 2. It doesn’t fail to create 2nd policy. When you re-run the plan, it finds the rule as changes from 1 to 2 and recreated the policy with 2nd rule replacing 1st rule.

I tried creating 2 rules statement using <<EOT but its failing with below error.
Appreciate if someone provide the solution…!

Error: InvalidParameterException: Invalid parameter at ‘LifecyclePolicyText’ failed to satisfy constraint: 'Lifecycle policy validation failure: instance type (string) does not match any allowed primitive type (allowed: [“object”])

lifecycle_rules = [{
rule_priority = 1
tag_status = “tagged”
tag_prefix_list = “test,test1,test2”
count_type = “sinceImagePushed”
count_number = 60
},

{
rule_priority = 2
tag_status = “tagged”
tag_prefix_list = “prod,prod1,prod2”
count_type = “sinceImagePushed”
count_number = 90
}]

===================

resource “aws_ecr_lifecycle_policy” “lifecycle_policy” {

count = length(var.lifecycle_rules)
repository = aws_ecr_repository.repository.name

policy = jsonencode(
{
rules = [
{
action = {
type = “expire”
}
description = format(“%s-Lifecycle-Rule-%s”,aws_ecr_repository_policy.repository_policy.repository,var.lifecycle_rules[count.index].rule_priority)

                  rulePriority = var.lifecycle_rules[count.index].rule_priority
                  selection    = {
                      countNumber = var.lifecycle_rules[count.index].count_number
                      countType   = var.lifecycle_rules[count.index].count_type 
                      countUnit   = "days"
                      tagStatus   = var.lifecycle_rules[count.index].tag_status
                      tagPrefixList = split(",",var.lifecycle_rules[count.index].tag_prefix_list) 

                  }

          }]

})

}

Hi @SanjuMLGeek ,
it would be helpful if you could reformat your code (enclose it in tripple backticks).

As per my understanding you cannot use count for creating multiple policies of the same ECR. So all rules have to be created within the policy section.

I assume your initial error points to a type mismatch for tag_prefix_list. It should be a list instead of a string.

terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
      #      version = "= 3.24.1"
    }
  }
}

resource "aws_ecr_repository" "foo" {
  name = "bar"
}

resource "aws_ecr_lifecycle_policy" "foopolicy" {
  repository = aws_ecr_repository.foo.name

  policy = jsonencode({ "rules" : var.lifecycle_rules })
  
}

variable "lifecycle_rules" {
  default = [{
    rule_priority   = 1
    tag_status      = "tagged"
    tag_prefix_list = ["test", "test1", "test2"]
    count_type      = "sinceImagePushed"
    count_number    = 60
    },

    {
      rule_priority   = 2
      tag_status      = "tagged"
      tag_prefix_list = ["prod", "prod1", "prod2"]
      count_type      = "sinceImagePushed"
      count_number    = 90
  }]
}

Hello, That approach required full list of policy attributes into variable object and was failing with syntax issues at “apply”. I tried building Locals instead which seem to be working fine except tagStatus = any or untagged, as tagPrefixList is not required when tagStatus is not “tagged”. I tried passing null or empty list as to the tag_prefix_list assignment in bold but its not working with error below. Is there a way to exclude that entire variable assignment for tag_prefix_list when tag_status is set to “any” or “untagged”?

Error: InvalidParameterException: Invalid parameter at ‘LifecyclePolicyText’ failed to satisfy constraint: 'Lifecycle policy validation failure: array is too short: must have at least 1 elements but instance has 0 elements

Split to form the list works when the tag_status = “tagged”, so no problem there.


#variable definition below

variable "lifecycle_rules" {
  
  type  = list(object({
    rule_priority = number
    tag_status = string
    tag_prefix_list = string
    count_type = string
    count_number = number  
  }))

  default = []
  description = "The List of Lifecycle Rule Configurations"

#variable scenarios below

lifecycle_rules = [{
  rule_priority = 1
  tag_status = "tagged"
  tag_prefix_list = "test,test1,test2"
  count_type = "sinceImagePushed"
  count_number = 60
},
{
  rule_priority = 2
  tag_status = "any"
  count_type = "sinceImagePushed"
  tag_prefix_list = ""
  count_number = 90
},
{
  rule_priority = 3
  tag_status = "untagged"
  count_type = "sinceImagePushed"
  tag_prefix_list = ""
  count_number = 90
}]
}



locals {
lifecycle_rules = [
      for rules in var.lifecycle_rules : {
        description = format("%s-Lifecycle-Rule-%s",aws_ecr_repository_policy.repository_policy.repository,rules.rule_priority)
        rulePriority = rules.rule_priority
        selection = {
          tagStatus = rules.tag_status
          tagPrefixList = rules.tag_status == "tagged" ? split(",",rules.tag_prefix_list) : []
          countType = rules.count_type
          countNumber = rules.count_number
          countUnit = "days"
        }
        action = {
          type = "expire"
        }
      }
  ]
}

@SanjuMLGeek Thank you for you method.
But when i applied you code above i got following error:

╷
│ Error: InvalidParameterException: Invalid parameter at 'LifecyclePolicyText' failed to satisfy constraint: 'Lifecycle policy validation failure: instance type (a
rray) does not match any allowed primitive type (allowed: ["object"])
│ '
│
│   with aws_ecr_lifecycle_policy.name,
│   on lifecycle_policy.tf line 21, in resource "aws_ecr_lifecycle_policy" "name":
│   21: resource "aws_ecr_lifecycle_policy" "name" {
│

Any ideas how to allowed primitive type “List” ?

I think that should exist a resource aws_ecr_lifecycle_policy_rule.

This issue was discussed here

With the proposal in the Github issue, you can solve it for a single terraform state.
But for example, I’m using also terragrunt, and trying to add a lifecycle rule since other state files, that imports the repository created in another one, and depending on the order it is correctly applied or not.