diff --git a/e2e/drift-exemptions/zero_trust_local_fallback_domain.yaml b/e2e/drift-exemptions/zero_trust_local_fallback_domain.yaml new file mode 100644 index 00000000..d8a2c278 --- /dev/null +++ b/e2e/drift-exemptions/zero_trust_local_fallback_domain.yaml @@ -0,0 +1,14 @@ +version: 1 + +exemptions: + - name: "precedence_recalculation" + description: "Precedence values may be auto-adjusted/recalculated by the Cloudflare API during migration" + resource_types: + - "cloudflare_zero_trust_device_custom_profile" + patterns: + - 'precedence.*->' + enabled: true + +settings: + apply_exemptions: true + verbose_exemptions: true diff --git a/integration/v4_to_v5/testdata/zero_trust_device_default_profile/expected/terraform.tfstate b/integration/v4_to_v5/testdata/zero_trust_device_default_profile/expected/terraform.tfstate deleted file mode 100644 index 73249021..00000000 --- a/integration/v4_to_v5/testdata/zero_trust_device_default_profile/expected/terraform.tfstate +++ /dev/null @@ -1,443 +0,0 @@ -{ - "resources": [ - { - "each": "map", - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "allow_mode_switch": true, - "auto_connect": 0, - "captive_portal": 180, - "id": "map-profile1-id" - }, - "index_key": "profile1", - "schema_version": 0 - }, - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "allow_mode_switch": false, - "auto_connect": 15, - "captive_portal": 300, - "id": "map-profile2-id" - }, - "index_key": "profile2", - "schema_version": 0 - }, - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "allow_mode_switch": true, - "auto_connect": 0, - "captive_portal": 0, - "id": "map-profile3-id" - }, - "index_key": "profile3", - "schema_version": 0 - } - ], - "mode": "managed", - "name": "map_example", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_default_profile" - }, - { - "each": "set", - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "allow_mode_switch": true, - "auto_connect": 0, - "captive_portal": 180, - "id": "set-alpha-id" - }, - "index_key": "alpha", - "schema_version": 0 - }, - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "allow_mode_switch": true, - "auto_connect": 0, - "captive_portal": 180, - "id": "set-beta-id" - }, - "index_key": "beta", - "schema_version": 0 - } - ], - "mode": "managed", - "name": "set_example", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_default_profile" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "allow_mode_switch": true, - "auto_connect": 0, - "captive_portal": 100, - "id": "counted-0-id" - }, - "index_key": 0, - "schema_version": 0 - }, - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "allow_mode_switch": false, - "auto_connect": 15, - "captive_portal": 200, - "id": "counted-1-id" - }, - "index_key": 1, - "schema_version": 0 - }, - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "allow_mode_switch": true, - "auto_connect": 30, - "captive_portal": 300, - "id": "counted-2-id" - }, - "index_key": 2, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "counted", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_default_profile" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "allow_mode_switch": true, - "auto_connect": 0, - "captive_portal": 300, - "id": "conditional-id" - }, - "index_key": 0, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "conditional", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_default_profile" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "allow_mode_switch": true, - "auto_connect": 0, - "captive_portal": 180, - "id": "lifecycle-id" - }, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "with_lifecycle", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_default_profile" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "allow_mode_switch": true, - "auto_connect": 30, - "captive_portal": 180, - "disable_auto_fallback": true, - "fallback_domain_enabled": false, - "id": "functions-id", - "support_url": "https://support.cftftest.cf-tf-test.com", - "switch_locked": true, - "tunnel_protocol": "wireguard-tls" - }, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "with_functions", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_default_profile" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "minimal-id" - }, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "minimal", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_default_profile" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "allow_mode_switch": true, - "allow_updates": true, - "allowed_to_leave": true, - "auto_connect": 30, - "captive_portal": 300, - "disable_auto_fallback": true, - "exclude_office_ips": true, - "fallback_domain_enabled": true, - "id": "maximal-id", - "service_mode_v2": { - "mode": "warp", - "port": 8080 - }, - "support_url": "https://support.cf-tf-test.com", - "switch_locked": false, - "tunnel_protocol": "wireguard-tls" - }, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "maximal", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_default_profile" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "mode-only-id", - "service_mode_v2": { - "mode": "proxy" - } - }, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "service_mode_mode_only", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_default_profile" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "port-only-id", - "service_mode_v2": { - "port": 443 - } - }, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "service_mode_port_only", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_default_profile" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "auto_connect": 0, - "captive_portal": 0, - "id": "zero-values-id" - }, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "zero_values", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_default_profile" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "allow_mode_switch": true, - "auto_connect": 15, - "captive_portal": 180, - "id": "variables-id" - }, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "with_variables", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_default_profile" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "allow_mode_switch": false, - "auto_connect": 0, - "captive_portal": 600, - "id": "old-name-id" - }, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "old_name", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_default_profile" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "auto_connect": 0, - "captive_portal": 180, - "id": "interpolation-id", - "support_url": "https://cftftest-support.cf-tf-test.com" - }, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "interpolation", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_default_profile" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f037e56e89293a057740de681ac9abbe/custom-employees-id", - "name": "Employee Profile", - "description": "Custom profile for employees", - "match": "identity.groups == \"employees\"", - "precedence": 100.0, - "policy_id": "custom-employees-id" - }, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "custom_employees", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_custom_profile" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f037e56e89293a057740de681ac9abbe/custom-contractors-id", - "name": "Contractor Profile", - "description": "Custom profile for contractors", - "match": "identity.groups == \"contractors\"", - "precedence": 200.0, - "policy_id": "custom-contractors-id", - "service_mode_v2": { - "mode": "proxy", - "port": 8080.0 - } - }, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "custom_contractors", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_custom_profile" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f037e56e89293a057740de681ac9abbe/custom-admins-id", - "name": "Admin Profile", - "description": "Custom profile for administrators", - "match": "identity.groups == \"admins\"", - "precedence": 50.0, - "policy_id": "custom-admins-id", - "allow_mode_switch": false, - "allow_updates": true, - "allowed_to_leave": true, - "auto_connect": 30.0, - "captive_portal": 300.0, - "disable_auto_fallback": true, - "exclude_office_ips": true, - "support_url": "https://admin-support.cf-tf-test.com", - "switch_locked": true, - "tunnel_protocol": "wireguard" - }, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "custom_admins", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_custom_profile" - }, - { - "each": "map", - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f037e56e89293a057740de681ac9abbe/custom-engineering-id", - "name": "Engineering Team Profile", - "description": "Custom profile for engineering team", - "match": "identity.groups == \"engineering\"", - "precedence": 150.0, - "policy_id": "custom-engineering-id", - "auto_connect": 15.0 - }, - "index_key": "engineering", - "schema_version": 0 - }, - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f037e56e89293a057740de681ac9abbe/custom-sales-id", - "name": "Sales Team Profile", - "description": "Custom profile for sales team", - "match": "identity.groups == \"sales\"", - "precedence": 160.0, - "policy_id": "custom-sales-id", - "auto_connect": 15.0 - }, - "index_key": "sales", - "schema_version": 0 - } - ], - "mode": "managed", - "name": "custom_teams", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_custom_profile" - } - ], - "terraform_version": "1.5.0", - "version": 4 -} \ No newline at end of file diff --git a/integration/v4_to_v5/testdata/zero_trust_device_default_profile/expected/zero_trust_device_default_profile_e2e.tf b/integration/v4_to_v5/testdata/zero_trust_device_default_profile/expected/zero_trust_device_default_profile_e2e.tf deleted file mode 100644 index bc88f87d..00000000 --- a/integration/v4_to_v5/testdata/zero_trust_device_default_profile/expected/zero_trust_device_default_profile_e2e.tf +++ /dev/null @@ -1,42 +0,0 @@ -# E2E test for zero_trust_device_default_profile migration -# This is a simplified version for actual API testing (singleton resource) - -variable "cloudflare_account_id" { - description = "Cloudflare account ID" - type = string -} - -variable "cloudflare_zone_id" { - description = "Cloudflare zone ID (not used by this account-scoped resource)" - type = string -} - -variable "cloudflare_domain" { - description = "Cloudflare domain (not used by this account-scoped resource)" - type = string -} - -# Single default profile test with all v4-valid fields populated -# Note: This is a singleton resource - only one default profile exists per account -resource "cloudflare_zero_trust_device_default_profile" "default" { - account_id = var.cloudflare_account_id - - # All v4-supported optional fields - allow_mode_switch = true - allow_updates = true - allowed_to_leave = false - auto_connect = 0 - captive_portal = 180 - switch_locked = false - disable_auto_fallback = false - exclude_office_ips = false - support_url = "https://support.example.com" - tunnel_protocol = "wireguard" - - service_mode_v2 = { - mode = "proxy" - port = 8080 - } - register_interface_ip_with_dns = true - sccm_vpn_boundary_support = false -} diff --git a/integration/v4_to_v5/testdata/zero_trust_device_default_profile/input/terraform.tfstate b/integration/v4_to_v5/testdata/zero_trust_device_default_profile/input/terraform.tfstate deleted file mode 100644 index 898b4e30..00000000 --- a/integration/v4_to_v5/testdata/zero_trust_device_default_profile/input/terraform.tfstate +++ /dev/null @@ -1,436 +0,0 @@ -{ - "resources": [ - { - "each": "map", - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "allow_mode_switch": true, - "auto_connect": 0, - "captive_portal": 180, - "id": "map-profile1-id" - }, - "index_key": "profile1", - "schema_version": 0 - }, - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "allow_mode_switch": false, - "auto_connect": 15, - "captive_portal": 300, - "id": "map-profile2-id" - }, - "index_key": "profile2", - "schema_version": 0 - }, - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "allow_mode_switch": true, - "auto_connect": 0, - "captive_portal": 0, - "id": "map-profile3-id" - }, - "index_key": "profile3", - "schema_version": 0 - } - ], - "mode": "managed", - "name": "map_example", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_profiles" - }, - { - "each": "set", - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "allow_mode_switch": true, - "auto_connect": 0, - "captive_portal": 180, - "id": "set-alpha-id" - }, - "index_key": "alpha", - "schema_version": 0 - }, - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "allow_mode_switch": true, - "auto_connect": 0, - "captive_portal": 180, - "id": "set-beta-id" - }, - "index_key": "beta", - "schema_version": 0 - } - ], - "mode": "managed", - "name": "set_example", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_profiles" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "allow_mode_switch": true, - "auto_connect": 0, - "captive_portal": 100, - "id": "counted-0-id" - }, - "index_key": 0, - "schema_version": 0 - }, - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "allow_mode_switch": false, - "auto_connect": 15, - "captive_portal": 200, - "id": "counted-1-id" - }, - "index_key": 1, - "schema_version": 0 - }, - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "allow_mode_switch": true, - "auto_connect": 30, - "captive_portal": 300, - "id": "counted-2-id" - }, - "index_key": 2, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "counted", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_profiles" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "allow_mode_switch": true, - "auto_connect": 0, - "captive_portal": 300, - "id": "conditional-id" - }, - "index_key": 0, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "conditional", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_profiles" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "allow_mode_switch": true, - "auto_connect": 0, - "captive_portal": 180, - "id": "lifecycle-id" - }, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "with_lifecycle", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_profiles" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "allow_mode_switch": true, - "auto_connect": 30, - "captive_portal": 180, - "disable_auto_fallback": true, - "fallback_domain_enabled": false, - "id": "functions-id", - "support_url": "https://support.cftftest.cf-tf-test.com", - "switch_locked": true, - "tunnel_protocol": "wireguard-tls" - }, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "with_functions", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_profiles" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "minimal-id" - }, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "minimal", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_profiles" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "allow_mode_switch": true, - "allow_updates": true, - "allowed_to_leave": true, - "auto_connect": 30, - "captive_portal": 300, - "disable_auto_fallback": true, - "exclude_office_ips": true, - "fallback_domain_enabled": true, - "id": "maximal-id", - "service_mode_v2": { - "mode": "warp", - "port": 8080 - }, - "support_url": "https://support.cf-tf-test.com", - "switch_locked": false, - "tunnel_protocol": "wireguard-tls" - }, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "maximal", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_profiles" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "mode-only-id", - "service_mode_v2": { - "mode": "proxy" - } - }, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "service_mode_mode_only", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_profiles" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "port-only-id", - "service_mode_v2": { - "port": 443 - } - }, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "service_mode_port_only", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_profiles" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "auto_connect": 0, - "captive_portal": 0, - "id": "zero-values-id" - }, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "zero_values", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_profiles" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "allow_mode_switch": true, - "auto_connect": 15, - "captive_portal": 180, - "id": "variables-id" - }, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "with_variables", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_profiles" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "allow_mode_switch": false, - "auto_connect": 0, - "captive_portal": 600, - "id": "old-name-id" - }, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "old_name", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_profiles" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "auto_connect": 0, - "captive_portal": 180, - "id": "interpolation-id", - "support_url": "https://cftftest-support.cf-tf-test.com" - }, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "interpolation", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_profiles" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f037e56e89293a057740de681ac9abbe/custom-employees-id", - "name": "Employee Profile", - "description": "Custom profile for employees", - "match": "identity.groups == \"employees\"", - "precedence": 100 - }, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "custom_employees", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_profiles" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f037e56e89293a057740de681ac9abbe/custom-contractors-id", - "name": "Contractor Profile", - "description": "Custom profile for contractors", - "match": "identity.groups == \"contractors\"", - "precedence": 200, - "service_mode_v2_mode": "proxy", - "service_mode_v2_port": 8080 - }, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "custom_contractors", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_profiles" - }, - { - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f037e56e89293a057740de681ac9abbe/custom-admins-id", - "name": "Admin Profile", - "description": "Custom profile for administrators", - "match": "identity.groups == \"admins\"", - "precedence": 50, - "allow_mode_switch": false, - "allow_updates": true, - "allowed_to_leave": true, - "auto_connect": 30, - "captive_portal": 300, - "disable_auto_fallback": true, - "exclude_office_ips": true, - "support_url": "https://admin-support.cf-tf-test.com", - "switch_locked": true, - "tunnel_protocol": "wireguard" - }, - "schema_version": 0 - } - ], - "mode": "managed", - "name": "custom_admins", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_profiles" - }, - { - "each": "map", - "instances": [ - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f037e56e89293a057740de681ac9abbe/custom-engineering-id", - "name": "Engineering Team Profile", - "description": "Custom profile for engineering team", - "match": "identity.groups == \"engineering\"", - "precedence": 150, - "auto_connect": 15 - }, - "index_key": "engineering", - "schema_version": 0 - }, - { - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f037e56e89293a057740de681ac9abbe/custom-sales-id", - "name": "Sales Team Profile", - "description": "Custom profile for sales team", - "match": "identity.groups == \"sales\"", - "precedence": 160, - "auto_connect": 15 - }, - "index_key": "sales", - "schema_version": 0 - } - ], - "mode": "managed", - "name": "custom_teams", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "type": "cloudflare_zero_trust_device_profiles" - } - ], - "terraform_version": "1.5.0", - "version": 4 -} \ No newline at end of file diff --git a/integration/v4_to_v5/testdata/zero_trust_device_default_profile/expected/zero_trust_device_default_profile.tf b/integration/v4_to_v5/testdata/zero_trust_device_profiles/expected/zero_trust_device_profiles.tf similarity index 72% rename from integration/v4_to_v5/testdata/zero_trust_device_default_profile/expected/zero_trust_device_default_profile.tf rename to integration/v4_to_v5/testdata/zero_trust_device_profiles/expected/zero_trust_device_profiles.tf index bc9e6a7c..2de91e38 100644 --- a/integration/v4_to_v5/testdata/zero_trust_device_default_profile/expected/zero_trust_device_default_profile.tf +++ b/integration/v4_to_v5/testdata/zero_trust_device_profiles/expected/zero_trust_device_profiles.tf @@ -1,10 +1,3 @@ -# Integration test for zero_trust_device_default_profile migration -# Covers v4 cloudflare_zero_trust_device_profiles (default=true) → v5 cloudflare_zero_trust_device_default_profile - -# ============================================================================ -# Pattern Group 1: Variables & Locals -# ============================================================================ - variable "cloudflare_account_id" { description = "Cloudflare account ID" type = string @@ -26,10 +19,24 @@ locals { test_tags = ["test", "migration", "device_profile"] } -# ============================================================================ -# Pattern Group 2: for_each with Maps (5 resources) -# Tests: map iteration, each.value, each.key -# ============================================================================ + + + + + + + + + + + + + + + + + + resource "cloudflare_zero_trust_device_default_profile" "map_example" { for_each = { @@ -69,10 +76,10 @@ resource "cloudflare_zero_trust_device_default_profile" "map_example" { sccm_vpn_boundary_support = false } -# ============================================================================ -# Pattern Group 3: for_each with Sets (4 resources) -# Tests: toset(), set iteration -# ============================================================================ +moved { + from = cloudflare_zero_trust_device_profiles.map_example + to = cloudflare_zero_trust_device_default_profile.map_example +} resource "cloudflare_zero_trust_device_default_profile" "set_example" { for_each = toset(["alpha", "beta", "gamma", "delta"]) @@ -86,10 +93,10 @@ resource "cloudflare_zero_trust_device_default_profile" "set_example" { sccm_vpn_boundary_support = false } -# ============================================================================ -# Pattern Group 4: count-based resources (4 resources) -# Tests: count, count.index -# ============================================================================ +moved { + from = cloudflare_zero_trust_device_profiles.set_example + to = cloudflare_zero_trust_device_default_profile.set_example +} resource "cloudflare_zero_trust_device_default_profile" "counted" { count = 4 @@ -103,10 +110,10 @@ resource "cloudflare_zero_trust_device_default_profile" "counted" { sccm_vpn_boundary_support = false } -# ============================================================================ -# Pattern Group 5: Conditional resource creation (1 resource or 0) -# Tests: conditional count, ternary -# ============================================================================ +moved { + from = cloudflare_zero_trust_device_profiles.counted + to = cloudflare_zero_trust_device_default_profile.counted +} resource "cloudflare_zero_trust_device_default_profile" "conditional" { count = local.name_prefix == "cftftest" ? 1 : 0 @@ -120,16 +127,10 @@ resource "cloudflare_zero_trust_device_default_profile" "conditional" { sccm_vpn_boundary_support = false } -# ============================================================================ -# Pattern Group 6: Cross-resource references (if applicable) -# Tests: resource references -# Note: Device profiles don't typically reference other resources -# ============================================================================ - -# ============================================================================ -# Pattern Group 7: Lifecycle meta-arguments (1 resource) -# Tests: lifecycle, create_before_destroy, ignore_changes -# ============================================================================ +moved { + from = cloudflare_zero_trust_device_profiles.conditional + to = cloudflare_zero_trust_device_default_profile.conditional +} resource "cloudflare_zero_trust_device_default_profile" "with_lifecycle" { account_id = var.cloudflare_account_id @@ -146,10 +147,10 @@ resource "cloudflare_zero_trust_device_default_profile" "with_lifecycle" { sccm_vpn_boundary_support = false } -# ============================================================================ -# Pattern Group 8: Terraform functions (various) -# Tests: base64encode, join, format, etc. -# ============================================================================ +moved { + from = cloudflare_zero_trust_device_profiles.with_lifecycle + to = cloudflare_zero_trust_device_default_profile.with_lifecycle +} resource "cloudflare_zero_trust_device_default_profile" "with_functions" { account_id = var.cloudflare_account_id @@ -166,9 +167,10 @@ resource "cloudflare_zero_trust_device_default_profile" "with_functions" { sccm_vpn_boundary_support = false } -# ============================================================================ -# Pattern Group 9: Edge Cases -# ============================================================================ +moved { + from = cloudflare_zero_trust_device_profiles.with_functions + to = cloudflare_zero_trust_device_default_profile.with_functions +} # Edge Case 1: Minimal config (only required fields + default) resource "cloudflare_zero_trust_device_default_profile" "minimal" { @@ -177,6 +179,11 @@ resource "cloudflare_zero_trust_device_default_profile" "minimal" { sccm_vpn_boundary_support = false } +moved { + from = cloudflare_zero_trust_device_profiles.minimal + to = cloudflare_zero_trust_device_default_profile.minimal +} + # Edge Case 2: Maximal config (all optional fields populated) resource "cloudflare_zero_trust_device_default_profile" "maximal" { account_id = var.cloudflare_account_id @@ -201,6 +208,11 @@ resource "cloudflare_zero_trust_device_default_profile" "maximal" { sccm_vpn_boundary_support = false } +moved { + from = cloudflare_zero_trust_device_profiles.maximal + to = cloudflare_zero_trust_device_default_profile.maximal +} + # Edge Case 3: service_mode_v2 with both mode and port resource "cloudflare_zero_trust_device_default_profile" "service_mode_both" { account_id = var.cloudflare_account_id @@ -213,6 +225,11 @@ resource "cloudflare_zero_trust_device_default_profile" "service_mode_both" { sccm_vpn_boundary_support = false } +moved { + from = cloudflare_zero_trust_device_profiles.service_mode_both + to = cloudflare_zero_trust_device_default_profile.service_mode_both +} + # Edge Case 5: Zero values for numeric fields resource "cloudflare_zero_trust_device_default_profile" "zero_values" { account_id = var.cloudflare_account_id @@ -223,6 +240,11 @@ resource "cloudflare_zero_trust_device_default_profile" "zero_values" { sccm_vpn_boundary_support = false } +moved { + from = cloudflare_zero_trust_device_profiles.zero_values + to = cloudflare_zero_trust_device_default_profile.zero_values +} + # Edge Case 6: Variable references throughout resource "cloudflare_zero_trust_device_default_profile" "with_variables" { account_id = var.cloudflare_account_id @@ -234,6 +256,11 @@ resource "cloudflare_zero_trust_device_default_profile" "with_variables" { sccm_vpn_boundary_support = false } +moved { + from = cloudflare_zero_trust_device_profiles.with_variables + to = cloudflare_zero_trust_device_default_profile.with_variables +} + # Edge Case 7: Old resource name (cloudflare_device_settings_policy) # This tests that both v4 resource names are handled resource "cloudflare_zero_trust_device_default_profile" "old_name" { @@ -246,6 +273,11 @@ resource "cloudflare_zero_trust_device_default_profile" "old_name" { sccm_vpn_boundary_support = false } +moved { + from = cloudflare_device_settings_policy.old_name + to = cloudflare_zero_trust_device_default_profile.old_name +} + # Edge Case 8: String interpolation resource "cloudflare_zero_trust_device_default_profile" "interpolation" { account_id = var.cloudflare_account_id @@ -257,10 +289,10 @@ resource "cloudflare_zero_trust_device_default_profile" "interpolation" { sccm_vpn_boundary_support = false } -# ============================================================================ -# Pattern Group 8: Custom Profiles (match + precedence) -# Tests: custom profile routing, precedence transformation, field preservation -# ============================================================================ +moved { + from = cloudflare_zero_trust_device_profiles.interpolation + to = cloudflare_zero_trust_device_default_profile.interpolation +} # Basic custom profile resource "cloudflare_zero_trust_device_custom_profile" "custom_employees" { @@ -271,6 +303,11 @@ resource "cloudflare_zero_trust_device_custom_profile" "custom_employees" { precedence = 1000 } +moved { + from = cloudflare_zero_trust_device_profiles.custom_employees + to = cloudflare_zero_trust_device_custom_profile.custom_employees +} + # Custom profile with service_mode_v2 resource "cloudflare_zero_trust_device_custom_profile" "custom_contractors" { account_id = var.cloudflare_account_id @@ -284,6 +321,11 @@ resource "cloudflare_zero_trust_device_custom_profile" "custom_contractors" { } } +moved { + from = cloudflare_zero_trust_device_profiles.custom_contractors + to = cloudflare_zero_trust_device_custom_profile.custom_contractors +} + # Custom profile with many optional fields resource "cloudflare_zero_trust_device_custom_profile" "custom_admins" { account_id = var.cloudflare_account_id @@ -303,6 +345,11 @@ resource "cloudflare_zero_trust_device_custom_profile" "custom_admins" { tunnel_protocol = "wireguard" } +moved { + from = cloudflare_zero_trust_device_profiles.custom_admins + to = cloudflare_zero_trust_device_custom_profile.custom_admins +} + # Multiple custom profiles with for_each resource "cloudflare_zero_trust_device_custom_profile" "custom_teams" { for_each = { @@ -324,13 +371,7 @@ resource "cloudflare_zero_trust_device_custom_profile" "custom_teams" { auto_connect = 15 } -# TOTAL RESOURCES: -# - for_each maps: 5 instances (default profiles) -# - for_each sets: 4 instances (default profiles) -# - count-based: 4 instances (default profiles) -# - conditional: 1 instance (default profile) -# - lifecycle: 1 instance (default profile) -# - functions: 1 instance (default profile) -# - edge cases: 7 instances (default profiles) -# - custom profiles: 5 instances (3 single + 2 for_each) -# TOTAL: 28 resource instances +moved { + from = cloudflare_zero_trust_device_profiles.custom_teams + to = cloudflare_zero_trust_device_custom_profile.custom_teams +} diff --git a/integration/v4_to_v5/testdata/zero_trust_device_default_profile/input/zero_trust_device_default_profile.tf b/integration/v4_to_v5/testdata/zero_trust_device_profiles/input/zero_trust_device_profiles.tf similarity index 71% rename from integration/v4_to_v5/testdata/zero_trust_device_default_profile/input/zero_trust_device_default_profile.tf rename to integration/v4_to_v5/testdata/zero_trust_device_profiles/input/zero_trust_device_profiles.tf index 5f098898..50ee3843 100644 --- a/integration/v4_to_v5/testdata/zero_trust_device_default_profile/input/zero_trust_device_default_profile.tf +++ b/integration/v4_to_v5/testdata/zero_trust_device_profiles/input/zero_trust_device_profiles.tf @@ -1,10 +1,3 @@ -# Integration test for zero_trust_device_default_profile migration -# Covers v4 cloudflare_zero_trust_device_profiles (default=true) → v5 cloudflare_zero_trust_device_default_profile - -# ============================================================================ -# Pattern Group 1: Variables & Locals -# ============================================================================ - variable "cloudflare_account_id" { description = "Cloudflare account ID" type = string @@ -26,11 +19,6 @@ locals { test_tags = ["test", "migration", "device_profile"] } -# ============================================================================ -# Pattern Group 2: for_each with Maps (5 resources) -# Tests: map iteration, each.value, each.key -# ============================================================================ - resource "cloudflare_zero_trust_device_profiles" "map_example" { for_each = { "profile1" = { @@ -70,11 +58,6 @@ resource "cloudflare_zero_trust_device_profiles" "map_example" { captive_portal = each.value.captive_portal } -# ============================================================================ -# Pattern Group 3: for_each with Sets (4 resources) -# Tests: toset(), set iteration -# ============================================================================ - resource "cloudflare_zero_trust_device_profiles" "set_example" { for_each = toset(["alpha", "beta", "gamma", "delta"]) @@ -88,11 +71,6 @@ resource "cloudflare_zero_trust_device_profiles" "set_example" { captive_portal = 180 } -# ============================================================================ -# Pattern Group 4: count-based resources (4 resources) -# Tests: count, count.index -# ============================================================================ - resource "cloudflare_zero_trust_device_profiles" "counted" { count = 4 @@ -106,11 +84,6 @@ resource "cloudflare_zero_trust_device_profiles" "counted" { captive_portal = (count.index + 1) * 100 } -# ============================================================================ -# Pattern Group 5: Conditional resource creation (1 resource or 0) -# Tests: conditional count, ternary -# ============================================================================ - resource "cloudflare_zero_trust_device_profiles" "conditional" { count = local.name_prefix == "cftftest" ? 1 : 0 @@ -124,17 +97,6 @@ resource "cloudflare_zero_trust_device_profiles" "conditional" { captive_portal = 300 } -# ============================================================================ -# Pattern Group 6: Cross-resource references (if applicable) -# Tests: resource references -# Note: Device profiles don't typically reference other resources -# ============================================================================ - -# ============================================================================ -# Pattern Group 7: Lifecycle meta-arguments (1 resource) -# Tests: lifecycle, create_before_destroy, ignore_changes -# ============================================================================ - resource "cloudflare_zero_trust_device_profiles" "with_lifecycle" { account_id = var.cloudflare_account_id name = "Default Profile Lifecycle" @@ -151,11 +113,6 @@ resource "cloudflare_zero_trust_device_profiles" "with_lifecycle" { } } -# ============================================================================ -# Pattern Group 8: Terraform functions (various) -# Tests: base64encode, join, format, etc. -# ============================================================================ - resource "cloudflare_zero_trust_device_profiles" "with_functions" { account_id = var.cloudflare_account_id name = "Default Profile Functions" @@ -172,10 +129,6 @@ resource "cloudflare_zero_trust_device_profiles" "with_functions" { disable_auto_fallback = true } -# ============================================================================ -# Pattern Group 9: Edge Cases -# ============================================================================ - # Edge Case 1: Minimal config (only required fields + default) resource "cloudflare_zero_trust_device_profiles" "minimal" { account_id = var.cloudflare_account_id @@ -267,11 +220,6 @@ resource "cloudflare_zero_trust_device_profiles" "interpolation" { captive_portal = 180 } -# ============================================================================ -# Pattern Group 8: Custom Profiles (match + precedence) -# Tests: custom profile routing, precedence transformation, field preservation -# ============================================================================ - # Basic custom profile resource "cloudflare_zero_trust_device_profiles" "custom_employees" { account_id = var.cloudflare_account_id @@ -324,21 +272,12 @@ resource "cloudflare_zero_trust_device_profiles" "custom_teams" { } } - account_id = var.cloudflare_account_id - name = "${title(each.key)} Team Profile" - description = "Custom profile for ${each.key} team" - match = each.value.match - precedence = each.value.precedence + account_id = var.cloudflare_account_id + name = "${title(each.key)} Team Profile" + description = "Custom profile for ${each.key} team" + match = each.value.match + precedence = each.value.precedence auto_connect = 15 } -# TOTAL RESOURCES: -# - for_each maps: 5 instances (default profiles) -# - for_each sets: 4 instances (default profiles) -# - count-based: 4 instances (default profiles) -# - conditional: 1 instance (default profile) -# - lifecycle: 1 instance (default profile) -# - functions: 1 instance (default profile) -# - edge cases: 7 instances (default profiles) -# - custom profiles: 5 instances (3 single + 2 for_each) -# TOTAL: 28 resource instances + diff --git a/integration/v4_to_v5/testdata/zero_trust_device_default_profile/input/zero_trust_device_default_profile_e2e.tf b/integration/v4_to_v5/testdata/zero_trust_device_profiles/input/zero_trust_device_profiles_e2e.tf similarity index 100% rename from integration/v4_to_v5/testdata/zero_trust_device_default_profile/input/zero_trust_device_default_profile_e2e.tf rename to integration/v4_to_v5/testdata/zero_trust_device_profiles/input/zero_trust_device_profiles_e2e.tf diff --git a/integration/v4_to_v5/testdata/zero_trust_local_fallback_domain/expected/terraform.tfstate b/integration/v4_to_v5/testdata/zero_trust_local_fallback_domain/expected/terraform.tfstate deleted file mode 100644 index c2edd682..00000000 --- a/integration/v4_to_v5/testdata/zero_trust_local_fallback_domain/expected/terraform.tfstate +++ /dev/null @@ -1,142 +0,0 @@ -{ - "version": 4, - "terraform_version": "1.5.0", - "serial": 1, - "lineage": "test-lineage", - "outputs": {}, - "resources": [ - { - "mode": "managed", - "type": "cloudflare_zero_trust_device_default_profile_local_domain_fallback", - "name": "default_single", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "test-account-id", - "domains": [ - { - "suffix": "example.com" - } - ] - } - } - ] - }, - { - "mode": "managed", - "type": "cloudflare_zero_trust_device_default_profile_local_domain_fallback", - "name": "default_multi", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "test-account-id", - "domains": [ - { - "suffix": "corp.example.com", - "description": "Corporate network", - "dns_server": ["10.0.0.1", "10.0.0.2"] - }, - { - "suffix": "internal.example.com", - "description": "Internal services", - "dns_server": ["10.1.0.1"] - }, - { - "suffix": "local.example.com" - } - ] - } - } - ] - }, - { - "mode": "managed", - "type": "cloudflare_zero_trust_device_custom_profile_local_domain_fallback", - "name": "custom_single", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "test-account-id", - "policy_id": "test-policy-id", - "domains": [ - { - "suffix": "custom.example.com" - } - ] - } - } - ] - }, - { - "mode": "managed", - "type": "cloudflare_zero_trust_device_custom_profile_local_domain_fallback", - "name": "custom_multi", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "test-account-id", - "policy_id": "another-policy-id", - "domains": [ - { - "suffix": "dev.example.com", - "description": "Development environment", - "dns_server": ["192.168.1.1"] - }, - { - "suffix": "staging.example.com" - } - ] - } - } - ] - }, - { - "mode": "managed", - "type": "cloudflare_zero_trust_device_default_profile_local_domain_fallback", - "name": "deprecated_default", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "test-account-id", - "domains": [ - { - "suffix": "deprecated.example.com", - "description": "Using deprecated resource name" - } - ] - } - } - ] - }, - { - "mode": "managed", - "type": "cloudflare_zero_trust_device_custom_profile_local_domain_fallback", - "name": "deprecated_custom", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "test-account-id", - "policy_id": "deprecated-policy", - "domains": [ - { - "suffix": "old.example.com" - } - ] - } - } - ] - } - ] -} diff --git a/integration/v4_to_v5/testdata/zero_trust_local_fallback_domain/expected/zero_trust_local_fallback_domain.tf b/integration/v4_to_v5/testdata/zero_trust_local_fallback_domain/expected/zero_trust_local_fallback_domain.tf index d2fa018c..9efef00d 100644 --- a/integration/v4_to_v5/testdata/zero_trust_local_fallback_domain/expected/zero_trust_local_fallback_domain.tf +++ b/integration/v4_to_v5/testdata/zero_trust_local_fallback_domain/expected/zero_trust_local_fallback_domain.tf @@ -16,6 +16,12 @@ variable "cloudflare_domain" { type = string } + + + + + + # Default profile - no policy_id resource "cloudflare_zero_trust_device_default_profile_local_domain_fallback" "default_single" { account_id = var.cloudflare_account_id @@ -27,6 +33,11 @@ resource "cloudflare_zero_trust_device_default_profile_local_domain_fallback" "d ] } +moved { + from = cloudflare_zero_trust_local_fallback_domain.default_single + to = cloudflare_zero_trust_device_default_profile_local_domain_fallback.default_single +} + # Default profile - multiple domains with all fields resource "cloudflare_zero_trust_device_default_profile_local_domain_fallback" "default_multi" { account_id = var.cloudflare_account_id @@ -48,6 +59,11 @@ resource "cloudflare_zero_trust_device_default_profile_local_domain_fallback" "d ] } +moved { + from = cloudflare_zero_trust_local_fallback_domain.default_multi + to = cloudflare_zero_trust_device_default_profile_local_domain_fallback.default_multi +} + # Custom profile - with policy_id resource "cloudflare_zero_trust_device_custom_profile_local_domain_fallback" "custom_single" { account_id = var.cloudflare_account_id @@ -60,6 +76,11 @@ resource "cloudflare_zero_trust_device_custom_profile_local_domain_fallback" "cu ] } +moved { + from = cloudflare_zero_trust_local_fallback_domain.custom_single + to = cloudflare_zero_trust_device_custom_profile_local_domain_fallback.custom_single +} + # Custom profile - multiple domains resource "cloudflare_zero_trust_device_custom_profile_local_domain_fallback" "custom_multi" { account_id = var.cloudflare_account_id @@ -77,6 +98,11 @@ resource "cloudflare_zero_trust_device_custom_profile_local_domain_fallback" "cu ] } +moved { + from = cloudflare_zero_trust_local_fallback_domain.custom_multi + to = cloudflare_zero_trust_device_custom_profile_local_domain_fallback.custom_multi +} + # Deprecated resource name - default profile resource "cloudflare_zero_trust_device_default_profile_local_domain_fallback" "deprecated_default" { account_id = var.cloudflare_account_id @@ -89,6 +115,11 @@ resource "cloudflare_zero_trust_device_default_profile_local_domain_fallback" "d ] } +moved { + from = cloudflare_fallback_domain.deprecated_default + to = cloudflare_zero_trust_device_default_profile_local_domain_fallback.deprecated_default +} + # Deprecated resource name - custom profile resource "cloudflare_zero_trust_device_custom_profile_local_domain_fallback" "deprecated_custom" { account_id = var.cloudflare_account_id @@ -100,3 +131,8 @@ resource "cloudflare_zero_trust_device_custom_profile_local_domain_fallback" "de } ] } + +moved { + from = cloudflare_fallback_domain.deprecated_custom + to = cloudflare_zero_trust_device_custom_profile_local_domain_fallback.deprecated_custom +} diff --git a/integration/v4_to_v5/testdata/zero_trust_local_fallback_domain/expected/zero_trust_local_fallback_domain_e2e.tf b/integration/v4_to_v5/testdata/zero_trust_local_fallback_domain/expected/zero_trust_local_fallback_domain_e2e.tf deleted file mode 100644 index b2aeee53..00000000 --- a/integration/v4_to_v5/testdata/zero_trust_local_fallback_domain/expected/zero_trust_local_fallback_domain_e2e.tf +++ /dev/null @@ -1,63 +0,0 @@ -# Test comprehensive migration of zero_trust_local_fallback_domain resources -# This includes both default profile (no policy_id) and custom profile (with policy_id) variants -# Custom profile migration now works because zero_trust_device_profiles with match/precedence -# migrates to zero_trust_device_custom_profile - -variable "cloudflare_account_id" { - description = "Cloudflare account ID" - type = string -} - -variable "cloudflare_zone_id" { - description = "Cloudflare zone ID" - type = string -} - -variable "cloudflare_domain" { - description = "Cloudflare domain for testing" - type = string -} - -# Default profile - multiple domains with all fields -resource "cloudflare_zero_trust_device_default_profile_local_domain_fallback" "default_multi" { - account_id = var.cloudflare_account_id - - domains = [ - { - suffix = "corp.${var.cloudflare_domain}" - description = "Corporate network" - dns_server = ["10.0.0.1", "10.0.0.2"] - }, - { - suffix = "internal.${var.cloudflare_domain}" - description = "Internal services" - dns_server = ["10.1.0.1"] - }, - { - suffix = "local.${var.cloudflare_domain}" - } - ] -} - -# Custom device profile for e2e testing -resource "cloudflare_zero_trust_device_custom_profile" "custom_e2e" { - account_id = var.cloudflare_account_id - name = "E2E Custom Profile" - description = "Custom profile for e2e testing" - match = "identity.email == \"e2e@example.com\"" - precedence = 1000 -} - -# Custom profile - fallback domain with policy_id -resource "cloudflare_zero_trust_device_custom_profile_local_domain_fallback" "custom_e2e" { - account_id = var.cloudflare_account_id - policy_id = cloudflare_zero_trust_device_custom_profile.custom_e2e.id - - domains = [ - { - suffix = "custom-e2e.${var.cloudflare_domain}" - description = "Custom profile e2e fallback" - dns_server = ["192.168.1.1"] - } - ] -} diff --git a/integration/v4_to_v5/testdata/zero_trust_local_fallback_domain/input/terraform.tfstate b/integration/v4_to_v5/testdata/zero_trust_local_fallback_domain/input/terraform.tfstate deleted file mode 100644 index 95ad4c42..00000000 --- a/integration/v4_to_v5/testdata/zero_trust_local_fallback_domain/input/terraform.tfstate +++ /dev/null @@ -1,142 +0,0 @@ -{ - "version": 4, - "terraform_version": "1.5.0", - "serial": 1, - "lineage": "test-lineage", - "outputs": {}, - "resources": [ - { - "mode": "managed", - "type": "cloudflare_zero_trust_local_fallback_domain", - "name": "default_single", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "test-account-id", - "domains": [ - { - "suffix": "example.com" - } - ] - } - } - ] - }, - { - "mode": "managed", - "type": "cloudflare_zero_trust_local_fallback_domain", - "name": "default_multi", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "test-account-id", - "domains": [ - { - "suffix": "corp.example.com", - "description": "Corporate network", - "dns_server": ["10.0.0.1", "10.0.0.2"] - }, - { - "suffix": "internal.example.com", - "description": "Internal services", - "dns_server": ["10.1.0.1"] - }, - { - "suffix": "local.example.com" - } - ] - } - } - ] - }, - { - "mode": "managed", - "type": "cloudflare_zero_trust_local_fallback_domain", - "name": "custom_single", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "test-account-id", - "policy_id": "test-policy-id", - "domains": [ - { - "suffix": "custom.example.com" - } - ] - } - } - ] - }, - { - "mode": "managed", - "type": "cloudflare_zero_trust_local_fallback_domain", - "name": "custom_multi", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "test-account-id", - "policy_id": "another-policy-id", - "domains": [ - { - "suffix": "dev.example.com", - "description": "Development environment", - "dns_server": ["192.168.1.1"] - }, - { - "suffix": "staging.example.com" - } - ] - } - } - ] - }, - { - "mode": "managed", - "type": "cloudflare_fallback_domain", - "name": "deprecated_default", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "test-account-id", - "domains": [ - { - "suffix": "deprecated.example.com", - "description": "Using deprecated resource name" - } - ] - } - } - ] - }, - { - "mode": "managed", - "type": "cloudflare_fallback_domain", - "name": "deprecated_custom", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "test-account-id", - "policy_id": "deprecated-policy", - "domains": [ - { - "suffix": "old.example.com" - } - ] - } - } - ] - } - ] -} diff --git a/integration/v4_to_v5/testdata/zero_trust_local_fallback_domain/input/zero_trust_local_fallback_domain_e2e.tf b/integration/v4_to_v5/testdata/zero_trust_local_fallback_domain/input/zero_trust_local_fallback_domain_e2e.tf index 1342adf2..ef8a7481 100644 --- a/integration/v4_to_v5/testdata/zero_trust_local_fallback_domain/input/zero_trust_local_fallback_domain_e2e.tf +++ b/integration/v4_to_v5/testdata/zero_trust_local_fallback_domain/input/zero_trust_local_fallback_domain_e2e.tf @@ -43,7 +43,7 @@ resource "cloudflare_zero_trust_device_profiles" "custom_e2e" { name = "E2E Custom Profile" description = "Custom profile for e2e testing" match = "identity.email == \"e2e@example.com\"" - precedence = 100 + precedence = 876 } # Custom profile - fallback domain with policy_id diff --git a/integration/v4_to_v5/testdata/zero_trust_split_tunnel/expected/split_tunnel.tf b/integration/v4_to_v5/testdata/zero_trust_split_tunnel/expected/split_tunnel.tf index c5f8af3a..667f1f21 100644 --- a/integration/v4_to_v5/testdata/zero_trust_split_tunnel/expected/split_tunnel.tf +++ b/integration/v4_to_v5/testdata/zero_trust_split_tunnel/expected/split_tunnel.tf @@ -12,58 +12,11 @@ locals { account_id = var.cloudflare_account_id } -# Default profile (will receive split tunnels without policy_id) -resource "cloudflare_zero_trust_device_default_profile" "default" { - account_id = local.account_id - include = [{ - address = "203.0.113.0/24" - description = "Corporate VPN" - }] - exclude = [{ - address = "192.168.0.0/16" - description = "Private network" - }, { - address = "10.0.0.0/8" - host = "internal.local" - }] - register_interface_ip_with_dns = true - sccm_vpn_boundary_support = false -} -# Custom profile with single tunnel -resource "cloudflare_zero_trust_device_custom_profile" "single_tunnel" { - account_id = local.account_id - name = "single_tunnel_profile" - match = "identity.groups == \"developers\"" - precedence = 1000 - exclude = [{ - address = "172.16.0.0/12" - description = "Dev environment" - }] -} -# Custom profile with multiple tunnels -resource "cloudflare_zero_trust_device_custom_profile" "multiple_tunnels" { - account_id = local.account_id - name = "multiple_tunnels_profile" - match = "identity.groups == \"admins\"" - precedence = 1100 - include = [{ - address = "10.100.0.0/16" - description = "Admin resources" - host = "prod.internal" - }] - exclude = [{ - address = "172.20.0.0/16" - description = "Admin network 1" - }, { - address = "172.21.0.0/16" - host = "admin.internal" - }] -} @@ -111,3 +64,68 @@ resource "cloudflare_zero_trust_device_custom_profile" "multiple_tunnels" { * } */ + +# Default profile (will receive split tunnels without policy_id) +resource "cloudflare_zero_trust_device_default_profile" "default" { + account_id = local.account_id + include = [{ + address = "203.0.113.0/24" + description = "Corporate VPN" + }] + exclude = [{ + address = "192.168.0.0/16" + description = "Private network" + }, { + address = "10.0.0.0/8" + host = "internal.local" + }] + register_interface_ip_with_dns = true + sccm_vpn_boundary_support = false +} + +moved { + from = cloudflare_zero_trust_device_profiles.default + to = cloudflare_zero_trust_device_default_profile.default +} + +# Custom profile with single tunnel +resource "cloudflare_zero_trust_device_custom_profile" "single_tunnel" { + account_id = local.account_id + name = "single_tunnel_profile" + match = "identity.groups == \"developers\"" + precedence = 1000 + exclude = [{ + address = "172.16.0.0/12" + description = "Dev environment" + }] +} + +moved { + from = cloudflare_zero_trust_device_profiles.single_tunnel + to = cloudflare_zero_trust_device_custom_profile.single_tunnel +} + +# Custom profile with multiple tunnels +resource "cloudflare_zero_trust_device_custom_profile" "multiple_tunnels" { + account_id = local.account_id + name = "multiple_tunnels_profile" + match = "identity.groups == \"admins\"" + precedence = 1100 + include = [{ + address = "10.100.0.0/16" + description = "Admin resources" + host = "prod.internal" + }] + exclude = [{ + address = "172.20.0.0/16" + description = "Admin network 1" + }, { + address = "172.21.0.0/16" + host = "admin.internal" + }] +} + +moved { + from = cloudflare_zero_trust_device_profiles.multiple_tunnels + to = cloudflare_zero_trust_device_custom_profile.multiple_tunnels +} diff --git a/integration/v4_to_v5/testdata/zero_trust_split_tunnel/expected/terraform.tfstate b/integration/v4_to_v5/testdata/zero_trust_split_tunnel/expected/terraform.tfstate index e5daebec..ccae7faf 100644 --- a/integration/v4_to_v5/testdata/zero_trust_split_tunnel/expected/terraform.tfstate +++ b/integration/v4_to_v5/testdata/zero_trust_split_tunnel/expected/terraform.tfstate @@ -7,6 +7,7 @@ { "attributes": { "account_id": "test-account-123", + "default": true, "exclude": [ { "address": "192.168.0.0/16", diff --git a/internal/registry/registry.go b/internal/registry/registry.go index a1ccbc53..86c8c6a8 100644 --- a/internal/registry/registry.go +++ b/internal/registry/registry.go @@ -59,11 +59,10 @@ import ( "github.com/cloudflare/tf-migrate/internal/resources/zero_trust_access_mtls_hostname_settings" "github.com/cloudflare/tf-migrate/internal/resources/zero_trust_access_policy" "github.com/cloudflare/tf-migrate/internal/resources/zero_trust_access_service_token" - "github.com/cloudflare/tf-migrate/internal/resources/zero_trust_device_default_profile" - "github.com/cloudflare/tf-migrate/internal/resources/zero_trust_device_posture_integration" "github.com/cloudflare/tf-migrate/internal/resources/zero_trust_device_managed_networks" + "github.com/cloudflare/tf-migrate/internal/resources/zero_trust_device_posture_integration" "github.com/cloudflare/tf-migrate/internal/resources/zero_trust_device_posture_rule" - "github.com/cloudflare/tf-migrate/internal/resources/zero_trust_split_tunnel" + "github.com/cloudflare/tf-migrate/internal/resources/zero_trust_device_profiles" "github.com/cloudflare/tf-migrate/internal/resources/zero_trust_dex_test" "github.com/cloudflare/tf-migrate/internal/resources/zero_trust_dlp_custom_profile" "github.com/cloudflare/tf-migrate/internal/resources/zero_trust_gateway_certificate" @@ -72,6 +71,7 @@ import ( "github.com/cloudflare/tf-migrate/internal/resources/zero_trust_list" "github.com/cloudflare/tf-migrate/internal/resources/zero_trust_local_fallback_domain" "github.com/cloudflare/tf-migrate/internal/resources/zero_trust_organization" + "github.com/cloudflare/tf-migrate/internal/resources/zero_trust_split_tunnel" "github.com/cloudflare/tf-migrate/internal/resources/zero_trust_tunnel_cloudflared" "github.com/cloudflare/tf-migrate/internal/resources/zero_trust_tunnel_cloudflared_config" "github.com/cloudflare/tf-migrate/internal/resources/zero_trust_tunnel_cloudflared_route" @@ -149,7 +149,7 @@ func RegisterAllMigrations() { zero_trust_access_mtls_hostname_settings.NewV4ToV5Migrator() zero_trust_access_policy.NewV4ToV5Migrator() zero_trust_access_service_token.NewV4ToV5Migrator() - zero_trust_device_default_profile.NewV4ToV5Migrator() + zero_trust_device_profiles.NewV4ToV5Migrator() zero_trust_device_managed_networks.NewV4ToV5Migrator() zero_trust_device_posture_integration.NewV4ToV5Migrator() zero_trust_device_posture_rule.NewV4ToV5Migrator() diff --git a/internal/resources/zero_trust_device_default_profile/v4_to_v5_test.go b/internal/resources/zero_trust_device_default_profile/v4_to_v5_test.go deleted file mode 100644 index 4bac75d1..00000000 --- a/internal/resources/zero_trust_device_default_profile/v4_to_v5_test.go +++ /dev/null @@ -1,1827 +0,0 @@ -package zero_trust_device_default_profile - -import ( - "testing" - - "github.com/cloudflare/tf-migrate/internal/testhelpers" -) - -func TestV4ToV5Transformation(t *testing.T) { - t.Run("ConfigTransformation", func(t *testing.T) { - migrator := NewV4ToV5Migrator() - tests := []testhelpers.ConfigTestCase{ - { - Name: "basic default profile with minimal fields", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Default Profile" - description = "Default device settings" - default = true -}`, - Expected: ` -resource "cloudflare_zero_trust_device_default_profile" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - register_interface_ip_with_dns = true - sccm_vpn_boundary_support = false -}`, - }, - { - Name: "default profile with service_mode_v2 fields", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Default Profile" - description = "Default device settings" - default = true - service_mode_v2_mode = "warp" - service_mode_v2_port = 8080 -}`, - Expected: ` -resource "cloudflare_zero_trust_device_default_profile" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - service_mode_v2 = { - mode = "warp" - port = 8080 - } - register_interface_ip_with_dns = true - sccm_vpn_boundary_support = false -}`, - }, - { - Name: "default profile with all settings", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Default Profile" - description = "Default device settings" - default = true - allow_mode_switch = false - allow_updates = true - allowed_to_leave = false - auto_connect = 300 - captive_portal = 180 - disable_auto_fallback = false - exclude_office_ips = true - support_url = "https://support.example.com" - switch_locked = false - tunnel_protocol = "wireguard" - service_mode_v2_mode = "warp" - service_mode_v2_port = 8080 -}`, - Expected: ` -resource "cloudflare_zero_trust_device_default_profile" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - allow_mode_switch = false - allow_updates = true - allowed_to_leave = false - auto_connect = 300 - captive_portal = 180 - disable_auto_fallback = false - exclude_office_ips = true - support_url = "https://support.example.com" - switch_locked = false - tunnel_protocol = "wireguard" - service_mode_v2 = { - mode = "warp" - port = 8080 - } - register_interface_ip_with_dns = true - sccm_vpn_boundary_support = false -}`, - }, - } - - testhelpers.RunConfigTransformTests(t, tests, migrator) - }) - - t.Run("ConfigTransformation_EdgeCases", func(t *testing.T) { - migrator := NewV4ToV5Migrator() - tests := []testhelpers.ConfigTestCase{ - { - Name: "old resource name - cloudflare_device_settings_policy", - Input: ` -resource "cloudflare_device_settings_policy" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Default" - description = "Default policy" - default = true -}`, - Expected: ` -resource "cloudflare_zero_trust_device_default_profile" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - register_interface_ip_with_dns = true - sccm_vpn_boundary_support = false -}`, - }, - { - Name: "only service_mode_v2_mode without port", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - default = true - service_mode_v2_mode = "warp" -}`, - Expected: ` -resource "cloudflare_zero_trust_device_default_profile" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - register_interface_ip_with_dns = true - sccm_vpn_boundary_support = false -}`, - }, - { - Name: "only service_mode_v2_port without mode", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - default = true - service_mode_v2_port = 8080 -}`, - Expected: ` -resource "cloudflare_zero_trust_device_default_profile" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - service_mode_v2 = { - port = 8080 - } - register_interface_ip_with_dns = true - sccm_vpn_boundary_support = false -}`, - }, - { - Name: "multiple resources in one file", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "first" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "First" - default = true -} - -resource "cloudflare_zero_trust_device_profiles" "second" { - account_id = "d138e56e89293a057740de681ac9abbf" - name = "Second" - description = "Second profile" - default = true -}`, - Expected: ` -resource "cloudflare_zero_trust_device_default_profile" "first" { - account_id = "f037e56e89293a057740de681ac9abbe" - register_interface_ip_with_dns = true - sccm_vpn_boundary_support = false -} - -resource "cloudflare_zero_trust_device_default_profile" "second" { - account_id = "d138e56e89293a057740de681ac9abbf" - register_interface_ip_with_dns = true - sccm_vpn_boundary_support = false -}`, - }, - { - Name: "resource with variables", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "example" { - account_id = var.cloudflare_account_id - name = "Default" - default = true -}`, - Expected: ` -resource "cloudflare_zero_trust_device_default_profile" "example" { - account_id = var.cloudflare_account_id - register_interface_ip_with_dns = true - sccm_vpn_boundary_support = false -}`, - }, - { - Name: "all removed fields present with various values", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - default = true - name = "Default Profile Name" - description = "This is a very long description that should be removed" - match = "identity.email == \"user@example.com\"" - precedence = 100 - enabled = false -}`, - Expected: ` -resource "cloudflare_zero_trust_device_default_profile" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - register_interface_ip_with_dns = true - sccm_vpn_boundary_support = false -}`, - }, - } - - testhelpers.RunConfigTransformTests(t, tests, migrator) - }) - - t.Run("ConfigTransformation_CustomProfile", func(t *testing.T) { - migrator := NewV4ToV5Migrator() - tests := []testhelpers.ConfigTestCase{ - { - Name: "custom profile with match and precedence (no default field)", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Custom Profile" - description = "Custom device settings" - match = "identity.email == \"user@example.com\"" - precedence = 100 - enabled = true -}`, - Expected: ` -resource "cloudflare_zero_trust_device_custom_profile" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Custom Profile" - description = "Custom device settings" - match = "identity.email == \"user@example.com\"" - precedence = 1000 -}`, - }, - { - Name: "custom profile with default=false", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Custom Profile" - description = "Custom device settings" - default = false - match = "identity.email == \"user@example.com\"" - precedence = 100 -}`, - Expected: ` -resource "cloudflare_zero_trust_device_custom_profile" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Custom Profile" - description = "Custom device settings" - match = "identity.email == \"user@example.com\"" - precedence = 1000 -}`, - }, - { - Name: "custom profile with service_mode_v2", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Custom Profile" - match = "identity.email == \"user@example.com\"" - precedence = 200 - service_mode_v2_mode = "proxy" - service_mode_v2_port = 8080 -}`, - Expected: ` -resource "cloudflare_zero_trust_device_custom_profile" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Custom Profile" - match = "identity.email == \"user@example.com\"" - precedence = 1100 - service_mode_v2 = { - mode = "proxy" - port = 8080 - } -}`, - }, - } - - testhelpers.RunConfigTransformTests(t, tests, migrator) - }) - - t.Run("ConfigTransformation_RoutingLogic", func(t *testing.T) { - migrator := NewV4ToV5Migrator() - tests := []testhelpers.ConfigTestCase{ - // Test all permutations of default, match, and precedence - // Priority: default field > presence of match+precedence - - // --- default=true cases (always routes to default profile) --- - { - Name: "default=true, no match, no precedence → default profile", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Profile" - default = true -}`, - Expected: ` -resource "cloudflare_zero_trust_device_default_profile" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - register_interface_ip_with_dns = true - sccm_vpn_boundary_support = false -}`, - }, - { - Name: "default=true, has match, no precedence → default profile (invalid config but default wins)", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Profile" - default = true - match = "identity.email == \"user@example.com\"" -}`, - Expected: ` -resource "cloudflare_zero_trust_device_default_profile" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - register_interface_ip_with_dns = true - sccm_vpn_boundary_support = false -}`, - }, - { - Name: "default=true, no match, has precedence → default profile (invalid config but default wins)", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Profile" - default = true - precedence = 100 -}`, - Expected: ` -resource "cloudflare_zero_trust_device_default_profile" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - register_interface_ip_with_dns = true - sccm_vpn_boundary_support = false -}`, - }, - { - Name: "default=true, has match, has precedence → default profile (default takes priority)", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Profile" - default = true - match = "identity.email == \"user@example.com\"" - precedence = 100 -}`, - Expected: ` -resource "cloudflare_zero_trust_device_default_profile" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - register_interface_ip_with_dns = true - sccm_vpn_boundary_support = false -}`, - }, - - // --- default=false cases --- - { - Name: "default=false, no match, no precedence → default profile", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Profile" - default = false -}`, - Expected: ` -resource "cloudflare_zero_trust_device_default_profile" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - register_interface_ip_with_dns = true - sccm_vpn_boundary_support = false -}`, - }, - { - Name: "default=false, has match, no precedence → default profile (missing precedence)", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Profile" - default = false - match = "identity.email == \"user@example.com\"" -}`, - Expected: ` -resource "cloudflare_zero_trust_device_default_profile" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - register_interface_ip_with_dns = true - sccm_vpn_boundary_support = false -}`, - }, - { - Name: "default=false, no match, has precedence → default profile (missing match)", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Profile" - default = false - precedence = 100 -}`, - Expected: ` -resource "cloudflare_zero_trust_device_default_profile" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - register_interface_ip_with_dns = true - sccm_vpn_boundary_support = false -}`, - }, - { - Name: "default=false, has match, has precedence → custom profile", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Custom Profile" - description = "Custom settings" - default = false - match = "identity.email == \"user@example.com\"" - precedence = 100 -}`, - Expected: ` -resource "cloudflare_zero_trust_device_custom_profile" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Custom Profile" - description = "Custom settings" - match = "identity.email == \"user@example.com\"" - precedence = 1000 -}`, - }, - - // --- no default field cases --- - { - Name: "no default, no match, no precedence → default profile", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Profile" -}`, - Expected: ` -resource "cloudflare_zero_trust_device_default_profile" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - register_interface_ip_with_dns = true - sccm_vpn_boundary_support = false -}`, - }, - { - Name: "no default, has match, no precedence → default profile (missing precedence)", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Profile" - match = "identity.email == \"user@example.com\"" -}`, - Expected: ` -resource "cloudflare_zero_trust_device_default_profile" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - register_interface_ip_with_dns = true - sccm_vpn_boundary_support = false -}`, - }, - { - Name: "no default, no match, has precedence → default profile (missing match)", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Profile" - precedence = 100 -}`, - Expected: ` -resource "cloudflare_zero_trust_device_default_profile" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - register_interface_ip_with_dns = true - sccm_vpn_boundary_support = false -}`, - }, - { - Name: "no default, has match, has precedence → custom profile", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Custom Profile" - description = "Custom settings" - match = "identity.email == \"user@example.com\"" - precedence = 150 -}`, - Expected: ` -resource "cloudflare_zero_trust_device_custom_profile" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Custom Profile" - description = "Custom settings" - match = "identity.email == \"user@example.com\"" - precedence = 1050 -}`, - }, - - // --- Edge cases with different precedence values --- - { - Name: "custom profile with precedence=1 → becomes 901", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Profile" - match = "identity.email == \"test@example.com\"" - precedence = 1 -}`, - Expected: ` -resource "cloudflare_zero_trust_device_custom_profile" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Profile" - match = "identity.email == \"test@example.com\"" - precedence = 901 -}`, - }, - { - Name: "custom profile with precedence=999 → becomes 1899", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Profile" - match = "identity.email == \"test@example.com\"" - precedence = 999 -}`, - Expected: ` -resource "cloudflare_zero_trust_device_custom_profile" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Profile" - match = "identity.email == \"test@example.com\"" - precedence = 1899 -}`, - }, - } - - testhelpers.RunConfigTransformTests(t, tests, migrator) - }) - - t.Run("StateTransformation", func(t *testing.T) { - migrator := NewV4ToV5Migrator() - tests := []testhelpers.StateTestCase{ - { - Name: "basic default profile state", - Input: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "mode": "managed", - "type": "cloudflare_zero_trust_device_profiles", - "name": "example", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415", - "name": "Default Profile", - "description": "Default device settings", - "default": true - } - } - ] - } - ] -}`, - Expected: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "type": "cloudflare_zero_trust_device_default_profile", - "name": "example", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415" - } - } - ] - } - ] -}`, - }, - { - Name: "state with service_mode_v2 transformation", - Input: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "mode": "managed", - "type": "cloudflare_zero_trust_device_profiles", - "name": "example", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415", - "default": true, - "service_mode_v2_mode": "warp", - "service_mode_v2_port": 8080 - } - } - ] - } - ] -}`, - Expected: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "type": "cloudflare_zero_trust_device_default_profile", - "name": "example", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415", - "service_mode_v2": { - "mode": "warp", - "port": 8080 - } - } - } - ] - } - ] -}`, - }, - { - Name: "state with numeric type conversions", - Input: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "mode": "managed", - "type": "cloudflare_zero_trust_device_profiles", - "name": "example", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415", - "default": true, - "auto_connect": 300, - "captive_portal": 180 - } - } - ] - } - ] -}`, - Expected: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "type": "cloudflare_zero_trust_device_default_profile", - "name": "example", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415", - "auto_connect": 300.0, - "captive_portal": 180.0 - } - } - ] - } - ] -}`, - }, - } - - testhelpers.RunStateTransformTests(t, tests, migrator) - }) - - t.Run("ConfigTransformation_SplitTunnelEmbedding", func(t *testing.T) { - migrator := NewV4ToV5Migrator() - tests := []testhelpers.ConfigTestCase{ - { - Name: "default profile with single exclude split tunnel", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Default Profile" - default = true -} - -resource "cloudflare_split_tunnel" "exclude_tunnel" { - account_id = "f037e56e89293a057740de681ac9abbe" - mode = "exclude" - tunnels { - address = "192.168.1.0/24" - description = "Local network" - } -}`, - Expected: ` -resource "cloudflare_zero_trust_device_default_profile" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - exclude = [{ - address = "192.168.1.0/24" - description = "Local network" - }] - register_interface_ip_with_dns = true - sccm_vpn_boundary_support = false -}`, - }, - { - Name: "default profile with single include split tunnel", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Default Profile" - default = true -} - -resource "cloudflare_split_tunnel" "include_tunnel" { - account_id = "f037e56e89293a057740de681ac9abbe" - mode = "include" - tunnels { - address = "10.0.0.0/8" - description = "Corporate network" - } -}`, - Expected: ` -resource "cloudflare_zero_trust_device_default_profile" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - include = [{ - address = "10.0.0.0/8" - description = "Corporate network" - }] - register_interface_ip_with_dns = true - sccm_vpn_boundary_support = false -}`, - }, - { - Name: "default profile with multiple exclude split tunnels", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Default Profile" - default = true -} - -resource "cloudflare_split_tunnel" "exclude_tunnel1" { - account_id = "f037e56e89293a057740de681ac9abbe" - mode = "exclude" - tunnels { - address = "192.168.1.0/24" - description = "Network 1" - } -} - -resource "cloudflare_split_tunnel" "exclude_tunnel2" { - account_id = "f037e56e89293a057740de681ac9abbe" - mode = "exclude" - tunnels { - address = "10.0.0.0/8" - description = "Network 2" - } -}`, - Expected: ` -resource "cloudflare_zero_trust_device_default_profile" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - exclude = [{ - address = "192.168.1.0/24" - description = "Network 1" - }, { - address = "10.0.0.0/8" - description = "Network 2" - }] - register_interface_ip_with_dns = true - sccm_vpn_boundary_support = false -}`, - }, - { - Name: "default profile with both exclude and include split tunnels", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Default Profile" - default = true -} - -resource "cloudflare_split_tunnel" "exclude_tunnel" { - account_id = "f037e56e89293a057740de681ac9abbe" - mode = "exclude" - tunnels { - address = "192.168.1.0/24" - description = "Exclude network" - } -} - -resource "cloudflare_split_tunnel" "include_tunnel" { - account_id = "f037e56e89293a057740de681ac9abbe" - mode = "include" - tunnels { - address = "10.0.0.0/8" - description = "Include network" - } -}`, - Expected: ` -resource "cloudflare_zero_trust_device_default_profile" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - exclude = [{ - address = "192.168.1.0/24" - description = "Exclude network" - }] - include = [{ - address = "10.0.0.0/8" - description = "Include network" - }] - register_interface_ip_with_dns = true - sccm_vpn_boundary_support = false -}`, - }, - { - Name: "default profile with split tunnels and other settings", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Default Profile" - default = true - allow_mode_switch = false - allow_updates = true - tunnel_protocol = "wireguard" - service_mode_v2_mode = "warp" - service_mode_v2_port = 8080 -} - -resource "cloudflare_split_tunnel" "exclude_tunnel" { - account_id = "f037e56e89293a057740de681ac9abbe" - mode = "exclude" - tunnels { - address = "192.168.0.0/16" - description = "Private network" - } -}`, - Expected: ` -resource "cloudflare_zero_trust_device_default_profile" "example" { - account_id = "f037e56e89293a057740de681ac9abbe" - allow_mode_switch = false - allow_updates = true - tunnel_protocol = "wireguard" - service_mode_v2 = { - mode = "warp" - port = 8080 - } - exclude = [{ - address = "192.168.0.0/16" - description = "Private network" - }] - register_interface_ip_with_dns = true - sccm_vpn_boundary_support = false -}`, - }, - { - Name: "v4 custom profile with split tunnel referencing it", - Input: ` -resource "cloudflare_zero_trust_device_profiles" "employees" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Employee Profile" - match = "identity.groups == \"employees\"" - precedence = 100 -} - -resource "cloudflare_split_tunnel" "employee_tunnel" { - account_id = "f037e56e89293a057740de681ac9abbe" - policy_id = cloudflare_zero_trust_device_profiles.employees.id - mode = "include" - tunnels { - address = "10.100.0.0/16" - description = "Employee resources" - } -}`, - Expected: ` -resource "cloudflare_zero_trust_device_custom_profile" "employees" { - account_id = "f037e56e89293a057740de681ac9abbe" - name = "Employee Profile" - match = "identity.groups == \"employees\"" - precedence = 1000 - include = [{ - address = "10.100.0.0/16" - description = "Employee resources" - }] -}`, - }, - } - - testhelpers.RunConfigTransformTests(t, tests, migrator) - }) - - t.Run("StateTransformation_CustomProfile", func(t *testing.T) { - migrator := NewV4ToV5Migrator() - tests := []testhelpers.StateTestCase{ - { - Name: "basic custom profile state", - Input: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "mode": "managed", - "type": "cloudflare_zero_trust_device_profiles", - "name": "custom", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f037e56e89293a057740de681ac9abbe/profile-abc-123", - "name": "Custom Profile", - "description": "Custom device settings", - "match": "identity.email == \"user@example.com\"", - "precedence": 100, - "enabled": true - } - } - ] - } - ] -}`, - Expected: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "type": "cloudflare_zero_trust_device_custom_profile", - "name": "custom", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f037e56e89293a057740de681ac9abbe/profile-abc-123", - "name": "Custom Profile", - "description": "Custom device settings", - "match": "identity.email == \"user@example.com\"", - "precedence": 100.0, - "policy_id": "profile-abc-123" - } - } - ] - } - ] -}`, - }, - { - Name: "custom profile with policy_id already present", - Input: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "mode": "managed", - "type": "cloudflare_zero_trust_device_profiles", - "name": "custom", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "test-policy-456", - "name": "Custom Profile", - "match": "identity.groups == \"developers\"", - "precedence": 200, - "policy_id": "test-policy-456" - } - } - ] - } - ] -}`, - Expected: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "type": "cloudflare_zero_trust_device_custom_profile", - "name": "custom", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "test-policy-456", - "name": "Custom Profile", - "match": "identity.groups == \"developers\"", - "precedence": 200.0, - "policy_id": "test-policy-456" - } - } - ] - } - ] -}`, - }, - { - Name: "custom profile with service_mode_v2 and all fields", - Input: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "mode": "managed", - "type": "cloudflare_zero_trust_device_profiles", - "name": "custom", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f037e56e89293a057740de681ac9abbe/policy-xyz", - "name": "Employee Profile", - "description": "For employees", - "default": false, - "match": "identity.groups == \"employees\"", - "precedence": 150, - "enabled": true, - "auto_connect": 30, - "captive_portal": 180, - "service_mode_v2_mode": "proxy", - "service_mode_v2_port": 8080, - "tunnel_protocol": "wireguard" - } - } - ] - } - ] -}`, - Expected: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "type": "cloudflare_zero_trust_device_custom_profile", - "name": "custom", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f037e56e89293a057740de681ac9abbe/policy-xyz", - "name": "Employee Profile", - "description": "For employees", - "match": "identity.groups == \"employees\"", - "precedence": 150.0, - "auto_connect": 30.0, - "captive_portal": 180.0, - "service_mode_v2": { - "mode": "proxy", - "port": 8080.0 - }, - "tunnel_protocol": "wireguard", - "policy_id": "policy-xyz" - } - } - ] - } - ] -}`, - }, - { - Name: "custom profile removes only default and enabled fields", - Input: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "mode": "managed", - "type": "cloudflare_zero_trust_device_profiles", - "name": "custom", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f037e56e89293a057740de681ac9abbe/custom-123", - "name": "Custom Profile", - "description": "Should be kept", - "default": false, - "match": "identity.email == \"test@example.com\"", - "precedence": 50, - "enabled": false - } - } - ] - } - ] -}`, - Expected: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "type": "cloudflare_zero_trust_device_custom_profile", - "name": "custom", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f037e56e89293a057740de681ac9abbe/custom-123", - "name": "Custom Profile", - "description": "Should be kept", - "match": "identity.email == \"test@example.com\"", - "precedence": 50.0, - "policy_id": "custom-123" - } - } - ] - } - ] -}`, - }, - } - - testhelpers.RunStateTransformTests(t, tests, migrator) - }) - - t.Run("StateTransformation_MultipleResources", func(t *testing.T) { - migrator := NewV4ToV5Migrator() - tests := []testhelpers.StateTestCase{ - { - Name: "multiple resources - mix of default and custom profiles", - Input: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "mode": "managed", - "type": "cloudflare_zero_trust_device_profiles", - "name": "default", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f037e56e89293a057740de681ac9abbe", - "name": "Default Profile", - "default": true, - "auto_connect": 15 - } - } - ] - }, - { - "mode": "managed", - "type": "cloudflare_zero_trust_device_profiles", - "name": "custom_one", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f037e56e89293a057740de681ac9abbe/policy-one", - "name": "Custom One", - "match": "identity.groups == \"group1\"", - "precedence": 100, - "captive_portal": 180 - } - } - ] - }, - { - "mode": "managed", - "type": "cloudflare_zero_trust_device_profiles", - "name": "custom_two", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f037e56e89293a057740de681ac9abbe/policy-two", - "name": "Custom Two", - "match": "identity.groups == \"group2\"", - "precedence": 200, - "allow_mode_switch": false - } - } - ] - } - ] -}`, - Expected: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "type": "cloudflare_zero_trust_device_default_profile", - "name": "default", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f037e56e89293a057740de681ac9abbe", - "auto_connect": 15.0 - } - } - ] - }, - { - "type": "cloudflare_zero_trust_device_custom_profile", - "name": "custom_one", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f037e56e89293a057740de681ac9abbe/policy-one", - "name": "Custom One", - "match": "identity.groups == \"group1\"", - "precedence": 100.0, - "captive_portal": 180.0, - "policy_id": "policy-one" - } - } - ] - }, - { - "type": "cloudflare_zero_trust_device_custom_profile", - "name": "custom_two", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f037e56e89293a057740de681ac9abbe/policy-two", - "name": "Custom Two", - "match": "identity.groups == \"group2\"", - "precedence": 200.0, - "allow_mode_switch": false, - "policy_id": "policy-two" - } - } - ] - } - ] -}`, - }, - { - Name: "multiple resources - all default profiles", - Input: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "mode": "managed", - "type": "cloudflare_zero_trust_device_profiles", - "name": "first", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "account-111", - "id": "profile-111", - "name": "First", - "default": true - } - } - ] - }, - { - "mode": "managed", - "type": "cloudflare_zero_trust_device_profiles", - "name": "second", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "account-222", - "id": "profile-222", - "name": "Second", - "default": true - } - } - ] - } - ] -}`, - Expected: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "type": "cloudflare_zero_trust_device_default_profile", - "name": "first", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "account-111", - "id": "profile-111" - } - } - ] - }, - { - "type": "cloudflare_zero_trust_device_default_profile", - "name": "second", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "account-222", - "id": "profile-222" - } - } - ] - } - ] -}`, - }, - { - Name: "multiple resources - all custom profiles", - Input: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "mode": "managed", - "type": "cloudflare_zero_trust_device_profiles", - "name": "employees", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f037e56e89293a057740de681ac9abbe/emp-policy", - "name": "Employees", - "match": "identity.groups == \"employees\"", - "precedence": 100 - } - } - ] - }, - { - "mode": "managed", - "type": "cloudflare_zero_trust_device_profiles", - "name": "contractors", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f037e56e89293a057740de681ac9abbe/con-policy", - "name": "Contractors", - "match": "identity.groups == \"contractors\"", - "precedence": 200 - } - } - ] - } - ] -}`, - Expected: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "type": "cloudflare_zero_trust_device_custom_profile", - "name": "employees", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f037e56e89293a057740de681ac9abbe/emp-policy", - "name": "Employees", - "match": "identity.groups == \"employees\"", - "precedence": 100.0, - "policy_id": "emp-policy" - } - } - ] - }, - { - "type": "cloudflare_zero_trust_device_custom_profile", - "name": "contractors", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f037e56e89293a057740de681ac9abbe/con-policy", - "name": "Contractors", - "match": "identity.groups == \"contractors\"", - "precedence": 200.0, - "policy_id": "con-policy" - } - } - ] - } - ] -}`, - }, - } - - testhelpers.RunStateTransformTests(t, tests, migrator) - }) - - t.Run("StateTransformation_EdgeCases", func(t *testing.T) { - migrator := NewV4ToV5Migrator() - tests := []testhelpers.StateTestCase{ - { - Name: "old resource name - cloudflare_device_settings_policy", - Input: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "mode": "managed", - "type": "cloudflare_device_settings_policy", - "name": "example", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415", - "default": true - } - } - ] - } - ] -}`, - Expected: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "type": "cloudflare_zero_trust_device_default_profile", - "name": "example", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415" - } - } - ] - } - ] -}`, - }, - { - Name: "only service_mode_v2_mode field", - Input: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "mode": "managed", - "type": "cloudflare_zero_trust_device_profiles", - "name": "example", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415", - "service_mode_v2_mode": "warp" - } - } - ] - } - ] -}`, - Expected: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "type": "cloudflare_zero_trust_device_default_profile", - "name": "example", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415" - } - } - ] - } - ] -}`, - }, - { - Name: "only service_mode_v2_port field", - Input: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "mode": "managed", - "type": "cloudflare_zero_trust_device_profiles", - "name": "example", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415", - "service_mode_v2_port": 9000 - } - } - ] - } - ] -}`, - Expected: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "type": "cloudflare_zero_trust_device_default_profile", - "name": "example", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415", - "service_mode_v2": { - "port": 9000 - } - } - } - ] - } - ] -}`, - }, - { - Name: "all fields with type conversions", - Input: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "mode": "managed", - "type": "cloudflare_zero_trust_device_profiles", - "name": "example", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415", - "default": true, - "name": "Default Profile", - "description": "Default settings", - "match": "identity.email == \"test@example.com\"", - "precedence": 10, - "enabled": true, - "auto_connect": 0, - "captive_portal": 300, - "allow_mode_switch": true, - "allow_updates": false, - "allowed_to_leave": true, - "disable_auto_fallback": false, - "exclude_office_ips": true, - "support_url": "https://support.example.com", - "switch_locked": false, - "tunnel_protocol": "wireguard", - "service_mode_v2_mode": "warp", - "service_mode_v2_port": 8080 - } - } - ] - } - ] -}`, - Expected: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "type": "cloudflare_zero_trust_device_default_profile", - "name": "example", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415", - "auto_connect": 0, - "captive_portal": 300, - "allow_mode_switch": true, - "allow_updates": false, - "allowed_to_leave": true, - "disable_auto_fallback": false, - "exclude_office_ips": true, - "support_url": "https://support.example.com", - "switch_locked": false, - "tunnel_protocol": "wireguard", - "service_mode_v2": { - "mode": "warp", - "port": 8080 - } - } - } - ] - } - ] -}`, - }, - { - Name: "minimal state - no optional fields", - Input: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "mode": "managed", - "type": "cloudflare_zero_trust_device_profiles", - "name": "example", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415", - "default": true - } - } - ] - } - ] -}`, - Expected: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "type": "cloudflare_zero_trust_device_default_profile", - "name": "example", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415" - } - } - ] - } - ] -}`, - }, - { - Name: "state with zero values for numeric fields", - Input: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "mode": "managed", - "type": "cloudflare_zero_trust_device_profiles", - "name": "example", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415", - "auto_connect": 0, - "captive_portal": 0 - } - } - ] - } - ] -}`, - Expected: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "type": "cloudflare_zero_trust_device_default_profile", - "name": "example", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415", - "auto_connect": 0, - "captive_portal": 0 - } - } - ] - } - ] -}`, - }, - { - Name: "empty instance attributes", - Input: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "mode": "managed", - "type": "cloudflare_zero_trust_device_profiles", - "name": "example", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": {} - } - ] - } - ] -}`, - Expected: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "type": "cloudflare_zero_trust_device_default_profile", - "name": "example", - "instances": [ - { - "schema_version": 0, - "attributes": {} - } - ] - } - ] -}`, - }, - { - Name: "state with fallback_domains and empty exclude array - both should be removed", - Input: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "mode": "managed", - "type": "cloudflare_zero_trust_device_profiles", - "name": "example", - "provider": "provider[\"registry.terraform.io/cloudflare/cloudflare\"]", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415", - "default": true, - "fallback_domains": [ - { - "suffix": "corp.example.com", - "description": "Corporate network", - "dns_server": ["10.0.0.1", "10.0.0.2"] - }, - { - "suffix": "internal.example.com", - "description": "Internal services", - "dns_server": ["10.1.0.1"] - } - ], - "exclude": [], - "tunnel_protocol": "wireguard" - } - } - ] - } - ] -}`, - Expected: `{ - "version": 4, - "terraform_version": "1.5.0", - "resources": [ - { - "type": "cloudflare_zero_trust_device_default_profile", - "name": "example", - "instances": [ - { - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "id": "f174e90a-fafe-4643-bbbc-4a0ed4fc8415", - "tunnel_protocol": "wireguard" - } - } - ] - } - ] -}`, - }, - } - - testhelpers.RunStateTransformTests(t, tests, migrator) - }) -} diff --git a/internal/resources/zero_trust_device_default_profile/README.md b/internal/resources/zero_trust_device_profiles/README.md similarity index 100% rename from internal/resources/zero_trust_device_default_profile/README.md rename to internal/resources/zero_trust_device_profiles/README.md diff --git a/internal/resources/zero_trust_device_default_profile/v4_to_v5.go b/internal/resources/zero_trust_device_profiles/v4_to_v5.go similarity index 60% rename from internal/resources/zero_trust_device_default_profile/v4_to_v5.go rename to internal/resources/zero_trust_device_profiles/v4_to_v5.go index 9c0f8349..226eedfe 100644 --- a/internal/resources/zero_trust_device_default_profile/v4_to_v5.go +++ b/internal/resources/zero_trust_device_profiles/v4_to_v5.go @@ -1,20 +1,16 @@ -package zero_trust_device_default_profile +package zero_trust_device_profiles import ( "fmt" - "os" - "path/filepath" "strings" "github.com/hashicorp/hcl/v2/hclwrite" "github.com/tidwall/gjson" - "github.com/tidwall/sjson" "github.com/cloudflare/tf-migrate/internal" "github.com/cloudflare/tf-migrate/internal/resources/zero_trust_split_tunnel" "github.com/cloudflare/tf-migrate/internal/transform" tfhcl "github.com/cloudflare/tf-migrate/internal/transform/hcl" - "github.com/cloudflare/tf-migrate/internal/transform/state" ) // V4ToV5Migrator handles migration of zero trust device profile resources from v4 to v5 @@ -26,27 +22,6 @@ type V4ToV5Migrator struct { newTypeCustom string } -// findStateFile searches upward from the given directory to find terraform.tfstate -// Returns empty string if not found -func findStateFile(startDir string) string { - dir := startDir - for { - stateFile := filepath.Join(dir, "terraform.tfstate") - if _, err := os.Stat(stateFile); err == nil { - return stateFile - } - - // Move to parent directory - parent := filepath.Dir(dir) - if parent == dir { - // Reached root directory - break - } - dir = parent - } - return "" -} - func NewV4ToV5Migrator() transform.ResourceTransformer { migrator := &V4ToV5Migrator{ oldType: "cloudflare_zero_trust_device_profiles", @@ -207,23 +182,49 @@ func (m *V4ToV5Migrator) TransformConfig(ctx *transform.Context, block *hclwrite tfhcl.EnsureAttribute(body, "sccm_vpn_boundary_support", false) } + // 9. Generate moved block for Terraform 1.8+ state migration + // The moved block triggers the provider's MoveState handler + resourceName := tfhcl.GetResourceName(block) + movedBlock := m.createMovedBlock(currentType, resourceName, newResourceType, resourceName) + return &transform.TransformResult{ - Blocks: []*hclwrite.Block{block}, - RemoveOriginal: false, + Blocks: []*hclwrite.Block{block, movedBlock}, + RemoveOriginal: true, }, nil } +// createMovedBlock creates a moved block for state migration tracking +// This triggers Terraform 1.8+ to call the provider's MoveState handler +func (m *V4ToV5Migrator) createMovedBlock(fromType, fromName, toType, toName string) *hclwrite.Block { + from := fmt.Sprintf("%s.%s", fromType, fromName) + to := fmt.Sprintf("%s.%s", toType, toName) + return tfhcl.CreateMovedBlock(from, to) +} + func (m *V4ToV5Migrator) TransformState(ctx *transform.Context, stateJSON gjson.Result, resourcePath, resourceName string) (string, error) { + // STATE TRANSFORMATION DISABLED - Provider handles state migration via MoveState + UpgradeState + // + // This migrator now uses Provider StateUpgraders (NEW pattern): + // - tf-migrate generates moved blocks (handled in TransformConfig) + // - Terraform 1.8+ triggers provider's MoveState when it sees moved blocks + // - Provider's MoveState + UpgradeState handle all state transformations: + // * Type conversions (Int64 → Float64) + // * Structure changes (flatten → nested) + // * ID extraction (custom profile) + // * Field removals/additions + // + // State is passed through UNCHANGED - provider will migrate it when user runs terraform apply. + result := stateJSON.String() attrs := stateJSON.Get("attributes") if !attrs.Exists() { - result, _ = sjson.Set(result, "schema_version", 0) + // No attributes - return as-is return result, nil } - // 1. Check if this is a custom profile (has match and precedence) or default profile - // Priority: default field > presence of match+precedence + // ROUTING LOGIC - Determine target resource type for moved blocks + // This logic is still needed to generate correct moved blocks in TransformConfig defaultAttr := attrs.Get("default") matchAttr := attrs.Get("match") precedenceAttr := attrs.Get("precedence") @@ -236,7 +237,7 @@ func (m *V4ToV5Migrator) TransformState(ctx *transform.Context, stateJSON gjson. isCustomProfile := !isExplicitDefault && matchAttr.Exists() && precedenceAttr.Exists() // Store the determined type in StateTypeRenames for the pipeline to apply - // This avoids state bleeding between resources (singleton migrator issue) + // This tells TransformConfig which resource type to use in moved blocks var newResourceType string if isCustomProfile { newResourceType = m.newTypeCustom @@ -257,136 +258,13 @@ func (m *V4ToV5Migrator) TransformState(ctx *transform.Context, stateJSON gjson. ctx.StateTypeRenames[stateTypeRenameKey1] = newResourceType ctx.StateTypeRenames[stateTypeRenameKey2] = newResourceType - // 2. Remove fields based on profile type - if isCustomProfile { - // Custom profile: remove only 'default' and 'enabled' fields - result = state.RemoveFields(result, "attributes", attrs, "default", "enabled") - } else { - // Default profile: remove custom-only fields - result = state.RemoveFields(result, "attributes", attrs, - "name", "description", "match", "precedence", "enabled", "default") - } - - // Re-parse attrs after removing fields to get updated structure - attrs = gjson.Parse(result).Get("attributes") - - // 3. Remove fallback_domains if present (both profile types) - // v4 allowed fallback_domains as nested attribute, v5 requires separate resource - // Remove from state to prevent drift and API errors when trying to update - // Users should migrate to cloudflare_zero_trust_device_default_profile_local_domain_fallback - // or cloudflare_zero_trust_device_custom_profile_local_domain_fallback resources - if attrs.Get("fallback_domains").Exists() { - result, _ = sjson.Delete(result, "attributes.fallback_domains") - } - - // 4. Remove empty exclude arrays (Optional+Computed field) - // If exclude is an empty array in v4 state, remove it to let v5 API provide defaults - if exclude := attrs.Get("exclude"); exclude.Exists() && exclude.IsArray() && len(exclude.Array()) == 0 { - result, _ = sjson.Delete(result, "attributes.exclude") - } - - // Re-parse attrs after removals to continue processing - attrs = gjson.Parse(result).Get("attributes") - - // 5. Handle tunnel_protocol - preserve in state - // v5 schema: Computed + Optional, but API returns "wireguard" as default - // Preserve tunnel_protocol in state to match what API returns - // This prevents drift when v5 provider refreshes from API - - // 6. Convert numeric types: Int → Float64 - if autoConnect := attrs.Get("auto_connect"); autoConnect.Exists() { - result, _ = sjson.Set(result, "attributes.auto_connect", state.ConvertToFloat64(autoConnect)) - } - - if captivePortal := attrs.Get("captive_portal"); captivePortal.Exists() { - result, _ = sjson.Set(result, "attributes.captive_portal", state.ConvertToFloat64(captivePortal)) - } - - // Custom profile: convert precedence Int → Float64 - if isCustomProfile { - if precedence := attrs.Get("precedence"); precedence.Exists() { - result, _ = sjson.Set(result, "attributes.precedence", state.ConvertToFloat64(precedence)) - } - - // Custom profile: set policy_id from the profile ID portion of the composite ID - // v4 ID format: account_id/profile_id - // v5 custom profile needs policy_id set to just the profile_id - if id := attrs.Get("id"); id.Exists() { - idStr := id.String() - // Split on "/" to get the profile_id - if slashIdx := strings.Index(idStr, "/"); slashIdx != -1 && slashIdx < len(idStr)-1 { - policyID := idStr[slashIdx+1:] - result, _ = sjson.Set(result, "attributes.policy_id", policyID) - } - } - } - - // 7. Create service_mode_v2 nested object from flat fields - result = m.createServiceModeV2(result, attrs) - - // 8. Always set schema_version - result, _ = sjson.Set(result, "schema_version", 0) - + // Return state UNCHANGED - provider will handle all transformations return result, nil } -// createServiceModeV2 creates the service_mode_v2 nested object from v4's flat fields -// Pattern from healthcheck migration (createTCPConfig) -// -// v4 structure: -// -// service_mode_v2_mode: "warp" -// service_mode_v2_port: 8080 -// -// v5 structure: -// -// service_mode_v2: { -// mode: "warp" -// port: 8080 -// } -func (m *V4ToV5Migrator) createServiceModeV2(stateJSON string, attrs gjson.Result) string { - mode := attrs.Get("service_mode_v2_mode") - port := attrs.Get("service_mode_v2_port") - - // Check if port has a meaningful value (not null/zero/empty) - hasPort := port.Exists() && port.Value() != nil && port.Int() != 0 - - // Handle v4 default: mode="warp" with no meaningful port value - // Don't create service_mode_v2 if it's just the v4 default - if mode.Exists() && mode.String() == "warp" && !hasPort { - // Remove the mode field - it's just the v4 default - stateJSON, _ = sjson.Delete(stateJSON, "attributes.service_mode_v2_mode") - // Also remove port if it exists but is null/zero - if port.Exists() { - stateJSON, _ = sjson.Delete(stateJSON, "attributes.service_mode_v2_port") - } - return stateJSON - } - - // Only create nested object if we have at least one non-default field - serviceMode := make(map[string]interface{}) - - // Collect fields for nested object - if mode.Exists() && mode.String() != "" { - serviceMode["mode"] = mode.String() - } - if hasPort { - // Convert Int → Float64 - serviceMode["port"] = state.ConvertToFloat64(port) - } - - // Only create nested object if we have at least one field - if len(serviceMode) > 0 { - stateJSON, _ = sjson.Set(stateJSON, "attributes.service_mode_v2", serviceMode) - - // Remove the old flat fields - if mode.Exists() { - stateJSON, _ = sjson.Delete(stateJSON, "attributes.service_mode_v2_mode") - } - if port.Exists() { - stateJSON, _ = sjson.Delete(stateJSON, "attributes.service_mode_v2_port") - } - } - - return stateJSON +// UsesProviderStateUpgrader indicates that this resource uses provider-based state migration +// When true, tf-migrate will not perform state transformation - the provider handles it via MoveState + UpgradeState +func (m *V4ToV5Migrator) UsesProviderStateUpgrader() bool { + return true } + diff --git a/internal/resources/zero_trust_device_profiles/v4_to_v5_test.go b/internal/resources/zero_trust_device_profiles/v4_to_v5_test.go new file mode 100644 index 00000000..1c60717b --- /dev/null +++ b/internal/resources/zero_trust_device_profiles/v4_to_v5_test.go @@ -0,0 +1,920 @@ +package zero_trust_device_profiles + +import ( + "testing" + + "github.com/cloudflare/tf-migrate/internal/testhelpers" +) + +func TestV4ToV5Transformation(t *testing.T) { + t.Run("ConfigTransformation", func(t *testing.T) { + migrator := NewV4ToV5Migrator() + tests := []testhelpers.ConfigTestCase{ + { + Name: "basic default profile with minimal fields", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Default Profile" + description = "Default device settings" + default = true +}`, + Expected: ` +resource "cloudflare_zero_trust_device_default_profile" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + register_interface_ip_with_dns = true + sccm_vpn_boundary_support = false +} + +moved { + from = cloudflare_zero_trust_device_profiles.example + to = cloudflare_zero_trust_device_default_profile.example +}`, + }, + { + Name: "default profile with service_mode_v2 fields", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Default Profile" + description = "Default device settings" + default = true + service_mode_v2_mode = "warp" + service_mode_v2_port = 8080 +}`, + Expected: ` +resource "cloudflare_zero_trust_device_default_profile" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + service_mode_v2 = { + mode = "warp" + port = 8080 + } + register_interface_ip_with_dns = true + sccm_vpn_boundary_support = false +} + +moved { + from = cloudflare_zero_trust_device_profiles.example + to = cloudflare_zero_trust_device_default_profile.example +}`, + }, + { + Name: "default profile with all settings", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Default Profile" + description = "Default device settings" + default = true + allow_mode_switch = false + allow_updates = true + allowed_to_leave = false + auto_connect = 300 + captive_portal = 180 + disable_auto_fallback = false + exclude_office_ips = true + support_url = "https://support.example.com" + switch_locked = false + tunnel_protocol = "wireguard" + service_mode_v2_mode = "warp" + service_mode_v2_port = 8080 +}`, + Expected: ` +resource "cloudflare_zero_trust_device_default_profile" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + allow_mode_switch = false + allow_updates = true + allowed_to_leave = false + auto_connect = 300 + captive_portal = 180 + disable_auto_fallback = false + exclude_office_ips = true + support_url = "https://support.example.com" + switch_locked = false + tunnel_protocol = "wireguard" + service_mode_v2 = { + mode = "warp" + port = 8080 + } + register_interface_ip_with_dns = true + sccm_vpn_boundary_support = false +} + +moved { + from = cloudflare_zero_trust_device_profiles.example + to = cloudflare_zero_trust_device_default_profile.example +}`, + }, + } + + testhelpers.RunConfigTransformTests(t, tests, migrator) + }) + + t.Run("ConfigTransformation_EdgeCases", func(t *testing.T) { + migrator := NewV4ToV5Migrator() + tests := []testhelpers.ConfigTestCase{ + { + Name: "old resource name - cloudflare_device_settings_policy", + Input: ` +resource "cloudflare_device_settings_policy" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Default" + description = "Default policy" + default = true +}`, + Expected: ` +resource "cloudflare_zero_trust_device_default_profile" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + register_interface_ip_with_dns = true + sccm_vpn_boundary_support = false +} + +moved { + from = cloudflare_device_settings_policy.example + to = cloudflare_zero_trust_device_default_profile.example +}`, + }, + { + Name: "only service_mode_v2_mode without port", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + default = true + service_mode_v2_mode = "warp" +}`, + Expected: ` +resource "cloudflare_zero_trust_device_default_profile" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + register_interface_ip_with_dns = true + sccm_vpn_boundary_support = false +} + +moved { + from = cloudflare_zero_trust_device_profiles.example + to = cloudflare_zero_trust_device_default_profile.example +}`, + }, + { + Name: "only service_mode_v2_port without mode", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + default = true + service_mode_v2_port = 8080 +}`, + Expected: ` +resource "cloudflare_zero_trust_device_default_profile" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + service_mode_v2 = { + port = 8080 + } + register_interface_ip_with_dns = true + sccm_vpn_boundary_support = false +} + +moved { + from = cloudflare_zero_trust_device_profiles.example + to = cloudflare_zero_trust_device_default_profile.example +}`, + }, + { + Name: "multiple resources in one file", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "first" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "First" + default = true +} + +resource "cloudflare_zero_trust_device_profiles" "second" { + account_id = "d138e56e89293a057740de681ac9abbf" + name = "Second" + description = "Second profile" + default = true +}`, + Expected: ` +resource "cloudflare_zero_trust_device_default_profile" "first" { + account_id = "f037e56e89293a057740de681ac9abbe" + register_interface_ip_with_dns = true + sccm_vpn_boundary_support = false +} + +moved { + from = cloudflare_zero_trust_device_profiles.first + to = cloudflare_zero_trust_device_default_profile.first +} + +resource "cloudflare_zero_trust_device_default_profile" "second" { + account_id = "d138e56e89293a057740de681ac9abbf" + register_interface_ip_with_dns = true + sccm_vpn_boundary_support = false +} + +moved { + from = cloudflare_zero_trust_device_profiles.second + to = cloudflare_zero_trust_device_default_profile.second +}`, + }, + { + Name: "resource with variables", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "example" { + account_id = var.cloudflare_account_id + name = "Default" + default = true +}`, + Expected: ` +resource "cloudflare_zero_trust_device_default_profile" "example" { + account_id = var.cloudflare_account_id + register_interface_ip_with_dns = true + sccm_vpn_boundary_support = false +} + +moved { + from = cloudflare_zero_trust_device_profiles.example + to = cloudflare_zero_trust_device_default_profile.example +}`, + }, + { + Name: "all removed fields present with various values", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + default = true + name = "Default Profile Name" + description = "This is a very long description that should be removed" + match = "identity.email == \"user@example.com\"" + precedence = 100 + enabled = false +}`, + Expected: ` +resource "cloudflare_zero_trust_device_default_profile" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + register_interface_ip_with_dns = true + sccm_vpn_boundary_support = false +} + +moved { + from = cloudflare_zero_trust_device_profiles.example + to = cloudflare_zero_trust_device_default_profile.example +}`, + }, + } + + testhelpers.RunConfigTransformTests(t, tests, migrator) + }) + + t.Run("ConfigTransformation_CustomProfile", func(t *testing.T) { + migrator := NewV4ToV5Migrator() + tests := []testhelpers.ConfigTestCase{ + { + Name: "custom profile with match and precedence (no default field)", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Custom Profile" + description = "Custom device settings" + match = "identity.email == \"user@example.com\"" + precedence = 100 + enabled = true +}`, + Expected: ` +resource "cloudflare_zero_trust_device_custom_profile" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Custom Profile" + description = "Custom device settings" + match = "identity.email == \"user@example.com\"" + precedence = 1000 +} + +moved { + from = cloudflare_zero_trust_device_profiles.example + to = cloudflare_zero_trust_device_custom_profile.example +}`, + }, + { + Name: "custom profile with default=false", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Custom Profile" + description = "Custom device settings" + default = false + match = "identity.email == \"user@example.com\"" + precedence = 100 +}`, + Expected: ` +resource "cloudflare_zero_trust_device_custom_profile" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Custom Profile" + description = "Custom device settings" + match = "identity.email == \"user@example.com\"" + precedence = 1000 +} + +moved { + from = cloudflare_zero_trust_device_profiles.example + to = cloudflare_zero_trust_device_custom_profile.example +}`, + }, + { + Name: "custom profile with service_mode_v2", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Custom Profile" + match = "identity.email == \"user@example.com\"" + precedence = 200 + service_mode_v2_mode = "proxy" + service_mode_v2_port = 8080 +}`, + Expected: ` +resource "cloudflare_zero_trust_device_custom_profile" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Custom Profile" + match = "identity.email == \"user@example.com\"" + precedence = 1100 + service_mode_v2 = { + mode = "proxy" + port = 8080 + } +} + +moved { + from = cloudflare_zero_trust_device_profiles.example + to = cloudflare_zero_trust_device_custom_profile.example +}`, + }, + } + + testhelpers.RunConfigTransformTests(t, tests, migrator) + }) + + t.Run("ConfigTransformation_RoutingLogic", func(t *testing.T) { + migrator := NewV4ToV5Migrator() + tests := []testhelpers.ConfigTestCase{ + // Test all permutations of default, match, and precedence + // Priority: default field > presence of match+precedence + + // --- default=true cases (always routes to default profile) --- + { + Name: "default=true, no match, no precedence → default profile", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Profile" + default = true +}`, + Expected: ` +resource "cloudflare_zero_trust_device_default_profile" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + register_interface_ip_with_dns = true + sccm_vpn_boundary_support = false +} + +moved { + from = cloudflare_zero_trust_device_profiles.example + to = cloudflare_zero_trust_device_default_profile.example +}`, + }, + { + Name: "default=true, has match, no precedence → default profile (invalid config but default wins)", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Profile" + default = true + match = "identity.email == \"user@example.com\"" +}`, + Expected: ` +resource "cloudflare_zero_trust_device_default_profile" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + register_interface_ip_with_dns = true + sccm_vpn_boundary_support = false +} + +moved { + from = cloudflare_zero_trust_device_profiles.example + to = cloudflare_zero_trust_device_default_profile.example +}`, + }, + { + Name: "default=true, no match, has precedence → default profile (invalid config but default wins)", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Profile" + default = true + precedence = 100 +}`, + Expected: ` +resource "cloudflare_zero_trust_device_default_profile" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + register_interface_ip_with_dns = true + sccm_vpn_boundary_support = false +} + +moved { + from = cloudflare_zero_trust_device_profiles.example + to = cloudflare_zero_trust_device_default_profile.example +}`, + }, + { + Name: "default=true, has match, has precedence → default profile (default takes priority)", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Profile" + default = true + match = "identity.email == \"user@example.com\"" + precedence = 100 +}`, + Expected: ` +resource "cloudflare_zero_trust_device_default_profile" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + register_interface_ip_with_dns = true + sccm_vpn_boundary_support = false +} + +moved { + from = cloudflare_zero_trust_device_profiles.example + to = cloudflare_zero_trust_device_default_profile.example +}`, + }, + + // --- default=false cases --- + { + Name: "default=false, no match, no precedence → default profile", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Profile" + default = false +}`, + Expected: ` +resource "cloudflare_zero_trust_device_default_profile" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + register_interface_ip_with_dns = true + sccm_vpn_boundary_support = false +} + +moved { + from = cloudflare_zero_trust_device_profiles.example + to = cloudflare_zero_trust_device_default_profile.example +}`, + }, + { + Name: "default=false, has match, no precedence → default profile (missing precedence)", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Profile" + default = false + match = "identity.email == \"user@example.com\"" +}`, + Expected: ` +resource "cloudflare_zero_trust_device_default_profile" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + register_interface_ip_with_dns = true + sccm_vpn_boundary_support = false +} + +moved { + from = cloudflare_zero_trust_device_profiles.example + to = cloudflare_zero_trust_device_default_profile.example +}`, + }, + { + Name: "default=false, no match, has precedence → default profile (missing match)", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Profile" + default = false + precedence = 100 +}`, + Expected: ` +resource "cloudflare_zero_trust_device_default_profile" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + register_interface_ip_with_dns = true + sccm_vpn_boundary_support = false +} + +moved { + from = cloudflare_zero_trust_device_profiles.example + to = cloudflare_zero_trust_device_default_profile.example +}`, + }, + { + Name: "default=false, has match, has precedence → custom profile", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Custom Profile" + description = "Custom settings" + default = false + match = "identity.email == \"user@example.com\"" + precedence = 100 +}`, + Expected: ` +resource "cloudflare_zero_trust_device_custom_profile" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Custom Profile" + description = "Custom settings" + match = "identity.email == \"user@example.com\"" + precedence = 1000 +} + +moved { + from = cloudflare_zero_trust_device_profiles.example + to = cloudflare_zero_trust_device_custom_profile.example +}`, + }, + + // --- no default field cases --- + { + Name: "no default, no match, no precedence → default profile", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Profile" +}`, + Expected: ` +resource "cloudflare_zero_trust_device_default_profile" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + register_interface_ip_with_dns = true + sccm_vpn_boundary_support = false +} + +moved { + from = cloudflare_zero_trust_device_profiles.example + to = cloudflare_zero_trust_device_default_profile.example +}`, + }, + { + Name: "no default, has match, no precedence → default profile (missing precedence)", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Profile" + match = "identity.email == \"user@example.com\"" +}`, + Expected: ` +resource "cloudflare_zero_trust_device_default_profile" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + register_interface_ip_with_dns = true + sccm_vpn_boundary_support = false +} + +moved { + from = cloudflare_zero_trust_device_profiles.example + to = cloudflare_zero_trust_device_default_profile.example +}`, + }, + { + Name: "no default, no match, has precedence → default profile (missing match)", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Profile" + precedence = 100 +}`, + Expected: ` +resource "cloudflare_zero_trust_device_default_profile" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + register_interface_ip_with_dns = true + sccm_vpn_boundary_support = false +} + +moved { + from = cloudflare_zero_trust_device_profiles.example + to = cloudflare_zero_trust_device_default_profile.example +}`, + }, + { + Name: "no default, has match, has precedence → custom profile", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Custom Profile" + description = "Custom settings" + match = "identity.email == \"user@example.com\"" + precedence = 150 +}`, + Expected: ` +resource "cloudflare_zero_trust_device_custom_profile" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Custom Profile" + description = "Custom settings" + match = "identity.email == \"user@example.com\"" + precedence = 1050 +} + +moved { + from = cloudflare_zero_trust_device_profiles.example + to = cloudflare_zero_trust_device_custom_profile.example +}`, + }, + + // --- Edge cases with different precedence values --- + { + Name: "custom profile with precedence=1 → becomes 901", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Profile" + match = "identity.email == \"test@example.com\"" + precedence = 1 +}`, + Expected: ` +resource "cloudflare_zero_trust_device_custom_profile" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Profile" + match = "identity.email == \"test@example.com\"" + precedence = 901 +} + +moved { + from = cloudflare_zero_trust_device_profiles.example + to = cloudflare_zero_trust_device_custom_profile.example +}`, + }, + { + Name: "custom profile with precedence=999 → becomes 1899", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Profile" + match = "identity.email == \"test@example.com\"" + precedence = 999 +}`, + Expected: ` +resource "cloudflare_zero_trust_device_custom_profile" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Profile" + match = "identity.email == \"test@example.com\"" + precedence = 1899 +} + +moved { + from = cloudflare_zero_trust_device_profiles.example + to = cloudflare_zero_trust_device_custom_profile.example +}`, + }, + } + + testhelpers.RunConfigTransformTests(t, tests, migrator) + }) + + + t.Run("StateTransformation_Removed", func(t *testing.T) { + t.Skip("State transformation tests removed - state migration is now handled by provider's StateUpgraders (MoveState + UpgradeState)") + }) + + t.Run("ConfigTransformation_SplitTunnelEmbedding", func(t *testing.T) { + migrator := NewV4ToV5Migrator() + tests := []testhelpers.ConfigTestCase{ + { + Name: "default profile with single exclude split tunnel", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Default Profile" + default = true +} + +resource "cloudflare_split_tunnel" "exclude_tunnel" { + account_id = "f037e56e89293a057740de681ac9abbe" + mode = "exclude" + tunnels { + address = "192.168.1.0/24" + description = "Local network" + } +}`, + Expected: ` +resource "cloudflare_zero_trust_device_default_profile" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + exclude = [{ + address = "192.168.1.0/24" + description = "Local network" + }] + register_interface_ip_with_dns = true + sccm_vpn_boundary_support = false +} + +moved { + from = cloudflare_zero_trust_device_profiles.example + to = cloudflare_zero_trust_device_default_profile.example +}`, + }, + { + Name: "default profile with single include split tunnel", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Default Profile" + default = true +} + +resource "cloudflare_split_tunnel" "include_tunnel" { + account_id = "f037e56e89293a057740de681ac9abbe" + mode = "include" + tunnels { + address = "10.0.0.0/8" + description = "Corporate network" + } +}`, + Expected: ` +resource "cloudflare_zero_trust_device_default_profile" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + include = [{ + address = "10.0.0.0/8" + description = "Corporate network" + }] + register_interface_ip_with_dns = true + sccm_vpn_boundary_support = false +} + +moved { + from = cloudflare_zero_trust_device_profiles.example + to = cloudflare_zero_trust_device_default_profile.example +}`, + }, + { + Name: "default profile with multiple exclude split tunnels", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Default Profile" + default = true +} + +resource "cloudflare_split_tunnel" "exclude_tunnel1" { + account_id = "f037e56e89293a057740de681ac9abbe" + mode = "exclude" + tunnels { + address = "192.168.1.0/24" + description = "Network 1" + } +} + +resource "cloudflare_split_tunnel" "exclude_tunnel2" { + account_id = "f037e56e89293a057740de681ac9abbe" + mode = "exclude" + tunnels { + address = "10.0.0.0/8" + description = "Network 2" + } +}`, + Expected: ` +resource "cloudflare_zero_trust_device_default_profile" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + exclude = [{ + address = "192.168.1.0/24" + description = "Network 1" + }, { + address = "10.0.0.0/8" + description = "Network 2" + }] + register_interface_ip_with_dns = true + sccm_vpn_boundary_support = false +} + +moved { + from = cloudflare_zero_trust_device_profiles.example + to = cloudflare_zero_trust_device_default_profile.example +}`, + }, + { + Name: "default profile with both exclude and include split tunnels", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Default Profile" + default = true +} + +resource "cloudflare_split_tunnel" "exclude_tunnel" { + account_id = "f037e56e89293a057740de681ac9abbe" + mode = "exclude" + tunnels { + address = "192.168.1.0/24" + description = "Exclude network" + } +} + +resource "cloudflare_split_tunnel" "include_tunnel" { + account_id = "f037e56e89293a057740de681ac9abbe" + mode = "include" + tunnels { + address = "10.0.0.0/8" + description = "Include network" + } +}`, + Expected: ` +resource "cloudflare_zero_trust_device_default_profile" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + exclude = [{ + address = "192.168.1.0/24" + description = "Exclude network" + }] + include = [{ + address = "10.0.0.0/8" + description = "Include network" + }] + register_interface_ip_with_dns = true + sccm_vpn_boundary_support = false +} + +moved { + from = cloudflare_zero_trust_device_profiles.example + to = cloudflare_zero_trust_device_default_profile.example +}`, + }, + { + Name: "default profile with split tunnels and other settings", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Default Profile" + default = true + allow_mode_switch = false + allow_updates = true + tunnel_protocol = "wireguard" + service_mode_v2_mode = "warp" + service_mode_v2_port = 8080 +} + +resource "cloudflare_split_tunnel" "exclude_tunnel" { + account_id = "f037e56e89293a057740de681ac9abbe" + mode = "exclude" + tunnels { + address = "192.168.0.0/16" + description = "Private network" + } +}`, + Expected: ` +resource "cloudflare_zero_trust_device_default_profile" "example" { + account_id = "f037e56e89293a057740de681ac9abbe" + allow_mode_switch = false + allow_updates = true + tunnel_protocol = "wireguard" + service_mode_v2 = { + mode = "warp" + port = 8080 + } + exclude = [{ + address = "192.168.0.0/16" + description = "Private network" + }] + register_interface_ip_with_dns = true + sccm_vpn_boundary_support = false +} + +moved { + from = cloudflare_zero_trust_device_profiles.example + to = cloudflare_zero_trust_device_default_profile.example +}`, + }, + { + Name: "v4 custom profile with split tunnel referencing it", + Input: ` +resource "cloudflare_zero_trust_device_profiles" "employees" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Employee Profile" + match = "identity.groups == \"employees\"" + precedence = 100 +} + +resource "cloudflare_split_tunnel" "employee_tunnel" { + account_id = "f037e56e89293a057740de681ac9abbe" + policy_id = cloudflare_zero_trust_device_profiles.employees.id + mode = "include" + tunnels { + address = "10.100.0.0/16" + description = "Employee resources" + } +}`, + Expected: ` +resource "cloudflare_zero_trust_device_custom_profile" "employees" { + account_id = "f037e56e89293a057740de681ac9abbe" + name = "Employee Profile" + match = "identity.groups == \"employees\"" + precedence = 1000 + include = [{ + address = "10.100.0.0/16" + description = "Employee resources" + }] +} + +moved { + from = cloudflare_zero_trust_device_profiles.employees + to = cloudflare_zero_trust_device_custom_profile.employees +}`, + }, + } + + testhelpers.RunConfigTransformTests(t, tests, migrator) + }) + +} diff --git a/internal/resources/zero_trust_local_fallback_domain/v4_to_v5.go b/internal/resources/zero_trust_local_fallback_domain/v4_to_v5.go index 3fd59cb6..d00c0ae8 100644 --- a/internal/resources/zero_trust_local_fallback_domain/v4_to_v5.go +++ b/internal/resources/zero_trust_local_fallback_domain/v4_to_v5.go @@ -6,7 +6,6 @@ import ( "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclwrite" "github.com/tidwall/gjson" - "github.com/tidwall/sjson" "github.com/cloudflare/tf-migrate/internal" "github.com/cloudflare/tf-migrate/internal/transform" @@ -142,6 +141,7 @@ func (m *V4ToV5Migrator) GetResourceRename() (string, string) { } func (m *V4ToV5Migrator) TransformConfig(ctx *transform.Context, block *hclwrite.Block) (*transform.TransformResult, error) { + resourceName := tfhcl.GetResourceName(block) body := block.Body() // Check if policy_id exists and is not null @@ -166,6 +166,7 @@ func (m *V4ToV5Migrator) TransformConfig(ctx *transform.Context, block *hclwrite // Rename resource type to appropriate v5 resource currentType := tfhcl.GetResourceType(block) + oldType := currentType // Store original type for moved block tfhcl.RenameResourceType(block, currentType, newResourceType) // Remove policy_id attribute if it's null (default profile doesn't accept policy_id) @@ -176,77 +177,25 @@ func (m *V4ToV5Migrator) TransformConfig(ctx *transform.Context, block *hclwrite // Convert domains blocks to attribute array tfhcl.ConvertBlocksToAttributeList(body, "domains", nil) + // Generate moved block + from := oldType + "." + resourceName + to := newResourceType + "." + resourceName + movedBlock := tfhcl.CreateMovedBlock(from, to) + return &transform.TransformResult{ - Blocks: []*hclwrite.Block{block}, - RemoveOriginal: false, + Blocks: []*hclwrite.Block{block, movedBlock}, + RemoveOriginal: true, }, nil } -func (m *V4ToV5Migrator) TransformState(ctx *transform.Context, stateJSON gjson.Result, resourcePath, resourceName string) (string, error) { - result := stateJSON.String() - - attrs := stateJSON.Get("attributes") - if !attrs.Exists() { - result, _ = sjson.Set(result, "schema_version", 0) - m.lastTransformedType = m.newTypeDefault - return result, nil - } - - // Check if policy_id exists and is not null - policyID := attrs.Get("policy_id") - hasPolicyID := policyID.Exists() && policyID.Type != gjson.Null - if hasPolicyID { - m.lastTransformedType = m.newTypeCustom - } else { - m.lastTransformedType = m.newTypeDefault - } - - transform.SetStateTypeRename(ctx, resourceName, m.oldType, m.lastTransformedType) - transform.SetStateTypeRename(ctx, resourceName, m.oldTypeDeprecated, m.lastTransformedType) - - // Remove policy_id from state if it's null (default profile doesn't have policy_id) - if !hasPolicyID && policyID.Exists() { - result, _ = sjson.Delete(result, "attributes.policy_id") - } - - // Transform ID format for custom profile - // v4 ID format: account_id/policy_id - // v5 ID format: policy_id (just the policy_id portion) - if hasPolicyID { - if id := attrs.Get("id"); id.Exists() { - idStr := id.String() - // Split on "/" to get the policy_id portion - if slashIdx := strings.Index(idStr, "/"); slashIdx != -1 && slashIdx < len(idStr)-1 { - newID := idStr[slashIdx+1:] - result, _ = sjson.Set(result, "attributes.id", newID) - } - } - } - - // Transform domains (TypeSet → ListNestedAttribute/SetNestedAttribute) - // The state structure is the same for both v5 variants, just different collection semantics - // v4: domains is a set of objects - // v5: domains is either a list (default profile) or set (custom profile) of objects - // The JSON structure is identical, so no transformation needed - domains := attrs.Get("domains") - if domains.Exists() && domains.IsArray() { - if len(domains.Array()) == 0 { - result, _ = sjson.Delete(result, "attributes.domains") - } - } - - // Handle dns_server empty arrays within each domain - if domains.Exists() && domains.IsArray() { - domains.ForEach(func(key, value gjson.Result) bool { - dnsServer := value.Get("dns_server") - if dnsServer.Exists() && dnsServer.IsArray() && len(dnsServer.Array()) == 0 { - result, _ = sjson.Delete(result, "attributes.domains."+key.String()+".dns_server") - } - return true - }) - } - - result, _ = sjson.Set(result, "schema_version", 0) +// UsesProviderStateUpgrader indicates that this resource uses provider-based state migration +func (m *V4ToV5Migrator) UsesProviderStateUpgrader() bool { + return true +} - return result, nil +func (m *V4ToV5Migrator) TransformState(ctx *transform.Context, stateJSON gjson.Result, resourcePath, resourceName string) (string, error) { + // State transformation is handled by the provider's StateUpgraders (MoveState/UpgradeState) + // The moved block generated in TransformConfig triggers the provider's migration logic + // This function is a no-op for zero_trust_local_fallback_domain migration + return stateJSON.String(), nil } diff --git a/internal/resources/zero_trust_local_fallback_domain/v4_to_v5_test.go b/internal/resources/zero_trust_local_fallback_domain/v4_to_v5_test.go index 26f567ca..af45fd6b 100644 --- a/internal/resources/zero_trust_local_fallback_domain/v4_to_v5_test.go +++ b/internal/resources/zero_trust_local_fallback_domain/v4_to_v5_test.go @@ -29,6 +29,11 @@ resource "cloudflare_zero_trust_device_default_profile_local_domain_fallback" "e suffix = "example.com" } ] +} + +moved { + from = cloudflare_zero_trust_local_fallback_domain.example + to = cloudflare_zero_trust_device_default_profile_local_domain_fallback.example }`, }, { @@ -54,6 +59,11 @@ resource "cloudflare_zero_trust_device_default_profile_local_domain_fallback" "e dns_server = ["1.1.1.1", "8.8.8.8"] } ] +} + +moved { + from = cloudflare_zero_trust_local_fallback_domain.example + to = cloudflare_zero_trust_device_default_profile_local_domain_fallback.example }`, }, { @@ -93,6 +103,11 @@ resource "cloudflare_zero_trust_device_default_profile_local_domain_fallback" "e suffix = "third.com" } ] +} + +moved { + from = cloudflare_zero_trust_local_fallback_domain.example + to = cloudflare_zero_trust_device_default_profile_local_domain_fallback.example }`, }, { @@ -114,6 +129,11 @@ resource "cloudflare_zero_trust_device_default_profile_local_domain_fallback" "e suffix = "example.com" } ] +} + +moved { + from = cloudflare_fallback_domain.example + to = cloudflare_zero_trust_device_default_profile_local_domain_fallback.example }`, }, { @@ -136,6 +156,11 @@ resource "cloudflare_zero_trust_device_default_profile_local_domain_fallback" "e suffix = "example.com" } ] +} + +moved { + from = cloudflare_zero_trust_local_fallback_domain.example + to = cloudflare_zero_trust_device_default_profile_local_domain_fallback.example }`, }, { @@ -159,6 +184,11 @@ resource "cloudflare_zero_trust_device_custom_profile_local_domain_fallback" "ex suffix = "example.com" } ] +} + +moved { + from = cloudflare_zero_trust_local_fallback_domain.example + to = cloudflare_zero_trust_device_custom_profile_local_domain_fallback.example }`, }, { @@ -186,6 +216,11 @@ resource "cloudflare_zero_trust_device_custom_profile_local_domain_fallback" "ex dns_server = ["10.0.0.1"] } ] +} + +moved { + from = cloudflare_zero_trust_local_fallback_domain.example + to = cloudflare_zero_trust_device_custom_profile_local_domain_fallback.example }`, }, { @@ -220,6 +255,11 @@ resource "cloudflare_zero_trust_device_custom_profile_local_domain_fallback" "ex dns_server = ["10.0.0.2", "10.0.0.3"] } ] +} + +moved { + from = cloudflare_zero_trust_local_fallback_domain.example + to = cloudflare_zero_trust_device_custom_profile_local_domain_fallback.example }`, }, { @@ -243,6 +283,11 @@ resource "cloudflare_zero_trust_device_custom_profile_local_domain_fallback" "ex suffix = "example.com" } ] +} + +moved { + from = cloudflare_fallback_domain.example + to = cloudflare_zero_trust_device_custom_profile_local_domain_fallback.example }`, }, { @@ -275,6 +320,11 @@ resource "cloudflare_zero_trust_device_default_profile_local_domain_fallback" "d ] } +moved { + from = cloudflare_zero_trust_local_fallback_domain.default + to = cloudflare_zero_trust_device_default_profile_local_domain_fallback.default +} + resource "cloudflare_zero_trust_device_custom_profile_local_domain_fallback" "custom" { account_id = "f037e56e89293a057740de681ac9abbe" policy_id = "policy123" @@ -284,302 +334,16 @@ resource "cloudflare_zero_trust_device_custom_profile_local_domain_fallback" "cu suffix = "custom.example.com" } ] -}`, - }, - } - - testhelpers.RunConfigTransformTests(t, tests, migrator) } -func TestStateTransformation(t *testing.T) { - migrator := NewV4ToV5Migrator() - - tests := []testhelpers.StateTestCase{ - { - Name: "basic default profile state", - Input: `{ - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "domains": [ - { - "suffix": "example.com" - } - ] - } -}`, - Expected: `{ - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "domains": [ - { - "suffix": "example.com" - } - ] - } -}`, - }, - { - Name: "default profile with all fields", - Input: `{ - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "domains": [ - { - "suffix": "example.com", - "description": "Example domain", - "dns_server": ["1.1.1.1", "8.8.8.8"] - } - ] - } -}`, - Expected: `{ - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "domains": [ - { - "suffix": "example.com", - "description": "Example domain", - "dns_server": ["1.1.1.1", "8.8.8.8"] - } - ] - } -}`, - }, - { - Name: "default profile with multiple domains", - Input: `{ - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "domains": [ - { - "suffix": "example.com", - "description": "Example" - }, - { - "suffix": "another.com", - "dns_server": ["1.1.1.1"] - }, - { - "suffix": "third.com" - } - ] - } -}`, - Expected: `{ - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "domains": [ - { - "suffix": "example.com", - "description": "Example" - }, - { - "suffix": "another.com", - "dns_server": ["1.1.1.1"] - }, - { - "suffix": "third.com" - } - ] - } -}`, - }, - { - Name: "default profile with null policy_id", - Input: `{ - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "policy_id": null, - "domains": [ - { - "suffix": "example.com" - } - ] - } -}`, - Expected: `{ - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "domains": [ - { - "suffix": "example.com" - } - ] - } -}`, - }, - { - Name: "basic custom profile state", - Input: `{ - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "policy_id": "policy123", - "domains": [ - { - "suffix": "corp.example.com" - } - ] - } -}`, - Expected: `{ - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "policy_id": "policy123", - "domains": [ - { - "suffix": "corp.example.com" - } - ] - } -}`, - }, - { - Name: "custom profile with all fields", - Input: `{ - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "policy_id": "policy123", - "domains": [ - { - "suffix": "corp.example.com", - "description": "Corporate domain", - "dns_server": ["10.0.0.1"] - } - ] - } -}`, - Expected: `{ - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "policy_id": "policy123", - "domains": [ - { - "suffix": "corp.example.com", - "description": "Corporate domain", - "dns_server": ["10.0.0.1"] - } - ] - } -}`, - }, - { - Name: "empty domains array", - Input: `{ - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "domains": [] - } -}`, - Expected: `{ - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe" - } -}`, - }, - { - Name: "empty dns_server in domain", - Input: `{ - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "domains": [ - { - "suffix": "example.com", - "dns_server": [] - } - ] - } -}`, - Expected: `{ - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "domains": [ - { - "suffix": "example.com" - } - ] - } -}`, - }, - { - Name: "missing optional fields", - Input: `{ - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "domains": [ - { - "suffix": "example.com" - } - ] - } -}`, - Expected: `{ - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "domains": [ - { - "suffix": "example.com" - } - ] - } -}`, - }, - { - Name: "invalid instance - missing attributes", - Input: `{ - "schema_version": 0 -}`, - Expected: `{ - "schema_version": 0 -}`, - }, - { - Name: "null description field", - Input: `{ - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "domains": [ - { - "suffix": "example.com", - "description": null - } - ] - } -}`, - Expected: `{ - "schema_version": 0, - "attributes": { - "account_id": "f037e56e89293a057740de681ac9abbe", - "domains": [ - { - "suffix": "example.com", - "description": null - } - ] - } +moved { + from = cloudflare_zero_trust_local_fallback_domain.custom + to = cloudflare_zero_trust_device_custom_profile_local_domain_fallback.custom }`, }, } - testhelpers.RunStateTransformTests(t, tests, migrator) + testhelpers.RunConfigTransformTests(t, tests, migrator) } func TestCanHandle(t *testing.T) {