Jenobi
Jenobi

Reputation: 468

Dynamically create JSON template in Terraform

Trying to dynamically create a json object that I can pass to another module in Terraform, however this is proving to be difficult and/or may not be feasible.

I am fetching a list of IAM Roles, via

data "aws_iam_roles" "matching_roles" {
  for_each   = toset(["role-prefix1-.*", "ecs-role.*"])
  name_regex = each.value
}

This provides a map which includes a section of arns. I then collect all the arns into a list via

locals {
  flat_arns = flatten([
    for key, role_set in data.aws_iam_roles.matching_roles_2 : role_set.arns
  ])
}

Next I want to then encode this list into a json template. I have tried using the following


data "template_file" "dynamic_roles_os" {
  template = <<EOF
{
  "roles": [
    {% for role in roles %}
    {
      "role_name": "all_access",
      "role_mapping_payload": {
        "backend_roles": [
          "${role}"
        ],
        "hosts": [],
        "users": []
      }
    }{% if not loop.last %},{% endif %}
    {% endfor %}
  ]
}
EOF

  vars = {
    roles = local.flat_arns
  }
}

resource "local_file" "backend_roles_json" {
  content  = data.template_file.dynamic_roles_os.rendered
  filename = "${path.module}/backend_roles.json"
}

But this breaks as the above is looking for role to include an attribute. error

?[31m╷?[0m?[0m
?[31m│?[0m ?[0m?[1m?[31mError: ?[0m?[0m?[1mInvalid reference?[0m
?[31m│?[0m ?[0m
?[31m│?[0m ?[0m?[0m  on dynamic-roles.tf line 266, in data "template_file" "dynamic_roles_os":
?[31m│?[0m ?[0m 266:           "${?[4mrole?[0m}"?[0m
?[31m│?[0m ?[0m
?[31m│?[0m ?[0mA reference to a resource type must be followed by at least one attribute
?[31m│?[0m ?[0maccess, specifying the resource name.
?[31m╵?[0m?[0m
Operation failed: failed running terraform plan (exit 1)

Additionally, I have tried to do the following

locals {

  matching_roles = flatten([
    for key, role_set in data.aws_iam_roles.matching_roles_2 : [
      for arn in role_set.arns : {
        arn = arn
      }
    ]
  ])
}

data "template_file" "dynamic_roles_os" {
  template = <<EOF
{
  "roles": [
    {% for role in roles %}
    {
      "role_name": "all_access",
      "role_mapping_payload": {
        "backend_roles": [
          "${role.arn}"
        ],
        "hosts": [],
        "users": []
      }
    }{% if not loop.last %},{% endif %}
    {% endfor %}
  ]
}
EOF

  vars = {
    roles = local.matching_roles
  }
}
resource "local_file" "backend_roles_json" {
  content  = data.template_file.dynamic_roles_os.rendered
  filename = "${path.module}/backend_roles.json"
}

But this errors with

?[31m│?[0m ?[0m?[1m?[31mError: ?[0m?[0m?[1mReference to undeclared resource?[0m
?[31m│?[0m ?[0m
?[31m│?[0m ?[0m?[0m  on dynamic-roles.tf line 268, in data "template_file" "dynamic_roles_os":
?[31m│?[0m ?[0m 268:           "${?[4mrole.arn?[0m}"?[0m
?[31m│?[0m ?[0m
?[31m│?[0m ?[0mA managed resource "role" "arn" has not been declared in the root module.
?[31m╵?[0m?[0m
Operation failed: failed running terraform plan (exit 1)

So how can I properly create a json template with the ARNs from this data source? I have looked into this Pass list variable to JSON template in terraform But I am still unable to come up with a working solution.

I would expect this to be feasible with the different terraform functions supporting json encoding and template strings.

Upvotes: 0

Views: 38

Answers (1)

Jenobi
Jenobi

Reputation: 468

I have found a working solution without using the template_file data. Instead using merge(), jsonencode(). This does require having the input file saved as a json file. Would be interested to know if there are other solutions to this problem

locals {
  # Read and decode the input JSON file
  input_json_file  = "${path.module}/input.json"
  output_json_file = "${path.module}/output.json"

  input_json = jsondecode(file(local.input_json_file))

  # Extract ARNs from the matching roles
  flat_arns = flatten([
    for key, role_set in data.aws_iam_roles.matching_roles : role_set.arns
  ])

  # Update the backend_roles in the reserved_roles
  updated_reserved_roles = [
    for role in local.input_json.reserved_roles : merge(role, {
      role_mapping_payload = merge(role.role_mapping_payload, {
        backend_roles = local.flat_arns
      })
    })
  ]

  # Create the modified JSON
  modified_json = merge(local.input_json, {
    reserved_roles = local.updated_reserved_roles
  })
}

resource "local_file" "output_json" {
  content  = jsonencode(local.modified_json)
  filename = local.output_json_file
}

output "modified_json" {
  value = local.modified_json
}

output "output_json_file" {
  value = local_file.output_json.content
}

output "arns" {
  value = local.flat_arns
}

Upvotes: 0

Related Questions