Terraform: Merge a List ofObjects into One


The goal is to Merge a variable List of Objects from a Terraform Data Source into One Object using Terraform's Merge function.
 mergedExpanded = merge(local.policy_parameters[*].parameters...)  
 mergedHardcoded = merge(local.mergedExpanded[0].parameters,local.mergedExpanded[1].parameters,local.mergedExpanded[2].parameters) 


Note, Terraform can Expand Function Arguments

If the arguments to pass to a function are available in a list or tuple value, that value can be expanded into separate arguments. Provide the list value as an argument and follow it with the ... symbol:
 myList = [{key=value,key2=value}, {key3=value,key4=value4},{key=value,key4=value4}]   
 merge(local.myList...)
The expansion symbol is three periods (...) and not a Unicode ellipsis character. Expansion is a special syntax that is only available in function calls.

Given Problem

We have the existing Policy Definitions:
 require-terraTest1-tag  
 require-terraTest2-tag  
 require-terraTest3-tag  

Which we can extract using a data source:
 data "azurerm_policy_definition" "d_policy_definitions" {  
   count = 3  
   display_name = "require-terraTest${count.index}-tag"  
 }  

We must then extract the parameters from the result, then decode it as JSON using the jsondecode() function:
 policy_parameters = [  
  for key,value in data.azurerm_policy_definition.d_policy_definitions:  
  {  
      parameters = jsondecode(value.parameters)     
  }  
 ]  

We should now be able to Merge all of the Parameters into one variable.
 mergedExpanded = merge(local.policy_parameters[*].parameters...)  
 mergedHardcoded = merge(local.mergedExpanded[0].parameters,local.mergedExpanded[1].parameters,local.mergedExpanded[2].parameters) 


Expected Behaviour:

Test1 and Test2 should have the same output.

Actual Behaviour:

Test1 returns an error:
"The expanding argument (indicated by ...) must be of a tuple, list, or set  type."

Test2 Works as expected.


Acceptance of the Limitation


As of today April 17, 2020. Terraform version 0.12 cannot merge a Tuple Variable by Expanding Function Arguments.

I have opened a Github Issue regarding this challenge.


The Workaround


I managed to find a workaround, which I do not claim to be optimized.
 policy_parameters = [  
   for key,value in data.azurerm_policy_definition.d_policy_definitions:  
    {  
     parameters = jsondecode(value.parameters)  
    }  
 ]  
 ph_parameters = local.policy_parameters[*].parameters  
 input_parameter = [for item in local.ph_parameters: merge(item,local.ph_parameters...)]  


Break down:


Extract the parameter values into a list of JSON values
 policy_parameters = [  
   for key,value in data.azurerm_policy_definition.d_policy_definitions:  
    {  
     parameters = jsondecode(value.parameters)  
    }  
  ]  

Reference the parameters as a variable
ph_parameters = local.policy_parameters[*].parameters  

Merge all item content into each item.
 input_parameter = [for item in local.ph_parameters: merge(item,local.ph_parameters...)]

The 3rd step gives all items in the list the same value, so we can use any index. The safest index to use is 0.
Usage:
 parameters = "${jsonencode(local.input_parameter[n])}"


Conclusion

Even though this method works, I think this workaround is wasteful. Imagine having 200 indices with the same value. We'll just have to wait until the next Terraform Update.


Comments

Popular posts from this blog

Send SMS using Windows and any GSM Modem

SMS Notification using Gammu on Linux