83

I want to attach one of the pre-existing AWS managed roles to a policy, here's my current code:

resource "aws_iam_role_policy_attachment" "sto-readonly-role-policy-attach" {
  role       = "${aws_iam_role.sto-test-role.name}"
  policy_arn = "arn:aws:iam::aws:policy/ReadOnlyAccess"
}

Is there a better way to model the managed policy and then reference it instead of hardcoding the ARN? It just seems like whenever I hardcode ARNs / paths or other stuff like this, I usually find out later there was a better way.

Is there something already existing in Terraform that models managed policies? Or is hardcoding the ARN the "right" way to do it?

GabLeRoux
  • 13,130
  • 13
  • 56
  • 74
Shorn
  • 13,549
  • 11
  • 63
  • 124
  • 7
    Yes, hardcore with pre-existing AWS managed roles is the right way, otherwise, you can define the similar policy and attach it. – BMW Jul 10 '17 at 00:56

2 Answers2

131

The IAM Policy data source is great for this. A data resource is used to describe data or resources that are not actively managed by Terraform, but are referenced by Terraform.

For your example, you would create a data resource for the managed policy as follows:

data "aws_iam_policy" "ReadOnlyAccess" {
  arn = "arn:aws:iam::aws:policy/ReadOnlyAccess"
}

The name of the data source, ReadOnlyAccess in this case, is entirely up to you. For managed policies I use the same name as the policy name for the sake of consistency, but you could just as easily name it readonly if that suits you.

You would then attach the IAM policy to your role as follows:

resource "aws_iam_role_policy_attachment" "sto-readonly-role-policy-attach" {
  role       = "${aws_iam_role.sto-test-role.name}"
  policy_arn = "${data.aws_iam_policy.ReadOnlyAccess.arn}"
}
GabLeRoux
  • 13,130
  • 13
  • 56
  • 74
jorelli
  • 7,045
  • 3
  • 34
  • 35
  • seems this doesn't always work, for example `AWSLambdaVPCAccessExecutionRole` – Bill Oct 12 '18 at 00:21
  • @Bill - watch out for resources that get "auto-created" for you by the AWS console (as mentioned in https://stackoverflow.com/a/38428834/924597). No idea if that's the problem you're having, just thought I'd mention the possibility. – Shorn Nov 14 '18 at 00:38
  • 1
    There are some `service-linked` role, for example, the sample is `arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole`. A feature request were raised by someone already: https://github.com/terraform-providers/terraform-provider-aws/issues/6072 – Bill Nov 14 '18 at 02:46
  • 1
    you can place the ARN in line having only one block for the attachment: resource "aws_iam_role_policy_attachment" "sto-readonly-role-policy-attach" { role = "${aws_iam_role.sto-test-role.name}" policy_arn = "arn:aws:iam::aws:policy/ReadOnlyAccess" } – Arcones Jul 09 '19 at 12:53
  • 1
    I can confirm it works with `AWSLambdaVPCAccessExecutionRole` – Daniel Aug 12 '19 at 21:17
  • 1
    Can you please explain why this is better than hardcoding the arn into `aws_iam_role_policy_attachment`? You hardcode it in both cases. – Ikar Pohorský Feb 12 '20 at 13:53
  • 3
    @IkarPohorský it makes no difference to your infrastructure, but it does make a difference to tools that are built on top of terraform. If you create a data object, the dependency is tracked in terraform alongside all of your other resources. Internally, terraform is building a graph of your infrastructure. You can dump this graph with `terraform graph`. You'll see the data object in the output if it's tracked as a data dependency, but not if you hard code a string. If you have GraphViz installed, visualize it with `terraform graph | dot -Tsvg > graph.svg`. – jorelli Mar 07 '20 at 18:17
  • Is there a way to check if the managed policy is already attached to the target role or not? say a check before attaching and attach only if it not already attach to the Role. – Kamlendra Sharma Oct 15 '20 at 11:02
14

When using values that Terraform itself doesn't directly manage, you have a few options.

The first, simplest option is to just hard-code the value as you did here. This is a straightforward answer if you expect that the value will never change. Given that these "canned policies" are documented, built-in AWS features they likely fit this criteria.

The second option is to create a Terraform module and hard-code the value into that, and then reference this module from several other modules. This allows you to manage the value centrally and use it many times. A module that contains only outputs is a common pattern for this sort of thing, although you could also choose to make a module that contains an aws_iam_role_policy_attachment resource with the role set from a variable.

The third option is to place the value in some location that Terraform can retrieve values from, such as Consul, and then retrieve it from there using a data source. With only Terraform in play, this ends up being largely equivalent to the second option, though it means Terraform will re-read it on each refresh rather than only when you update the module using terraform init -upgrade, and thus this could be a better option for values that change often.

The fourth option is to use a specialized data source that can read the value directly from the source of truth. Terraform does not currently have a data source for fetching information on AWS managed policies, so this is not an option for your current situation, but can be used to fetch other AWS-defined data such as the AWS IP address ranges, service ARNs, etc.

Which of these is appropriate for a given situation will depend on how commonly the value changes, who manages changes to it, and on the availability of specialized Terraform data sources.

Shorn
  • 13,549
  • 11
  • 63
  • 124
Martin Atkins
  • 30,327
  • 3
  • 66
  • 78