Build output map in terraform

MardyDev picture MardyDev · Mar 5, 2019 · Viewed 9.2k times · Source

I have a list of users to create, a list of sns topics and to create policies to give permissions to users on topics. These are all namespaced against the user...

Given:

main.tf


provider "aws" {
  region                  = "eu-west-1"
  profile                 = "terraform"
}

module "topics" {
  source = "./queues/topics"
}

module "users" {
  source = "./users"
}

module "policies" {
  source = "./policies"

  sns_topics = "${module.topics.sns_topics}"
}

./queues/topics.tf

resource "aws_sns_topic" "svc_topic" {
  count = "${length(var.sns_topics)}"
  name = "${element(var.sns_topics, count.index)}"
}

./queues/topics/vars.tf

# List of topics
variable "sns_topics" {
  type = "list"

  default = [
    "a-topic",
    "b-topic",
    "c-topic",
  ]
}

./queues/topics/output.tf

output "sns_topics" {
  value = "${var.sns_topics}"
}

./users/main.tf

resource "aws_iam_user" "usrs" {
  count = "${length(var.topic_user)}"
  name = "usr-msvc-${element(var.topic_user, count.index)}"
}

./users/vars.tf

variable "topic_user" {
  type = "list"

  default =[
    "user-a",
    "user-b",
    "user-c",
  ]
}

./users/output.tf

output "topic_user" {
  value = "${var.topic_user}"
}

./policies/main.tf

resource "aws_iam_policy" "sns_publisher" {
  count = "${length(var.sns_topics)}"

  name = "sns-${element(var.sns_topics, count.index)}-publisher"
  policy = <<POLICY
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "sns:Publish",
      "Resource": "arn:aws:sns:*:*:${element(var.sns_topics, count.index)}"
    }
  ]
}
POLICY
}

This is where I'd like to build a map in the output to map user to topic

output "usr_topic_map" {
  value = {
    "user-a" = "a-topic
    "user-b" = "c-topic
    "user-c" = "c-topic
  }
}

I can pass the list of users in to the policy module but I've no idea how to generate this map in the output.

I want to use this to attach the policy to the corresponding user.

Open to improving structure too if it simplifies tasks.

Answer

Matt Schuchard picture Matt Schuchard · Mar 5, 2019

You can do this with the Terraform function zipmap. Since your keys are output from the users module as the list module.users.topic_user and your values are output from the topics modules as the list module.topics.sns_topics (module output doc), you can make them the arguments to the function in the output:

output "user_topic_map" {
  value = "${zipmap(module.users.topic_user, module.topics.sns_topics)}"
}

Remember that the two argument lists to zipmap need to be of equal length, so possibly guard code around that too somewhere in the resource/variable/output blocks.