11

I am creating some resources using the for_each method on Terraform version 0.14.15. The resource has an attirbute, input_parameters that takes a string in JSON format as its value. I am defining this value in a map variable utilizing separate objects. The value I am specifying as a string in JSON format, and I am getting an error upon execution that I need to declare a string. Any insight on fixing this error would be helpful. Below is how I have my resource and variable declared.

Resource

resource "aws_config_config_rule" "managed_rules" {
  for_each         = var.managed_rules
  name             = each.value.name
  description      = each.value.description
  input_parameters = each.value.input_parameters

  source {
    owner             = each.value.owner
    source_identifier = each.value.source_identifier
  }

  depends_on = [aws_config_configuration_recorder.config_recorder]
}

Variable

variable "managed_rules" {
  type = map(object({
    name              = string
    description       = string
    owner             = string
    source_identifier = string
# Is there a variable for strings in JSON format?
    input_parameters  = string
  }))
  default = {
    "1" = {
      name              = "alb-http-to-https-redirection-check"
      description       = "Checks whether HTTP to HTTPS redirection is configured on all HTTP listeners of Application Load Balancers. The rule is NON_COMPLIANT if one or more HTTP listeners of Application Load Balancer do not have HTTP to HTTPS redirection configured."
      owner             = "AWS"
      source_identifier = "ALB_HTTP_TO_HTTPS_REDIRECTION_CHECK"
      input_parameters = {
        "MaximumExecutionFrequency" : "TwentyFour_Hours",
      }
  }

Error

This default value is not compatible with the variable's type constraint:
element "2": attribute "input_parameters": string required.

After updating code with jsonencode function and changing input_parameters to any, this is the error:

    This default value is not compatible with the variable's type constraint:
collection elements cannot be unified.
2
  • Try jsonencode: jsonencode(each.value.input_parameters). Commented Jul 15, 2021 at 0:07
  • For the updated issue: input_parameters = jsonencode(each.value.input_parameters) in aws_config_config_rule. Commented Jul 15, 2021 at 8:24

4 Answers 4

8

You have a couple things going on here:

  1. The resource requires input_parameters to be a JSON-encoded string
  2. You have the variable type as a string
  3. You're passing an object type into the variable that only accepts a string type

So (2) and (3) are conflicting. At some point, you have to convert your object into a JSON string. You can either do that before passing it in as an input variable, or change your input variable to accept objects and convert the object to JSON when providing it to the resource.

I'd choose the second option because it's more intuitive to pass the object into the module instead of a string. So, try this:

resource "aws_config_config_rule" "managed_rules" {
  for_each         = var.managed_rules
  name             = each.value.name
  description      = each.value.description
  input_parameters = jsonencode(each.value.input_parameters)

  source {
    owner             = each.value.owner
    source_identifier = each.value.source_identifier
  }

  depends_on = [aws_config_configuration_recorder.config_recorder]
}


variable "managed_rules" {
  type = map(object({
    name              = string
    description       = string
    owner             = string
    source_identifier = string
# Is there a variable for strings in JSON format?
    input_parameters  = any
  }))
  default = {
    "1" = {
      name              = "alb-http-to-https-redirection-check"
      description       = "Checks whether HTTP to HTTPS redirection is configured on all HTTP listeners of Application Load Balancers. The rule is NON_COMPLIANT if one or more HTTP listeners of Application Load Balancer do not have HTTP to HTTPS redirection configured."
      owner             = "AWS"
      source_identifier = "ALB_HTTP_TO_HTTPS_REDIRECTION_CHECK"
      input_parameters = {
        "MaximumExecutionFrequency" : "TwentyFour_Hours",
      }
  }

Note that I've used jsonencode in the resource's input_parameters and I've changed the variable type for that field to any (so it will accept an object of any structure).

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you for the insight. It definitely helps, but now I am getting a different error. I will edit and update.
Now that is one I'm not familiar with. In that case, switch the variable type back to string, remove the jsonencode from your resource, wrap your default object in jsonencode, and wrap your input variable in jsonencode.
The issue with that approach is you cannot call functions in variables.
Ah, good point @DaveMichaels. You could specify the default as a raw JSON string though, e.g. default = "{\"1\" = {\"name\" = ...}}"
4

To add to Jordan's answer. I Had a similar concern when trying to add a json policy to a module.

I used the any object type in place of the string object type.

Here's how I fixed it:

Module main.tf

resource "aws_ecr_repository_policy" "main" {
  repository = var.repository_name
  policy     = var.repository_policy
}

Module variables.tf

variable "repository_name" {
  type        = string
  description = "Name of the repository."
}

variable "repository_policy" {
  type        = any
  description = "The policy document. This is a JSON formatted string."
}

Resource Creation main.tf

# ECR Repository for container images
module "ecr_repository_1" {
  source = "../../../../modules/aws/ecr-repository"

  ecr_repository_name  = var.ecr_repository_name.1
  image_tag_mutability = var.image_tag_mutability
  image_scan_on_push   = var.image_scan_on_push
  tag_environment      = local.tag_environment
  tag_terraform        = local.tag_terraform.true
}

# ECR Repository policies
module "ecr_repository_policy_1" {
  source = "../../../../modules/aws/ecr-repository-policy"

  repository_name   = var.ecr_repository_name.1
  repository_policy = var.repository_policy.1
}

Resource creation variables.tf

variable "ecr_repository_name" {
  type        = map(string)
  description = "Name of the repository."
  default = {
    "1" = "my-backend-api"
  }
}

variable "image_tag_mutability" {
  type        = string
  description = "The tag mutability setting for the repository. Must be one of: MUTABLE or IMMUTABLE. Defaults to MUTABLE."
  default     = "MUTABLE"
}

variable "image_scan_on_push" {
  type        = bool
  description = "Indicates whether images are scanned after being pushed to the repository (true) or not scanned (false)."
  default     = true
}

variable "repository_policy" {
  type        = any
  description = "The policy document. This is a JSON formatted string."
  default = {
    "1" = <<EOF
          {
              "Version": "2008-10-17",
              "Statement": [
                  {
                      "Sid": "new policy",
                      "Effect": "Allow",
                      "Principal": "*",
                      "Action": [
                          "ecr:GetDownloadUrlForLayer",
                          "ecr:BatchGetImage",
                          "ecr:BatchCheckLayerAvailability",
                          "ecr:PutImage",
                          "ecr:InitiateLayerUpload",
                          "ecr:UploadLayerPart",
                          "ecr:CompleteLayerUpload",
                          "ecr:DescribeRepositories",
                          "ecr:GetRepositoryPolicy",
                          "ecr:ListImages",
                          "ecr:DeleteRepository",
                          "ecr:BatchDeleteImage",
                          "ecr:SetRepositoryPolicy",
                          "ecr:DeleteRepositoryPolicy"
                      ]
                  }
              ]
          }
          EOF
  }
}

1 Comment

The <<EOF example was the thing I was looking for
2

You can create your json string as follows:

variable "managed_rules" {

  type = map(object({
    name              = string
    description       = string
    owner             = string
    source_identifier = string
# Is there a variable for strings in JSON format?
    input_parameters  = string
  }))
  
  default = {
    "1" = {
      name              = "alb-http-to-https-redirection-check"
      description       = "Checks whether HTTP to HTTPS redirection is configured on all HTTP listeners of Application Load Balancers. The rule is NON_COMPLIANT if one or more HTTP listeners of Application Load Balancer do not have HTTP to HTTPS redirection configured."
      owner             = "AWS"
      source_identifier = "ALB_HTTP_TO_HTTPS_REDIRECTION_CHECK"
      input_parameters = <<EOL
      {
        "MaximumExecutionFrequency" : "TwentyFour_Hours",
      }
EOL
  }
 } 
}  

But then you have to use jsondecode if you want to parse this string. You can't use functions in variables, so it must be done later.

2 Comments

Thank you for the insight. I am able to compile with Terraform but now I'm facing an error stating InvalidParameterValueException: Invalid JSON passed in the inputParameters field.
@DaveMichaels Sorry, but I'm confused what is the current state of your TF script now. Is it still exactly as in the question?
-1
input_parameters = {
    "MaximumExecutionFrequency" : "TwentyFour_Hours",
  }

This has to be a string instead of Object, Since you defined it as String

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.