I get this error using “concat” (no error without concat function),
Error: Invalid count argument
on .../main.tf line xx, in resource "iam_role_policy_attachment" "custom":
count = var.create_role && length(var.custom_role_policy_arns) > 0 ? length(var.custom_role_policy_arns) : 0
The "count" value depends on resource attributes that cannot be determined
until apply, so Terraform cannot predict how many instances will be created.
To work around this, use the -target argument to first apply only the
resources that the count depends on.
for the below terraform configuration, no error removing concat function, no error using
In this configuration, it appears that the value for module.iam_policy_mod1_backup1.arn inside the concat argument is coming from the managed resource module.iam_policy_mod1_backup1.aws_iam_policy.policy.
This means that during planning the module.iam_policy_mod1_backup1.arn value will be unknown. Because the value is unknown, the result of the concat function will be unknown, since it cannot be determined until apply time how many non-empty values there will be in the list.
Using the list directly works, because the number of items is known, even if they were to end up as null values.
Thank you @jbardin. If functions cannot be used for values not know until apply, how can I achieve this whitout running TF many times (or using -target option) to create such resources before reference them in functions ?
Below what I like to use. Can you depict alternative code for such situations ?
One way some modules do is accept the length value as a parameter (rather than using the length function). That way there are no unknowns for the count, so it works.
It’s not that functions cannot be used in general, but you are using a particular function in a way where the result cannot be determined while planning. The value for count must be known, yet the arguments you are passing to concat mean that the length of the result is unknown.
In this case I would suggest restructuring the configuration so that compact is not needed, and you can use a known length to determine the correct value. From the sections of configuration you’ve shared, there doesn’t seem to be any reason this couldn’t be achieved, as even if the resources in the modules are conditionally created in some way the conditions themselves must be based on known values.
I explane more logic behind the code and ask you a question about TF.
To make code as DRY as possible I used a single module and dependency inversion to configure similar security environments for three account types involved in a backup & disaster recovery solution:
backupper: one or more accounts (normally one) with an ec2 appliance assuming specific role of backed and dr accounts (with specific policies allowing backup operations)
backed: one or more accounts allowing to assume its specific role to need backuppers to do backup of its resources (EC2, etc.)
dr: disaster recovery accounts are as backed accounts but need specific permissions in addition to backed (e.g. dr policy)
as per dependency inversion live configuration passes only “account_is_a_dr” to make module reusable for all types. The latter can be empty so I used compact to allow type (3.) conditionally.
in addition (but this is not the focus here), it can be useful to pass extra arns as permission from live configuration, so I added:
var.iam_policy_extra_arns,
and concat to allow also this extra arns (if defined in live configuration).
Certainly I can simplify and split cases 1., 2. and 3. into different modules (probably 2 will be enough) duplicating lot of code, or adding to all types special permissions now used only for dr, so compact will not be needed anymore without lot of duplication, but probably I’ll face same issue with concat and iam_policy_extra_arns too (i need to try this).
As it works if I create module.iam_policy_mod1_backup1 and module.iam_policy_mod1_backup1 with -target before apply entire configuration, what I ask you is: it’s not an option for TF to catch this issue and decide if a “function” should be evaulated based on plan or apply data so creating resources in right order (since here we do not have a dependency cycle) ?
Sorry, I’m not sure how to continue here, but I’ll try and address your specific question, which may help with any remaining confusion.
Terraform is attempting to evaluate everything as accurately as possible. The fact that when the arguments to compact are unknown, the length of the result of compact is logically unknown as well is not avoidable. If you are certain that the number of elements should be known, then there is absolutely no reason to use compact.
In order for terraform to plan the creation of resources, it must know how many of those resources it is planning, and known values must be used to determine that. It’s possible that in the future we may have some way of running progressive applies in a single execution, but until then it needs to be done manually with multiple configurations or the use of -target.
Terraform is a great tool and do a great job to find dependencies and “to do all” in one apply. Probably it be of great value also a progressive applies behavior.
Sure I lack lot of knowledge of the internal terraform evaluation logic probably known in deept only with source code experience and knowledge.
Great ! But… I’m not completely aware why internally concat function behave differently by compact function as they use the same informations in the same environment:
1. two modules created inside the same terraform config
2. one bool (account_is_a_dr) changing length of resulting list
custom_role_policy_arns will be a list with length = 2 if account_is_a_dr is false
custom_role_policy_arns will be a list with length = 3 if account_is_a_dr is true
Do you explain why concat is executed differently from compact inside terraform ?
I can only guess what is going on without all the values, but the difference is that compact will remove empty strings, and concat does not.
If module.iam_policy_dr1[0].arn is not known, compact cannot determine if that is an empty string which will be removed or not. In the concat example, you have a list with either 2 or 3 values which will not change regardless of what those values turn out to be.
In truth module.iam_policy_dr1[0] is a simple policy created inside the same terraform configuration as module.iam_policy_backup1 and module.iam_policy_backup2 so it is always known as other two resources. It’s only the flag local.account_is_a_dr to choice inclusion or not for “…_dr” policy arn in the arns list.
You’re right on compact and concat functions main difference here:
conpact produces a list with a number of strings (policies arns) known at apply time, so unknown by count at plan time.
concat works on lists but the end result will be always a variable number of strings (policies arns) so probably with concat the computed list of strings is made at plan time not raising the error.
For what I can understand here this is a step forward but still with some doubts not having a clear picture about actions done in which phase (plan/apply).