@@ -13,6 +13,7 @@ import (
1313 "github.com/hashicorp/hcl/v2"
1414 "github.com/hashicorp/hcl/v2/hclsyntax"
1515 "github.com/hashicorp/hcl/v2/hclwrite"
16+ tfjson "github.com/hashicorp/terraform-json"
1617 "github.com/zclconf/go-cty/cty"
1718)
1819
@@ -138,9 +139,12 @@ var knownReferences = []string{
138139 "grafana_team_preferences.team_id=grafana_team.id" ,
139140}
140141
141- // TODO: Also find references from the state (computed fields, like ID)
142- func replaceReferences (fpath string , extraKnownReferences []string ) error {
143- file , err := readHCLFile (fpath )
142+ type postprocessor struct {
143+ plannedState * tfjson.Plan
144+ }
145+
146+ func (p * postprocessor ) replaceReferences (fpath string , extraKnownReferences []string ) error {
147+ file , err := p .readHCLFile (fpath )
144148 if err != nil {
145149 return err
146150 }
@@ -149,17 +153,23 @@ func replaceReferences(fpath string, extraKnownReferences []string) error {
149153
150154 knownReferences := knownReferences
151155 knownReferences = append (knownReferences , extraKnownReferences ... )
152- // Find all resources. This map will be used to search for references
153- resourcesBlocks := map [string ]* hclwrite.Block {}
156+
157+ plannedResources := p .plannedState .PlannedValues .RootModule .Resources
158+
154159 for _ , block := range file .Body ().Blocks () {
155- if block .Type () == "resource" {
156- resourcesBlocks [block .Labels ()[0 ]+ "." + block .Labels ()[1 ]] = block
160+ var blockResource * tfjson.StateResource
161+ for _ , plannedResource := range plannedResources {
162+ if plannedResource .Type == block .Labels ()[0 ] && plannedResource .Name == block .Labels ()[1 ] {
163+ blockResource = plannedResource
164+ break
165+ }
166+ }
167+ if blockResource == nil {
168+ return fmt .Errorf ("resource %s.%s not found in planned state" , block .Labels ()[0 ], block .Labels ()[1 ])
157169 }
158- }
159170
160- for _ , block := range file .Body ().Blocks () {
161- for attrName , attr := range block .Body ().Attributes () {
162- attrValue := string (attr .Expr ().BuildTokens (nil ).Bytes ())
171+ for attrName := range block .Body ().Attributes () {
172+ attrValue := blockResource .AttributeValues [attrName ]
163173 attrReplaced := false
164174
165175 // Check the field name. If it has a possible reference, we have to search for it in the resources
@@ -178,20 +188,19 @@ func replaceReferences(fpath string, extraKnownReferences []string) error {
178188 refToResource := strings .Split (refTo , "." )[0 ]
179189 refToAttr := strings .Split (refTo , "." )[1 ]
180190
181- for possibleResourceRefName , possibleResourceRef := range resourcesBlocks {
182- if strings .HasPrefix (possibleResourceRefName , refToResource + "." ) {
183- valueFromRef := ""
184- if possibleResourceRef .Body ().GetAttribute (refToAttr ) != nil {
185- valueFromRef = string (possibleResourceRef .Body ().GetAttribute (refToAttr ).Expr ().BuildTokens (nil ).Bytes ())
186- }
187- // If the value from the first block matches the value from the second block, we have a reference
188- if attrValue == valueFromRef {
189- // Replace the value with the reference
190- block .Body ().SetAttributeTraversal (attrName , traversal (possibleResourceRefName , refToAttr ))
191- hasChanges = true
192- attrReplaced = true
193- break
194- }
191+ for _ , plannedResource := range plannedResources {
192+ if plannedResource .Type != refToResource {
193+ continue
194+ }
195+
196+ valueFromRef := plannedResource .AttributeValues [refToAttr ]
197+ // If the value from the first block matches the value from the second block, we have a reference
198+ if attrValue == valueFromRef {
199+ // Replace the value with the reference
200+ block .Body ().SetAttributeTraversal (attrName , traversal (plannedResource .Type , plannedResource .Name , refToAttr ))
201+ hasChanges = true
202+ attrReplaced = true
203+ break
195204 }
196205 }
197206 }
@@ -206,15 +215,15 @@ func replaceReferences(fpath string, extraKnownReferences []string) error {
206215 return nil
207216}
208217
209- func stripDefaults (fpath string , extraFieldsToRemove map [string ]any ) error {
210- file , err := readHCLFile (fpath )
218+ func ( p * postprocessor ) stripDefaults (fpath string , extraFieldsToRemove map [string ]any ) error {
219+ file , err := p . readHCLFile (fpath )
211220 if err != nil {
212221 return err
213222 }
214223
215224 hasChanges := false
216225 for _ , block := range file .Body ().Blocks () {
217- if s := stripDefaultsFromBlock (block , extraFieldsToRemove ); s {
226+ if s := p . stripDefaultsFromBlock (block , extraFieldsToRemove ); s {
218227 hasChanges = true
219228 }
220229 }
@@ -225,8 +234,8 @@ func stripDefaults(fpath string, extraFieldsToRemove map[string]any) error {
225234 return nil
226235}
227236
228- func wrapJSONFieldsInFunction (fpath string ) error {
229- file , err := readHCLFile (fpath )
237+ func ( p * postprocessor ) wrapJSONFieldsInFunction (fpath string ) error {
238+ file , err := p . readHCLFile (fpath )
230239 if err != nil {
231240 return err
232241 }
@@ -235,7 +244,7 @@ func wrapJSONFieldsInFunction(fpath string) error {
235244 // Find json attributes and use jsonencode
236245 for _ , block := range file .Body ().Blocks () {
237246 for key , attr := range block .Body ().Attributes () {
238- asMap , err := attributeToMap (attr )
247+ asMap , err := p . attributeToMap (attr )
239248 if err != nil || asMap == nil {
240249 continue
241250 }
@@ -252,11 +261,11 @@ func wrapJSONFieldsInFunction(fpath string) error {
252261 return nil
253262}
254263
255- func abstractDashboards (fpath string ) error {
264+ func ( p * postprocessor ) abstractDashboards (fpath string ) error {
256265 fDir := filepath .Dir (fpath )
257266 outPath := filepath .Join (fDir , "files" )
258267
259- file , err := readHCLFile (fpath )
268+ file , err := p . readHCLFile (fpath )
260269 if err != nil {
261270 return err
262271 }
@@ -269,7 +278,7 @@ func abstractDashboards(fpath string) error {
269278 continue
270279 }
271280
272- dashboard , err := attributeToJSON (block .Body ().GetAttribute ("config_json" ))
281+ dashboard , err := p . attributeToJSON (block .Body ().GetAttribute ("config_json" ))
273282 if err != nil {
274283 return err
275284 }
@@ -316,7 +325,7 @@ func abstractDashboards(fpath string) error {
316325 return nil
317326}
318327
319- func attributeToMap (attr * hclwrite.Attribute ) (map [string ]interface {}, error ) {
328+ func ( p * postprocessor ) attributeToMap (attr * hclwrite.Attribute ) (map [string ]interface {}, error ) {
320329 var err error
321330
322331 // Convert jsonencode to raw json
@@ -345,8 +354,8 @@ func attributeToMap(attr *hclwrite.Attribute) (map[string]interface{}, error) {
345354 return dashboardMap , nil
346355}
347356
348- func attributeToJSON (attr * hclwrite.Attribute ) ([]byte , error ) {
349- jsonMap , err := attributeToMap (attr )
357+ func ( p * postprocessor ) attributeToJSON (attr * hclwrite.Attribute ) ([]byte , error ) {
358+ jsonMap , err := p . attributeToMap (attr )
350359 if err != nil || jsonMap == nil {
351360 return nil , err
352361 }
@@ -359,7 +368,7 @@ func attributeToJSON(attr *hclwrite.Attribute) ([]byte, error) {
359368 return jsonMarshalled , nil
360369}
361370
362- func readHCLFile (fpath string ) (* hclwrite.File , error ) {
371+ func ( p * postprocessor ) readHCLFile (fpath string ) (* hclwrite.File , error ) {
363372 src , err := os .ReadFile (fpath )
364373 if err != nil {
365374 return nil , err
@@ -373,10 +382,10 @@ func readHCLFile(fpath string) (*hclwrite.File, error) {
373382 return file , nil
374383}
375384
376- func stripDefaultsFromBlock (block * hclwrite.Block , extraFieldsToRemove map [string ]any ) bool {
385+ func ( p * postprocessor ) stripDefaultsFromBlock (block * hclwrite.Block , extraFieldsToRemove map [string ]any ) bool {
377386 hasChanges := false
378387 for _ , innblock := range block .Body ().Blocks () {
379- if s := stripDefaultsFromBlock (innblock , extraFieldsToRemove ); s {
388+ if s := p . stripDefaultsFromBlock (innblock , extraFieldsToRemove ); s {
380389 hasChanges = true
381390 }
382391 if len (innblock .Body ().Attributes ()) == 0 && len (innblock .Body ().Blocks ()) == 0 {
@@ -405,7 +414,7 @@ func stripDefaultsFromBlock(block *hclwrite.Block, extraFieldsToRemove map[strin
405414 if name == key {
406415 toRemove := false
407416 fieldValue := strings .TrimSpace (string (attribute .Expr ().BuildTokens (nil ).Bytes ()))
408- fieldValue , err := extractJSONEncode (fieldValue )
417+ fieldValue , err := p . extractJSONEncode (fieldValue )
409418 if err != nil {
410419 continue
411420 }
@@ -425,6 +434,16 @@ func stripDefaultsFromBlock(block *hclwrite.Block, extraFieldsToRemove map[strin
425434 }
426435 return hasChanges
427436}
437+ func (p * postprocessor ) extractJSONEncode (value string ) (string , error ) {
438+ if ! strings .HasPrefix (value , "jsonencode(" ) {
439+ return "" , nil
440+ }
441+ value = strings .TrimPrefix (value , "jsonencode(" )
442+ value = strings .TrimSuffix (value , ")" )
443+
444+ b , err := json .MarshalIndent (value , "" , " " )
445+ return string (b ), err
446+ }
428447
429448// BELOW IS FROM https://github.com/hashicorp/terraform/blob/main/internal/configs/hcl2shim/values.go
430449
@@ -472,14 +491,3 @@ func HCL2ValueFromConfigValue(v interface{}) cty.Value {
472491 panic (fmt .Errorf ("can't convert %#v to cty.Value" , v ))
473492 }
474493}
475-
476- func extractJSONEncode (value string ) (string , error ) {
477- if ! strings .HasPrefix (value , "jsonencode(" ) {
478- return "" , nil
479- }
480- value = strings .TrimPrefix (value , "jsonencode(" )
481- value = strings .TrimSuffix (value , ")" )
482-
483- b , err := json .MarshalIndent (value , "" , " " )
484- return string (b ), err
485- }
0 commit comments