Skip to content

Migrate resources, data sources from terraform-plugin-sdk/v2 to terraform-plugin-framework #333

Description

@sureshanaparti

Summary

The provider is already a muxed provider serving both terraform-plugin-sdk/v2 and
terraform-plugin-framework behind a single protocol-v6 endpoint, but only 3 of 95
resource types have been migrated to the framework. This issue tracks migrating the
remaining 63 resources and 29 data sources.

Current state

main.go combines two provider servers via tf6muxserver:

  • providerserver.NewProtocol6(cloudstack.New()) — framework provider, defined in cloudstack/provider_v6.go
  • cloudstack.Provider().GRPCProvider — SDKv2 provider, wrapped by tf5to6server.UpgradeServer() to bridge protocol 5 → 6

Split of resource types by SDK:

Resources Data sources
SDKv2 (cloudstack/provider.go) 63 29
Framework (CloudstackProvider.Resources()) 3 0

Already on the framework: cloudstack_service_offering_constrained,
cloudstack_service_offering_fixed, cloudstack_service_offering_unconstrained.

100 of the ~109 Go files under cloudstack/ still import terraform-plugin-sdk/v2;
9 import the framework.

Relevant dependencies in go.mod — both SDKs and the mux are already present, so no
dependency work is needed:

Module Version
terraform-plugin-framework v1.12.0
terraform-plugin-framework-validators v0.12.0
terraform-plugin-mux v0.16.0
terraform-plugin-go v0.24.0
terraform-plugin-sdk/v2 v2.33.0
terraform-plugin-testing v1.7.0
Go 1.23.0

References

Motivation

Per HashiCorp's framework benefits page,
HashiCorp has stopped most feature development on SDKv2 and recommends the framework for
new development and for migrating existing providers. Staying on SDKv2 for the bulk of
the provider costs us:

  • Null vs unknown vs zero. SDKv2 conflates "unset", "", 0 and false, so optional
    attributes cannot distinguish "user did not set this" from "user set the zero value".
  • No provider-defined functions.
  • No ephemeral resources / write-only attributes, the modern mechanism for handling
    short-lived secrets (API keys, VM passwords, Kubernetes cluster configs) without
    persisting them in state.
  • No list operations for discovering unmanaged resources.
  • Two schema idioms in one repo, so every new contribution has to pick a side.

Scope

Port each remaining SDKv2 resource and data source to the framework: remove it from
ResourcesMap/DataSourcesMap and register it in
CloudstackProvider.Resources()/DataSources(). Once the lists below are complete,
terraform-plugin-sdk/v2, cloudstack/provider.go and the tf5to6server bridge in
main.go can be removed.

No user-facing HCL change is expected or intended: attribute names, block shapes and
semantics must be preserved, and upgrading from the last SDKv2 release should produce
an empty plan for every resource type.

The in-tree reference pattern is cloudstack/service_offering_*.go together with
cloudstack/provider_v6.go.

Notes for implementers

  • A resource type may be served by exactly one server in the mux. Registering a type
    with the framework while it remains in ResourcesMap makes the mux server fail at
    startup with a duplicate-type error, so both changes belong in the same commit.
  • Every SDKv2 ForceNew: true must be reproduced as a RequiresReplace() plan modifier
    on the same attribute. There are 296 across 59 files.
  • ConflictsWith (16 uses), ValidateFunc (11), CustomizeDiff (13) and
    DiffSuppressFunc (1) become framework validators and plan modifiers.
  • ImportState must be written explicitly for the 45 files that currently have an
    Importer.
  • There are no StateUpgraders, MigrateState, Timeouts blocks, partial state or
    resource.Retry uses anywhere in the SDKv2 surface.
  • 35 schema.TypeSet and 22 schema.TypeList uses need a deliberate choice between
    nested blocks and nested attributes; turning a block into an attribute is a
    config-breaking change.
  • The cloudstack-go call sites port verbatim — NewCreate*Params / Set* are identical
    on both sides. Only the surrounding plumbing changes.
  • Acceptance tests largely survive unchanged; terraform-plugin-testing is already a
    dependency and is protocol-agnostic.

Resources (63)

  • cloudstack_account
  • cloudstack_affinity_group
  • cloudstack_attach_volume
  • cloudstack_autoscale_policy
  • cloudstack_autoscale_vm_group
  • cloudstack_autoscale_vm_profile
  • cloudstack_cluster
  • cloudstack_cni_configuration
  • cloudstack_condition
  • cloudstack_configuration
  • cloudstack_counter
  • cloudstack_disk
  • cloudstack_disk_offering
  • cloudstack_domain
  • cloudstack_egress_firewall
  • cloudstack_firewall
  • cloudstack_host
  • cloudstack_instance
  • cloudstack_ipaddress
  • cloudstack_kubernetes_cluster
  • cloudstack_kubernetes_version
  • cloudstack_limits
  • cloudstack_loadbalancer
  • cloudstack_loadbalancer_rule
  • cloudstack_network
  • cloudstack_network_acl
  • cloudstack_network_acl_rule
  • cloudstack_network_acl_ruleset
  • cloudstack_network_offering
  • cloudstack_network_service_provider
  • cloudstack_network_service_provider_state
  • cloudstack_nic
  • cloudstack_physical_network
  • cloudstack_pod
  • cloudstack_port_forward
  • cloudstack_private_gateway
  • cloudstack_project
  • cloudstack_quota_tariff
  • cloudstack_role
  • cloudstack_role_permission
  • cloudstack_secondary_ipaddress
  • cloudstack_secondary_storage
  • cloudstack_security_group
  • cloudstack_security_group_rule
  • cloudstack_service_offering
  • cloudstack_snapshot_policy
  • cloudstack_ssh_keypair
  • cloudstack_static_nat
  • cloudstack_static_route
  • cloudstack_storage_network_ip_range
  • cloudstack_storage_pool
  • cloudstack_template
  • cloudstack_traffic_type
  • cloudstack_user
  • cloudstack_user_data
  • cloudstack_vlan_ip_range
  • cloudstack_volume
  • cloudstack_vpc
  • cloudstack_vpc_offering
  • cloudstack_vpn_connection
  • cloudstack_vpn_customer_gateway
  • cloudstack_vpn_gateway
  • cloudstack_zone

Data sources (29)

  • cloudstack_autoscale_policy
  • cloudstack_autoscale_vm_group
  • cloudstack_autoscale_vm_profile
  • cloudstack_cluster
  • cloudstack_condition
  • cloudstack_counter
  • cloudstack_domain
  • cloudstack_instance
  • cloudstack_ipaddress
  • cloudstack_kubernetes_cluster_config
  • cloudstack_limits
  • cloudstack_network_offering
  • cloudstack_physical_network
  • cloudstack_pod
  • cloudstack_project
  • cloudstack_quota
  • cloudstack_quota_enabled
  • cloudstack_quota_tariff
  • cloudstack_role
  • cloudstack_service_offering
  • cloudstack_ssh_keypair
  • cloudstack_template
  • cloudstack_user
  • cloudstack_user_data
  • cloudstack_volume
  • cloudstack_vpc
  • cloudstack_vpc_offering
  • cloudstack_vpn_connection
  • cloudstack_zone

Teardown once the above is complete

  • Remove terraform-plugin-sdk/v2 from go.mod
  • Remove the tf5to6server bridge from main.go
  • Delete cloudstack/provider.go and the SDKv2 test helpers

Related findings

  • cloudstack_service_offering (SDKv2) coexists with the three framework
    cloudstack_service_offering_* resources, giving two ways to create a service
    offering. Needs a decision on whether the older one is deprecated.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions