Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 849
yla Avatar asked Jun 03 '20 23:06

yla


People also ask

How do I add multiple statements in IAM policy?

The Statement element can contain a single statement or an array of individual statements. Each individual statement block must be enclosed in curly braces { }. For multiple statements, the array must be enclosed in square brackets [ ].

What is dynamic terraform?

Terraform provides the dynamic block to create repeatable nested blocks within a resource. A dynamic block is similar to the for expression.


1 Answers

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.

like image 59
Falk Tandetzky Avatar answered Sep 18 '22 10:09

Falk Tandetzky