Bryce P
Bryce P

Reputation: 31

Unable to create dynamic terraform outputs for use in terraform_remote_state

I have the following code block for creating various IAM groups

resource "aws_iam_group" "environment-access" {
  count = "${length(var.environments)}"
  name = "access-${element(var.environments, count.index)}"
}

variable "environments" {
  default = ["production", "non-production"]
  type = "list"
}

I want to write the outputs of the IAM groups created in order to grab the ARN of each group to use as data via terraform_remote_state where it would look something like the following

Outputs:

access-production = arn:aws:iam::XXXXXXX:group/basepath/access-production
access-non-production = arn:aws:iam::XXXXXXX:group/basepath/access-non-production

I am having trouble creating the dynamic outputs as I am unsure how to dynamically create the output stanzas based on the the resource originally created as using the below code yields an error referencing unknown resource 'aws_iam_group.access-production' referenced.

output "access-production" {
  value = "${aws_iam_group.access-production.arn}"
}

output "access-non-production" {
  value = "${aws_iam_group.access-non-production.arn}"
}

Upvotes: 0

Views: 3501

Answers (1)

Martin Atkins
Martin Atkins

Reputation: 74694

An initial problem with this requirement is that it calls for having a single dynamic list of environments but multiple separate output values. In order to make this work, you'll need to either make the environment inputs separate values or produce a single output value describing the environments.

# Variant with a fixed set of environments (v0.11 syntax)

variable "production_environment_name" {
  type    = "string"
  default = "production"
}

variable "non_production_environment_name" {
  type    = "string"
  default = "non-production"
}

resource "aws_iam_group" "production_access" {
  name = "access-${var.production_environment_name}"
}

resource "aws_iam_group" "non_production_access" {
  name = "access-${var.non_production_environment_name}"
}

output "access_production" {
  value = "aws_iam_group.production_access.arn"
}

output "access_non_production" {
  value = "aws_iam_group.non_production_access.arn"
}
# Variant with dynamic set of environments (v0.11 syntax)

variable "environments" {
  type    = "list"
  default = ["production", "non_production"]
}

resource "aws_iam_group" "access" {
  count = "${length(var.environments)}"

  name = "access-${var.environments[count.index]}"
}

output "access" {
  value = "${aws_iam_group.access.*.arn}"
}

The key here is that the input variable and the output value must have the same form, so that we can make all of the necessary references between the objects. In the second example, the environment names are provided as a list, and the group ARNs are also provided as a list such that the indices correspond between the two.

You can also use a variant of the output "access" expression to combine the two with zipmap and get a map keyed by the environment names, which will probably be more convenient for the caller to use:

output "access" {
  value = "${zipmap(var.environments, aws_iam_group.access.*.arn)}"
}

The new features in Terraform 0.12 allow tidying this up a bit. Here's an idiomatic Terraform 0.12 equivalent of the version that produces a map as a result:

# Variant with dynamic set of environments (v0.12 syntax)

variable "environments" {
  type    = set(string)
  default = ["production", "non_production"]
}

resource "aws_iam_group" "access" {
  for_each = var.environments

  name = "access-${each.key}"
}

output "access" {
  value = { for env, group in aws_iam_group.access : env => group.arn }
}

As well as having some slightly different syntax patterns, this 0.12 example has an additional practical advantage: Terraform will track those IAM groups with addresses like aws_iam_group.access["production"] and aws_iam_group.access["non_production"], so the positions of the environment names in the var.environments list are not important and it's possible to add and remove environments without potentially disturbing the groups from other environments due to the list element renumbering.

It achieves that by using resource for_each, which makes aws_iam_group.access appear as a map of objects where the environment names are keys, whereas count makes it a list of objects.

Upvotes: 1

Related Questions