@@ -11,6 +11,7 @@ import (
1111 "github.com/hashicorp/hcl/v2/hclwrite"
1212
1313 "github.com/cloudflare/tf-migrate/internal/transform"
14+ tfhcl "github.com/cloudflare/tf-migrate/internal/transform/hcl"
1415)
1516
1617// resourceClassification describes how a resource will be handled during migration.
@@ -157,6 +158,11 @@ func classifyResource(block *hclwrite.Block, file string, providers transform.Mi
157158
158159 // Check if this resource will be renamed
159160 if newType , ok := renames [resourceType ]; ok {
161+ // Conditional renames: some v4 types map to one of two v5 types
162+ // depending on block attributes. Override the static rename target
163+ // with the per-instance target based on attribute inspection.
164+ newType = resolveConditionalRename (resourceType , newType , block )
165+
160166 return & scannedResource {
161167 File : file ,
162168 ResourceType : resourceType ,
@@ -176,6 +182,51 @@ func classifyResource(block *hclwrite.Block, file string, providers transform.Mi
176182 }
177183}
178184
185+ // resolveConditionalRename overrides the static rename target for resource types
186+ // where the v5 target depends on per-instance block attributes.
187+ //
188+ // Currently handles:
189+ // - cloudflare_zero_trust_device_profiles / cloudflare_device_settings_policy:
190+ // Routes to cloudflare_zero_trust_device_custom_profile when the block has
191+ // match + precedence and is not explicitly default=true.
192+ // - cloudflare_zero_trust_local_fallback_domain / cloudflare_fallback_domain:
193+ // Routes to cloudflare_zero_trust_device_custom_profile_local_domain_fallback
194+ // when the block has a non-empty policy_id.
195+ func resolveConditionalRename (resourceType , staticNewType string , block * hclwrite.Block ) string {
196+ body := block .Body ()
197+
198+ switch resourceType {
199+ case "cloudflare_zero_trust_device_profiles" , "cloudflare_device_settings_policy" :
200+ // Mirror the routing logic from zero_trust_device_profiles TransformConfig:
201+ // custom if !default && match && precedence, else default.
202+ isExplicitDefault := false
203+ if attr := body .GetAttribute ("default" ); attr != nil {
204+ val , ok := tfhcl .ExtractBoolFromAttribute (attr )
205+ if ok {
206+ isExplicitDefault = val
207+ }
208+ }
209+ hasMatch := body .GetAttribute ("match" ) != nil
210+ hasPrecedence := body .GetAttribute ("precedence" ) != nil
211+
212+ if ! isExplicitDefault && hasMatch && hasPrecedence {
213+ return "cloudflare_zero_trust_device_custom_profile"
214+ }
215+
216+ case "cloudflare_zero_trust_local_fallback_domain" , "cloudflare_fallback_domain" :
217+ // Mirror the routing logic from zero_trust_local_fallback_domain TransformConfig:
218+ // custom if policy_id is present and non-empty, else default.
219+ if attr := body .GetAttribute ("policy_id" ); attr != nil {
220+ val := tfhcl .ExtractStringFromAttribute (attr )
221+ if val != "" || tfhcl .IsExpressionAttribute (attr ) {
222+ return "cloudflare_zero_trust_device_custom_profile_local_domain_fallback"
223+ }
224+ }
225+ }
226+
227+ return staticNewType
228+ }
229+
179230// parseMovedBlock extracts from/to information from a moved block.
180231func parseMovedBlock (block * hclwrite.Block , file string ) * existingMovedBlock {
181232 body := block .Body ()
0 commit comments