@@ -2,6 +2,7 @@ package api_token
22
33import (
44 "regexp"
5+ "sort"
56
67 "github.com/cloudflare/tf-migrate/internal"
78 "github.com/cloudflare/tf-migrate/internal/transform"
@@ -95,6 +96,7 @@ func (m *V4ToV5Migrator) transformPolicyBlocks(body *hclwrite.Body) {
9596// transformPermissionGroups converts permission_groups from list of strings to list of objects
9697// v4: permission_groups = ["id1", "id2"]
9798// v5: permission_groups = [{ id = "id1" }, { id = "id2" }]
99+ // The IDs are sorted alphabetically to match the v5 provider's canonical ordering.
98100func (m * V4ToV5Migrator ) transformPermissionGroups (body * hclwrite.Body ) {
99101 permGroupsAttr := body .GetAttribute ("permission_groups" )
100102 if permGroupsAttr == nil {
@@ -104,13 +106,9 @@ func (m *V4ToV5Migrator) transformPermissionGroups(body *hclwrite.Body) {
104106 // Parse the existing list expression to extract the permission IDs
105107 exprTokens := permGroupsAttr .Expr ().BuildTokens (nil )
106108
107- // Build a list of objects where each string ID becomes { id = "..." }
108- var permObjects []hclwrite.Tokens
109-
110- // We need to manually parse the tokens to extract string values
111- // For simplicity, we'll reconstruct the structure
109+ // Collect all permission group IDs first
110+ var permIDs []string
112111 inList := false
113- var currentID string
114112
115113 for _ , token := range exprTokens {
116114 switch token .Type {
@@ -120,26 +118,33 @@ func (m *V4ToV5Migrator) transformPermissionGroups(body *hclwrite.Body) {
120118 inList = false
121119 case hclsyntax .TokenQuotedLit :
122120 if inList {
123- // Extract the ID from the quoted literal (remove quotes)
124- currentID = string (token .Bytes )
125- // Create an object: { id = "currentID" }
126- objAttrs := []hclwrite.ObjectAttrTokens {
127- {
128- Name : hclwrite .TokensForIdentifier ("id" ),
129- Value : hclwrite .TokensForValue (cty .StringVal (currentID )),
130- },
131- }
132- permObjects = append (permObjects , hclwrite .TokensForObject (objAttrs ))
121+ permIDs = append (permIDs , string (token .Bytes ))
133122 }
134123 }
135124 }
136125
137- // If we found any permission IDs, replace the attribute with the new format
138- if len (permObjects ) > 0 {
139- body .RemoveAttribute ("permission_groups" )
140- listTokens := hclwrite .TokensForTuple (permObjects )
141- body .SetAttributeRaw ("permission_groups" , listTokens )
126+ if len (permIDs ) == 0 {
127+ return
128+ }
129+
130+ // Sort IDs alphabetically to match the v5 provider's canonical ordering
131+ sort .Strings (permIDs )
132+
133+ // Build a list of objects where each string ID becomes { id = "..." }
134+ var permObjects []hclwrite.Tokens
135+ for _ , id := range permIDs {
136+ objAttrs := []hclwrite.ObjectAttrTokens {
137+ {
138+ Name : hclwrite .TokensForIdentifier ("id" ),
139+ Value : hclwrite .TokensForValue (cty .StringVal (id )),
140+ },
141+ }
142+ permObjects = append (permObjects , hclwrite .TokensForObject (objAttrs ))
142143 }
144+
145+ body .RemoveAttribute ("permission_groups" )
146+ listTokens := hclwrite .TokensForTuple (permObjects )
147+ body .SetAttributeRaw ("permission_groups" , listTokens )
143148}
144149
145150// transformResources wraps the resources map with jsonencode()
@@ -200,4 +205,3 @@ func (m *V4ToV5Migrator) transformConditionBlock(body *hclwrite.Body) {
200205 body .SetAttributeRaw ("condition" , conditionTokens )
201206 body .RemoveBlock (conditionBlock )
202207}
203-
0 commit comments