Reputation: 51
I have a problem I need groups to be created and policies attached to those groups but I also need that if the variable policy_name is equal to "" he does not believe me anything, I know that it is repetitive but I have been asked to do it in this way I appreciate your support
This is my main.tf
resource "aws_iam_group" "this" {
count = length(var.name) != 0 ? length(var.name) : 0
name = element(var.name, count.index)
path = var.path
}
resource "aws_iam_policy" "prueba" {
count = var.policy_name != "" ? 1 : 0
name = var.policy_name
policy = jsonencode(var.policy)
}
resource "aws_iam_group_policy_attachment" "test-attach" {
for_each = toset(var.name)
group = each.key
policy_arn = aws_iam_policy.prueba[0].arn
}
This is variables
variable "name" {
description = "Name of IAM group"
type = list
default = []
}
variable "policy" {
description = "The policy in IAM (tpl file)"
type = any
default = null
}
variable "policy_name" {
type = string
default = ""
}
This is de var.tfvars
policy = {
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"autoscaling:Describe*",
"cloudwatch:*",
"logs:*",
"sns:*",
"iam:GetPolicy",
"iam:GetPolicyVersion",
"iam:GetRole"
],
"Effect": "Allow",
"Resource": "*"
},
{
"Effect": "Allow",
"Action": "iam:CreateServiceLinkedRole",
"Resource": "arn:aws:iam:::role/aws-service-role/events.amazonaws.com/AWSServiceRoleForCloudWatchEvents",
"Condition": {
"StringLike": {
"iam:AWSServiceName": "events.amazonaws.com"
}
}
},
{
"Action": [
"cloudwatch:DeleteAlarms",
"cloudwatch:DeleteAnomalyDetector",
"cloudwatch:DeleteDashboards",
"cloudwatch:DeleteInsightRules",
"cloudwatch:DeleteMetricStream"
],
"Effect": "Deny",
"Resource": "*"
},
{
"Action": [
"logs:DeleteDestination",
"logs:DeleteLogDelivery",
"logs:DeleteLogGroup",
"logs:DeleteLogStream",
"logs:DeleteMetricFilter",
"logs:DeleteQueryDefinition",
"logs:DeleteResourcePolicy",
"logs:DeleteRetentionPolicy",
"logs:DeleteSubscriptionFilter"
],
"Effect": "Deny",
"Resource": "*"
},
{
"Action": [
"sns:DeleteEndpoint",
"sns:DeletePlatformApplication",
"sns:DeleteSMSSandboxPhoneNumber",
"sns:DeleteTopic",
"sns:RemovePermission"
],
"Effect": "Deny",
"Resource": "*"
},
{
"Action": "events:*",
"Effect": "Allow",
"Resource": "*"
},
{
"Action": [
"events:RemovePermission",
"events:DeleteApiDestination",
"events:DeleteRule",
"events:DeleteArchive",
"events:DeleteConnection",
"events:DeletePartnerEventSource",
"events:DeleteEventBus"
],
"Effect": "Deny",
"Resource": "*"
}
]
}
name = ["test_group","test_group_1","test_group_2"]
policy_name = ""
This is the error
Error: Invalid index
on ../groups/main.tf line 18, in resource "aws_iam_group_policy_attachment"
"test-attach":
18: policy_arn = aws_iam_policy.prueba[0].arn
├────────────────
│ aws_iam_policy.prueba is empty tuple
The given key does not identify an element in this collection value.
Upvotes: 1
Views: 88
Reputation: 74064
If you have an object that is only defined in certain situations then you must explain to Terraform how other parts of the module should react to that object not being present.
In your case you have an IAM policy that may or may not exist depending on the condition var.policy_name != "" ? 1 : 0
. However, you have another resource which can only exist if the policy does, and so Terraform correctly returned an error explaining that you cannot refer to the ARN of a policy that doesn't exist.
Since the meaning of aws_iam_group_policy_attachment
is to create a relationship between an IAM group and an IAM policy, you presumably need an instance of that resource type for every pair of group and policy. A typical way to represent that relationship is with the setproduct
function, which computes a set of all of the combinations of elements in two other sets.
For example:
resource "aws_iam_group_policy_attachment" "test-attach" {
for_each = {
for pair in setproduct(toset(var.name), toset(aws_iam_policy.prueba)) :
"${pair[0]}:${pair[1].name}" => {
group_name = pair[0]
policy_arn = pair[1].arn
}
}
group = each.value.group_name
policy_arn = each.value.policy_arn
}
This for_each
expression has considerably more components than the one you started with, so I will try to summarize what each part is doing:
{ for ... in ... : ... => ...}
part is a for
expression, which allows deriving one collection value from another collection value. In this case, the source collection is the setproduct
result.setproduct(...)
takes one or more set values and returns a new set of tuples representing all of the combinations of elements in the given sets. In this case, it returns a set of [ group_name, policy_object ]
pairs, where group_name
is one of the elements from var.name
and policy_object
is the object representing one of the instances of aws_iam_policy.prueba
.for_each
requires a unique key for each instance specified as a map key, and so the for
expression is transforming that set of tuples into a map where the keys are built from combining a group name with a policy name, and the values are each an object that describes the one relevant group name and the one relevant policy ARN.each.value
inside that resource block always refers to one of the values from that resulting map, we can refer to each.value.group_name
and each.value.policy_arn
to populate the specific pairing of those for a single instance.Note that setproduct
always returns a set whose number of elements is the multiplication of the number of elements in the given sets, and so if either of them has zero elements the result will have zero elements -- zero multiplied by anything is zero. That then ultimately avoids the problem you encountered here, because if you have either zero groups or zero policies then you will have zero attachments.
Upvotes: 1