Skip to content

Commit d8abfce

Browse files
authored
Merge branch 'main' into ssicard/managed-transforms
2 parents a466406 + 3505881 commit d8abfce

File tree

74 files changed

+16491
-702
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+16491
-702
lines changed

cmd/tf-migrate/main.go

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"os"
66
"path/filepath"
7+
"regexp"
78
"strings"
89

910
"github.com/cloudflare/cloudflare-go/v6"
@@ -336,14 +337,16 @@ func processConfigFiles(log hclog.Logger, p *pipeline.Pipeline, cfg config, stat
336337
return parsedConfigs, nil
337338
}
338339

339-
// applyGlobalPostprocessing applies cross-file reference updates for resource renames
340+
// applyGlobalPostprocessing applies cross-file reference updates for resource and attribute renames
340341
func applyGlobalPostprocessing(log hclog.Logger, cfg config, outputPaths []string) error {
341-
// Collect resource renames from all migrators
342+
// Collect resource renames and attribute renames from all migrators
342343
providers := getProviders(cfg.resourcesToMigrate...)
343344
migrators := providers.GetAllMigrators(cfg.sourceVersion, cfg.targetVersion, cfg.resourcesToMigrate...)
344345

345346
// Map to store old type -> new type mappings
346347
renames := make(map[string]string)
348+
// Slice to store attribute renames
349+
var attributeRenames []transform.AttributeRename
347350

348351
for _, migrator := range migrators {
349352
// Check if this migrator implements ResourceRenamer interface
@@ -367,15 +370,30 @@ func applyGlobalPostprocessing(log hclog.Logger, cfg config, outputPaths []strin
367370
log.Warn("Migrator does not implement ResourceRenamer interface - cross-file references may not be updated",
368371
"migrator", fmt.Sprintf("%T", migrator))
369372
}
373+
374+
// Check if this migrator implements AttributeRenamer interface
375+
if attrRenamer, ok := migrator.(transform.AttributeRenamer); ok {
376+
renames := attrRenamer.GetAttributeRenames()
377+
if len(renames) > 0 {
378+
attributeRenames = append(attributeRenames, renames...)
379+
for _, r := range renames {
380+
log.Debug("Collected attribute rename",
381+
"resource_type", r.ResourceType,
382+
"old_attr", r.OldAttribute,
383+
"new_attr", r.NewAttribute)
384+
}
385+
}
386+
}
370387
}
371388

372389
// If no renames found, skip global postprocessing
373-
if len(renames) == 0 {
374-
log.Debug("No resource renames found, skipping global postprocessing")
390+
if len(renames) == 0 && len(attributeRenames) == 0 {
391+
log.Debug("No renames found, skipping global postprocessing")
375392
return nil
376393
}
377394

378-
fmt.Printf("\nApplying cross-file reference updates (%d renames across %d files)...\n", len(renames), len(outputPaths))
395+
totalUpdates := len(renames) + len(attributeRenames)
396+
fmt.Printf("\nApplying cross-file reference updates (%d updates across %d files)...\n", totalUpdates, len(outputPaths))
379397

380398
// Apply renames to all files
381399
for _, outputPath := range outputPaths {
@@ -388,7 +406,7 @@ func applyGlobalPostprocessing(log hclog.Logger, cfg config, outputPaths []strin
388406
contentStr := string(content)
389407
modified := false
390408

391-
// Apply all renames
409+
// Apply all resource type renames
392410
for oldType, newType := range renames {
393411
newContent := strings.ReplaceAll(contentStr, oldType+".", newType+".")
394412
if newContent != contentStr {
@@ -398,6 +416,30 @@ func applyGlobalPostprocessing(log hclog.Logger, cfg config, outputPaths []strin
398416
}
399417
}
400418

419+
// Apply all attribute renames
420+
// Pattern: data.cloudflare_zones.<instance_name>.zones → data.cloudflare_zones.<instance_name>.result
421+
// We need to match: <ResourceType>.<instance_name>.<OldAttribute>
422+
for _, rename := range attributeRenames {
423+
// Build regex pattern: data\.cloudflare_zones\.([a-zA-Z0-9_-]+)\.zones
424+
// The instance name can contain letters, numbers, underscores, and hyphens
425+
pattern := regexp.QuoteMeta(rename.ResourceType) + `\.([a-zA-Z0-9_-]+)\.` + regexp.QuoteMeta(rename.OldAttribute)
426+
re := regexp.MustCompile(pattern)
427+
428+
// Replace with: data.cloudflare_zones.$1.result (preserving instance name)
429+
replacement := rename.ResourceType + ".$1." + rename.NewAttribute
430+
newContent := re.ReplaceAllString(contentStr, replacement)
431+
432+
if newContent != contentStr {
433+
modified = true
434+
contentStr = newContent
435+
log.Debug("Updated attribute references",
436+
"file", filepath.Base(outputPath),
437+
"resource_type", rename.ResourceType,
438+
"old_attr", rename.OldAttribute,
439+
"new_attr", rename.NewAttribute)
440+
}
441+
}
442+
401443
// Write back if modified
402444
if modified {
403445
if err := os.WriteFile(outputPath, []byte(contentStr), 0644); err != nil {
@@ -406,7 +448,7 @@ func applyGlobalPostprocessing(log hclog.Logger, cfg config, outputPaths []strin
406448
}
407449
}
408450

409-
fmt.Printf("✓ Updated cross-file references (%d renames applied)\n", len(renames))
451+
fmt.Printf("✓ Updated cross-file references (%d updates applied)\n", totalUpdates)
410452
return nil
411453
}
412454

integration/v4_to_v5/testdata/api_token/expected/api_token.tf

Lines changed: 85 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ resource "cloudflare_api_token" "basic_token" {
1414

1515
policies = [{
1616
effect = "allow"
17-
resources = {
17+
resources = jsonencode({
1818
"com.cloudflare.api.account.*" = "*"
19-
}
19+
})
2020
permission_groups = [{
2121
id = "c8fed203ed3043cba015a93ad1616f1f"
2222
}, {
@@ -32,17 +32,17 @@ resource "cloudflare_api_token" "multi_policy_token" {
3232

3333
policies = [{
3434
effect = "allow"
35-
resources = {
35+
resources = jsonencode({
3636
"com.cloudflare.api.account.*" = "*"
37-
}
37+
})
3838
permission_groups = [{
3939
id = "c8fed203ed3043cba015a93ad1616f1f"
4040
}]
4141
}, {
4242
effect = "deny"
43-
resources = {
43+
resources = jsonencode({
4444
"com.cloudflare.api.account.zone.*" = "*"
45-
}
45+
})
4646
permission_groups = [{
4747
id = "82e64a83756745bbbb1c9c2701bf816b"
4848
}]
@@ -56,9 +56,9 @@ resource "cloudflare_api_token" "conditional_token" {
5656

5757
policies = [{
5858
effect = "allow"
59-
resources = {
59+
resources = jsonencode({
6060
"com.cloudflare.api.account.*" = "*"
61-
}
61+
})
6262
permission_groups = [{
6363
id = "c8fed203ed3043cba015a93ad1616f1f"
6464
}]
@@ -80,10 +80,10 @@ resource "cloudflare_api_token" "advanced_condition_token" {
8080

8181
policies = [{
8282
effect = "allow"
83-
resources = {
83+
resources = jsonencode({
8484
"com.cloudflare.api.account.*" = "*"
8585
"com.cloudflare.api.account.zone.*" = "*"
86-
}
86+
})
8787
permission_groups = [{
8888
id = "c8fed203ed3043cba015a93ad1616f1f"
8989
}]
@@ -111,9 +111,9 @@ resource "cloudflare_api_token" "time_limited_token" {
111111

112112
policies = [{
113113
effect = "allow"
114-
resources = {
114+
resources = jsonencode({
115115
"com.cloudflare.api.account.*" = "*"
116-
}
116+
})
117117
permission_groups = [{
118118
id = "c8fed203ed3043cba015a93ad1616f1f"
119119
}]
@@ -126,9 +126,9 @@ resource "cloudflare_api_token" "empty_perms_token" {
126126

127127
policies = [{
128128
effect = "allow"
129-
resources = {
129+
resources = jsonencode({
130130
"com.cloudflare.api.account.*" = "*"
131-
}
131+
})
132132
permission_groups = [{
133133
id = "c8fed203ed3043cba015a93ad1616f1f"
134134
}]
@@ -145,18 +145,18 @@ resource "cloudflare_api_token" "full_example" {
145145

146146
policies = [{
147147
effect = "allow"
148-
resources = {
148+
resources = jsonencode({
149149
"com.cloudflare.api.account.*" = "*"
150150
"com.cloudflare.api.account.zone.*" = "*"
151-
}
151+
})
152152
permission_groups = [{
153153
id = "c8fed203ed3043cba015a93ad1616f1f"
154154
}]
155155
}, {
156156
effect = "deny"
157-
resources = {
157+
resources = jsonencode({
158158
"com.cloudflare.api.account.zone.*" = "*"
159-
}
159+
})
160160
permission_groups = [{
161161
id = "e086da7e2179491d91ee5f35b3ca210a"
162162
}]
@@ -186,9 +186,9 @@ resource "cloudflare_api_token" "api_token_create" {
186186
expires_on = "2035-12-31T23:59:59Z"
187187

188188
policies = [{
189-
resources = {
189+
resources = jsonencode({
190190
"com.cloudflare.api.account.*" = "*"
191-
}
191+
})
192192
effect = "allow"
193193
permission_groups = [{
194194
id = "c8fed203ed3043cba015a93ad1616f1f"
@@ -200,4 +200,68 @@ resource "cloudflare_api_token" "api_token_create" {
200200
not_in = ["198.51.100.1/32"]
201201
}
202202
}
203-
}
203+
}
204+
205+
# Test Case 9: Token with complex multi-key resources map
206+
resource "cloudflare_api_token" "complex_resources_token" {
207+
name = "Complex Resources Token"
208+
209+
policies = [{
210+
effect = "allow"
211+
resources = jsonencode({
212+
"com.cloudflare.api.account.*" = "*"
213+
"com.cloudflare.api.account.zone.*" = "*"
214+
})
215+
permission_groups = [{
216+
id = "c8fed203ed3043cba015a93ad1616f1f"
217+
}]
218+
}]
219+
}
220+
221+
# Test Case 10: Token with multiple permission groups (v4 string format)
222+
resource "cloudflare_api_token" "multi_perms_token" {
223+
name = "Multiple Permissions Token"
224+
225+
policies = [{
226+
effect = "allow"
227+
resources = jsonencode({
228+
"com.cloudflare.api.account.*" = "*"
229+
})
230+
permission_groups = [{
231+
id = "c8fed203ed3043cba015a93ad1616f1f"
232+
}, {
233+
id = "82e64a83756745bbbb1c9c2701bf816b"
234+
}]
235+
}]
236+
}
237+
238+
# Test Case 11: Token without effect (should default to allow)
239+
resource "cloudflare_api_token" "no_effect_token" {
240+
name = "No Effect Token"
241+
242+
policies = [{
243+
resources = jsonencode({
244+
"com.cloudflare.api.account.*" = "*"
245+
})
246+
effect = "allow"
247+
permission_groups = [{
248+
id = "c8fed203ed3043cba015a93ad1616f1f"
249+
}]
250+
}]
251+
}
252+
253+
# Test Case 12: Token with variable references in resources
254+
resource "cloudflare_api_token" "variable_resources_token" {
255+
name = "Variable Resources Token"
256+
257+
policies = [{
258+
effect = "allow"
259+
resources = jsonencode({
260+
"com.cloudflare.api.account.${var.cloudflare_account_id}" = "*"
261+
"com.cloudflare.api.account.zone.${var.cloudflare_zone_id}" = "*"
262+
})
263+
permission_groups = [{
264+
id = "c8fed203ed3043cba015a93ad1616f1f"
265+
}]
266+
}]
267+
}

0 commit comments

Comments
 (0)