Open scalp42 opened 1 year ago
@4n3w saved the ~day~ night:
locals {
role_exists = can(data.aws_iam_role.AWSServiceRoleForAutoScaling.name)
}
data "aws_iam_role" "AWSServiceRoleForAutoScaling" {
name = "AWSServiceRoleForAutoScaling"
}
resource "aws_iam_service_linked_role" "AWSServiceRoleForAutoScaling" {
count = local.role_exists ? 0 : 1
aws_service_name = "autoscaling.amazonaws.com"
description = "Default Service-Linked Role enables access to AWS Services and Resources used or managed by Auto Scaling."
}
It's not 100% the same behavior but probably can close as can
/try
help with that AFAIK. If a maintainer can confirm that this is the way to go, I'll take it 🙇
Hi @scalp42,
The primary purpose of check
blocks -- what distinguishes them from preconditions and postconditions -- is that they never block forward progress and instead just act as an additional signal about whether the system is functioning as intended.
That means that in practice nothing can refer to a check block, because by definition checks must always be evaluated only after everything else has been dealt with. In a sense, a check block implicitly depends on everything else in the configuration and so if something else refers to it then that would produce a dependency cycle.
Given that, I don't think the specific solution you've proposed is viable, but I'd like to learn more about your use-case regardless of the specific proposed solution, because we might be able to find another way to get there using another language feature.
However, it seems like your goal might be "create this if it doesn't already exist", in which case that is intentionally not allowed because Terraform is a desired state system and so you must tell it what it is supposed to be managing or else its behavior would be unpredictable.
For example, if we were to implement exactly what you proposed (the dependency ordering issue notwithstanding) then on the first run Terraform would find that the object doesn't exist yet and therefore see your declaration that it should exist, but then on the next run Terraform would find that the object already exists and therefore conclude that you want zero instances of the resource. Terraform would see that you currently have one instance of that resource, and so would propose to destroy it to converge with the new desired state. Then the third run would propose to create it again, and so on.
Instead, your configuration should state whether the object is already expected to exist or not, and should therefore fail if that expectation isn't met.
@apparentlymart thanks for the detailed answer.
Unfortunately, can
won't work here because the data
source triggers first. I just went with the import
resource for now (which doesn't support interpolation unfortunately).
While I understand the core philosophy of Terraform, I sometimes wish some of the choices could be left to the user. That being said, a lot of progress has been made (like can
, import
etc) and reflecting back on the earlier years of Terraform, a lot of flexibility was added so I understand. I just wish we could have a more of it at times.
Thanks again to you @apparentlymart and a special mention to @ewbankkit! Your contributions over the years are truly valued and appreciated ❤️
We should probably consider closing this issue.
@scalp42, you could use the aws_iam_roles
(plural) data source, then build your aws_iam_role
(singular) data source from that, and then use a couple of local variables to check if your IAM Role should be managed.
data.aws_iam_roles
: Using the the name_regex
argument, you can ensure that only IAM Roles with exactly the given name will be discovered.data.aws_iam_role
: Will create a set of IAM Roles based on those discovered by data.aws_iam_roles
. So if the set is empty, it can be assumed your IAM Role didn't exist. If it is not empty, it can be assumed that the role does exist. You can then use locals to ascertain whether or not Terraform should manage that IAM Role.local.role_exists
: Will be true
or false
depending on whether or not `data.aws_iam_roles
discovered your IAM Role.local.role_has_tag
: Whether or not your IAM Role (if discovered) has a specific value for the ManagedBy
tag.This solution isn't ideal though
ManagedBy
tag with the value your Terraform code expects to an existing IAM Role of that name, then Terraform will assume that it should manage it and try to create it, which will cause an error.ManagedBy
tag from the IAM Role managed by your code, it will cause your code to think that it should not be managing the IAM Role and it will be destroyed.variable "role_name" {
description = "The name of the role to create."
type = string
default = "my-role"
}
variable "role_managed_by" {
description = "The entitiy that manages the role."
type = string
default = "my-terraform"
}
data "aws_iam_roles" "my_role_only" {
name_regex = "^${var.role_name}$"
}
data "aws_iam_role" "my_role" {
for_each = data.aws_iam_roles.my_role_only.names
name = each.value
}
locals {
role_exists = length(data.aws_iam_roles.my_role_only.names) == 1
role_has_tag = lookup(lookup(lookup(data.aws_iam_role.my_role, var.role_name, {}), "tags", {}), "ManagedBy", "NOT_FOUND") == var.role_managed_by
}
resource "aws_iam_role" "my_role" {
count = (!local.role_exists) || (local.role_exists && local.role_has_tag) ? 1 : 0
name = var.role_name
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
}
]
})
tags = {
ManagedBy = var.role_managed_by
}
}
Terraform Version
Use Cases
data
sources return an error when checking for something that doesn't exist.With the new
check
resource added, it'd be great if we could use it to wrapdata
source that could fail and use the result of the check as a boolean forcount
attribute for example.In the case of an AWS IAM service role, you may end up in a situation where the role might already exist (AWS create the roles "for you" when accessing/enabling certain services through the UI) or not (never use the service).
Writing Terraform config for this use case becomes painful afaik and solutions are limited:
null_resource
to script a way around the problemAttempted Solutions
Use a Terraform
null_resource
resource withlocal-exec
:Proposal
It'd be amazing if we could simply just refer to the
check
resource with some kind of "return" parameter to dictate the behavior:References
34208