terraformterraform-provider-awslifecyclepreconditions

Terraform - Precondition lifecycle


I want to do the following

  1. Update the KMS key resource to include lifecycle precondition

  2. If the referenced policy is null, use the constructed local policy

  3. Add a validator for the policy that if not set and the key type is direct, then direct_key_principal should not be empty

resource "aws_kms_key" "direct_key" {
  description              = var.description
  enable_key_rotation      = true
  deletion_window_in_days  = var.deletion_window
  policy                   = var.policy != null ? var.policy : data.aws_iam_policy_document.kms_key_policy_direct[0].json
  key_usage                = var.key_usage
  customer_master_key_spec = var.customer_master_key_spec

lifecycle {
  precondition {
    condition     = var.policy == null && var.key_type == "direct" && length(values(var.direct_key_principal)[0]) > 0
    error_message = "When the policy is not set and the key type is direct, all direct_key_principal elements should be non-empty."
  }
}
variable "direct_key_principal" {
  description = "Principal Information - Type & Identifier required for a 'direct' key"
  type        = map(list(string))
  default = {
    AWS = []
  }
  validation {
    condition     = alltrue([for principal_type in keys(var.direct_key_principal) : contains(["AWS", "Service"], principal_type)])
    error_message = "Valid values for Principal type are AWS and Service."
  }
}

I get this error message below

│   12:     condition     = var.policy == null && var.key_type == "direct" && length(values(var.direct_key_principal)[0]) > 0
│     ├────────────────
│     │ var.direct_key_principal is map of list of string with 1 element
│     │ var.key_type is "direct"
│     │ var.policy is "{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Sid\": \"\",\n      \"Effect\": \"Allow\",\n      \"Action\": \"kms:*\",\n      \"Resource\": \"arn:aws:*:*:765862725291:*\",\n      \"Principal\": {\n        \"AWS\": \"arn:aws:iam::765862725291499556464691:root\"\n      }\n    },\n    {\n      \"Sid\": \"sid1\",\n      \"Effect\": \"Allow\",\n      \"Action\": [\n        \"kms:TagResource\",\n        \"kms:List*\",\n        \"kms:Get*\",\n        \"kms:Describe*\",\n        \"kms:Decrypt\"\n      ],\n      \"Resource\": [\n        \"arn:aws:*:*:499556464691:*\",\n        \"arn:aws:*:*:999999955:*\"\n      ],\n      \"Principal\": {\n        \"AWS\": \"999999955\"\n      }\n    }\n  ]\n}"
│ 
│ When the policy is not set and the key type is direct, all
│ direct_key_principal elements should be non-empty.

Solution

  • This error message is correct for the condition you wrote because you've required that var.policy be null but we can see from the error message that it is not null.

    From the error message you wrote I believe what you intended to write as the rule is that this rule is only relevant if var.policy is null and var.key_type is "direct".

    If so, you'll need to use a conditional expression to test that first and then choose between enforcing your rule or just returning true to make the rule always pass, like this:

      precondition {
        condition     = (
          var.policy == null && var.key_type == "direct" ? 
          length(values(var.direct_key_principal)[0]) > 0 :
          true
        )
        error_message = "When the policy is not set and the key type is direct, all direct_key_principal elements should be non-empty."
      }
    

    If var.policy is null and var.key_type is "direct" then Terraform will use the result of the first expression that tests whether the length is greater than zero. If either of those pre-conditions is not true then Terraform will use the result of the second expression, which is literally true and will therefore always pass the check.


    Note that your error message says "all direct_key_principal elements should be non-empty", but yet your conditional expression only tests one element of the map -- whichever one sorts first in the result from values. Making this test apply to all of the elements is possible but beyond the scope of what you asked in this question, so if you'd like to implement that change too and you're not sure how to proceed I'd suggest starting another separate question after you've verified that my answer above works for avoiding the error you asked about.