terraform: is there a way to create iam policy statements dynamically?

26k Views Asked by At

Terraform version: 0.11

I am running multiple eks clusters and trying to enable IAM Roles-based service account in all cluster following this doc: https://www.terraform.io/docs/providers/aws/r/eks_cluster.html#enabling-iam-roles-for-service-accounts

This works when I hardcode the cluster name in the policy statement and create multiple statements

data "aws_iam_policy_document" "example_assume_role_policy" {

# for cluster 1

  statement {
    actions = ["sts:AssumeRoleWithWebIdentity"]
    effect  = "Allow"

    condition {
      test     = "StringEquals"
      variable = "${replace(aws_iam_openid_connect_provider.example1.url, "https://", "")}:sub"
      values   = ["system:serviceaccount:kube-system:aws-node"]
    }

    principals {
      identifiers = ["${aws_iam_openid_connect_provider.example1.arn}"]
      type        = "Federated"
    }
  }
}

Since I have multiple clusters, I want to be able to generate the statement dynamically so I made the following changes:

I created a count variable and changed values in principals and and condition

count = "${length(var.my_eks_cluster)}" 

    condition {
      test     = "StringEquals"
      variable = "${replace(element(aws_iam_openid_connect_provider.*.url, count.index), "https://", "")}:sub"
      values   = ["system:serviceaccount:kube-system:aws-node"]
    }

    principals {
      identifiers = ["${element(aws_iam_openid_connect_provider.*.url, count.index)}"]
      type        = "Federated"
    }

Terraform now is able to find the clusters BUT also generate multiple policies. And this will not work, since in the following syntax, the assume_role_policy doesn't take the list

resource "aws_iam_role" "example" {
  assume_role_policy = "${data.aws_iam_policy_document.example_assume_role_policy.*.json}"
  name               = "example"
}

It seems like instead of creating multiple policies, I need to generate multiple statements in one policy (so I can add to one iam_role). Has anyone done something similar before ? Thanks.

1

There are 1 best solutions below

0
On BEST ANSWER

You only want one policy, so you should not use the count argument in your policy. What you want to have instead is multiple statements, like this

data "aws_iam_policy_document" "example" {
  statement {
    # ...
  }
  statement {
    # ...
  }
}

Now you could hard-code this directly (maybe that would be a good start to test if it works). If you want to generate this dynamically from a variable you would need a dynamic-block as described here: https://www.terraform.io/docs/configuration/expressions.html

In your case that would probably be

data "aws_iam_policy_document" "example" {
  dynamic "statement" {
    for_each = aws_iam_openid_connect_provider

    content {
      actions = ["sts:AssumeRoleWithWebIdentity"]
      effect  = "Allow"

      condition {
        test     = "StringEquals"
        variable = "${replace(statement.value.url, "https://", "")}:sub"
          values   = ["system:serviceaccount:kube-system:aws-node"]
      }

      principals {
        identifiers = ["${statement.value.arn}"]
        type        = "Federated"
      }      
    }
  }
}

I think that "dynamic" is only available since TF 0.12, though.