Keywords: Terraform | for_each | module output
Abstract: This article provides an in-depth exploration of how to correctly configure output values when using for_each to create multiple resources within Terraform modules (version 0.12+). Through analysis of a common error case, it explains why traditional splat expressions (such as .* and [*]) fail with the error "This object does not have an attribute named 'name'" when applied to map types generated by for_each. The focus is on two applications of for expressions: one generating key-value mappings to preserve original identifiers, and another producing lists or sets for deduplicated values. As supplementary reference, an alternative using the values() function is briefly discussed. By comparing the suitability of different approaches, the article helps developers choose the most appropriate output strategy based on practical requirements.
Problem Context and Error Analysis
In Terraform infrastructure-as-code practice, modular design and dynamic resource creation are key to improving configuration reusability and flexibility. When multiple similar resources need to be created based on variable maps, the for_each parameter offers an elegant solution. However, many developers encounter a typical syntax error when attempting to output attributes of these resources: This object does not have an attribute named "name".
Consider the following configuration scenario: a map-type variable bridge_domains is defined in the root module, containing multiple bridge domain configurations. This map is passed to a child module via for_each = var.bridge_domains, and the child module internally uses the mso_schema_template_bd resource to create corresponding bridge domains. The core issue arises during output configuration.
# Example of incorrect output configuration
output "bd_name" {
value = module.schema_template_bd.mso_bd.*.name
}
This error stems from a misunderstanding of Terraform expression semantics. When a resource uses the for_each parameter, Terraform treats it as a map rather than a list. The map keys correspond to the keys in the for_each input, and the values are the individual resource instances. The .* and [*] operators (known as splat expressions) are designed specifically for list types and cannot correctly handle map structures.
Solution: Application of For Expressions
Terraform provides the more general for expression to handle various collection types. Unlike splat expressions, for expressions can process lists, sets, and maps, and can produce output structures of any type.
Option 1: Preserving Key-Value Mapping Relationships
When it is necessary to maintain the correspondence between output values and original inputs, a map-type output can be generated. This approach is particularly useful for scenarios where specific resource attributes need to be referenced later based on original identifiers.
output "bd_name" {
value = {
for k, bd in mso_schema_template_bd.bd : k => bd.name
}
}
In this expression, k represents the keys from the original bridge_domains map (e.g., bd1, bd2), and bd represents the corresponding resource instances. The output will be a map like {bd1 = "BD1", bd2 = "BD2"}, perfectly preserving identifier information.
Option 2: Generating Lists or Sets
If only the values of resource attributes are needed without concern for original identifiers, a list-type output can be generated. This method is suitable for scenarios requiring only collections of attribute values.
output "bd_name" {
value = [
for bd in mso_schema_template_bd.bd : bd.name
]
}
This expression produces a list containing all bridge domain names: ["BD1", "BD2"]. If value uniqueness is required (to avoid duplicates), the toset() function can convert the list to a set:
output "bd_name" {
value = toset([
for bd in mso_schema_template_bd.bd : bd.name
])
}
Supplementary Option: Using the Values() Function
As an alternative, the values() function can convert a map to a list of values, which can then be combined with splat expressions. This approach may be more concise in simple scenarios but offers less flexibility compared to for expressions.
output "bd_name" {
value = values(mso_schema_template_bd.bd).*.name
}
The values() function extracts all values from the map to form a list, and then the .*.name splat expression extracts the name attribute from each element in this list. Note that this method loses the original key information, retaining only the values.
Selection Strategy and Best Practices
When choosing an output strategy, consider the following factors:
- Importance of Identifier Information: If downstream configurations need to know which name corresponds to which original identifier, use the map output option.
- Value Uniqueness Requirements: If duplicate values may exist and deduplication is needed, use the set output option.
- Output Structure Compatibility: Consider how the output values will be used in other modules or external systems, and select the most appropriate structure type.
- Code Readability: Although
forexpressions are slightly more verbose, they offer clearer semantics, especially in complex transformation scenarios.
In practical applications, it is recommended to prioritize for expressions due to their maximum flexibility and expressiveness. Additionally, understanding the distinction between for_each and count is crucial: for_each generates maps, suitable for resources based on string identifiers; count generates lists, suitable for resources based on numeric indices.
Conclusion
Terraform's expression system provides rich tools for handling collection data. When using for_each to create resources, traditional splat expressions are no longer applicable, and developers must transition to the more general for expressions. By appropriately selecting output structures—whether preserving key-value mappings or generating value lists/sets—configuration correctness and usability can be ensured. Understanding these concepts not only helps resolve specific syntax errors but also enhances the design quality of Terraform configurations, laying a solid foundation for complex infrastructure management.