diff --git a/internal/services/vpc/acl.go b/internal/services/vpc/acl.go index 25c220b64..e0ef4828c 100644 --- a/internal/services/vpc/acl.go +++ b/internal/services/vpc/acl.go @@ -2,6 +2,8 @@ package vpc import ( "context" + "errors" + "fmt" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -10,6 +12,7 @@ import ( "github.com/scaleway/terraform-provider-scaleway/v2/internal/httperrors" "github.com/scaleway/terraform-provider-scaleway/v2/internal/locality" "github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/regional" + "github.com/scaleway/terraform-provider-scaleway/v2/internal/meta" "github.com/scaleway/terraform-provider-scaleway/v2/internal/verify" ) @@ -20,7 +23,57 @@ func ResourceACL() *schema.Resource { UpdateContext: ResourceVPCACLUpdate, DeleteContext: ResourceVPCACLDelete, Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, + StateContext: func( + ctx context.Context, + d *schema.ResourceData, + m interface{}, + ) ([]*schema.ResourceData, error) { + // If importing by ID (e.g. "fr-par/8cef…"), we just set the ID field to state, allowing the read to fill in the rest of the data + if d.Id() != "" { + return []*schema.ResourceData{d}, nil + } + + // Otherwise, we're importing by identity “identity = { id = ..., region = ... }” + identity, err := d.Identity() + if err != nil { + return nil, fmt.Errorf("error retrieving identity: %w", err) + } + + rawID := identity.Get("id").(string) + + regionVal := identity.Get("region").(string) + if regionVal == "" { + region, err := meta.ExtractRegion(d, m) + if err != nil { + return nil, errors.New("identity.region was not set") + } + + regionVal = region.String() + } + + localizedID := fmt.Sprintf("%s/%s", regionVal, rawID) + + d.SetId(localizedID) + + return []*schema.ResourceData{d}, nil + }, + }, + Identity: &schema.ResourceIdentity{ + Version: 0, + SchemaFunc: func() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + RequiredForImport: true, + Description: "The ACL ID (e.g. `11111111-1111-1111-1111-111111111111`)", + }, + "region": { + Type: schema.TypeString, + OptionalForImport: true, + Description: "The region of the VPC. If omitted during import, defaults from provider", + }, + } + }, }, SchemaVersion: 0, Schema: map[string]*schema.Schema{ diff --git a/internal/services/vpc/private_network.go b/internal/services/vpc/private_network.go index b4b3e7dfe..0aaa8b46e 100644 --- a/internal/services/vpc/private_network.go +++ b/internal/services/vpc/private_network.go @@ -2,6 +2,8 @@ package vpc import ( "context" + "errors" + "fmt" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry" @@ -25,7 +27,57 @@ func ResourcePrivateNetwork() *schema.Resource { UpdateContext: ResourceVPCPrivateNetworkUpdate, DeleteContext: ResourceVPCPrivateNetworkDelete, Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, + StateContext: func( + ctx context.Context, + d *schema.ResourceData, + m interface{}, + ) ([]*schema.ResourceData, error) { + // If importing by ID (e.g. "fr-par/8cef…"), we just set the ID field to state, allowing the read to fill in the rest of the data + if d.Id() != "" { + return []*schema.ResourceData{d}, nil + } + + // Otherwise, we're importing by identity “identity = { id = ..., region = ... }” + identity, err := d.Identity() + if err != nil { + return nil, fmt.Errorf("error retrieving identity: %w", err) + } + + rawID := identity.Get("id").(string) + + regionVal := identity.Get("region").(string) + if regionVal == "" { + region, err := meta.ExtractRegion(d, m) + if err != nil { + return nil, errors.New("identity.region was not set") + } + + regionVal = region.String() + } + + localizedID := fmt.Sprintf("%s/%s", regionVal, rawID) + + d.SetId(localizedID) + + return []*schema.ResourceData{d}, nil + }, + }, + Identity: &schema.ResourceIdentity{ + Version: 0, + SchemaFunc: func() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + RequiredForImport: true, + Description: "The Private Network ID (e.g. `11111111-1111-1111-1111-111111111111`)", + }, + "region": { + Type: schema.TypeString, + OptionalForImport: true, + Description: "The region of the VPC. If omitted during import, defaults from provider", + }, + } + }, }, SchemaVersion: 1, StateUpgraders: []schema.StateUpgrader{ @@ -266,6 +318,18 @@ func ResourceVPCPrivateNetworkRead(ctx context.Context, d *schema.ResourceData, _ = d.Set("ipv4_subnet", ipv4Subnet) _ = d.Set("ipv6_subnets", ipv6Subnets) + identity, err := d.Identity() + if err != nil { + return diag.FromErr(err) + } + + if err = identity.Set("id", pn.ID); err != nil { + return diag.FromErr(err) + } + if err = identity.Set("region", region); err != nil { + return diag.FromErr(err) + } + return nil } diff --git a/internal/services/vpc/route.go b/internal/services/vpc/route.go index 342ee1b49..d72044bf9 100644 --- a/internal/services/vpc/route.go +++ b/internal/services/vpc/route.go @@ -2,6 +2,8 @@ package vpc import ( "context" + "errors" + "fmt" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -11,6 +13,7 @@ import ( "github.com/scaleway/terraform-provider-scaleway/v2/internal/httperrors" "github.com/scaleway/terraform-provider-scaleway/v2/internal/locality" "github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/regional" + "github.com/scaleway/terraform-provider-scaleway/v2/internal/meta" "github.com/scaleway/terraform-provider-scaleway/v2/internal/types" ) @@ -21,7 +24,57 @@ func ResourceRoute() *schema.Resource { UpdateContext: ResourceRouteUpdate, DeleteContext: ResourceRouteDelete, Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, + StateContext: func( + ctx context.Context, + d *schema.ResourceData, + m interface{}, + ) ([]*schema.ResourceData, error) { + // If importing by ID (e.g. "fr-par/8cef…"), we just set the ID field to state, allowing the read to fill in the rest of the data + if d.Id() != "" { + return []*schema.ResourceData{d}, nil + } + + // Otherwise, we're importing by identity “identity = { id = ..., region = ... }” + identity, err := d.Identity() + if err != nil { + return nil, fmt.Errorf("error retrieving identity: %w", err) + } + + rawID := identity.Get("id").(string) + + regionVal := identity.Get("region").(string) + if regionVal == "" { + region, err := meta.ExtractRegion(d, m) + if err != nil { + return nil, errors.New("identity.region was not set") + } + + regionVal = region.String() + } + + localizedID := fmt.Sprintf("%s/%s", regionVal, rawID) + + d.SetId(localizedID) + + return []*schema.ResourceData{d}, nil + }, + }, + Identity: &schema.ResourceIdentity{ + Version: 0, + SchemaFunc: func() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + RequiredForImport: true, + Description: "The Route ID (e.g. `11111111-1111-1111-1111-111111111111`)", + }, + "region": { + Type: schema.TypeString, + OptionalForImport: true, + Description: "The region of the VPC. If omitted during import, defaults from provider", + }, + } + }, }, SchemaVersion: 0, Schema: map[string]*schema.Schema{ diff --git a/internal/services/vpc/vpc.go b/internal/services/vpc/vpc.go index 2b98fdb89..54e24fd7b 100644 --- a/internal/services/vpc/vpc.go +++ b/internal/services/vpc/vpc.go @@ -3,6 +3,7 @@ package vpc import ( "context" "errors" + "fmt" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -10,6 +11,7 @@ import ( "github.com/scaleway/scaleway-sdk-go/scw" "github.com/scaleway/terraform-provider-scaleway/v2/internal/httperrors" "github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/regional" + "github.com/scaleway/terraform-provider-scaleway/v2/internal/meta" "github.com/scaleway/terraform-provider-scaleway/v2/internal/services/account" "github.com/scaleway/terraform-provider-scaleway/v2/internal/types" ) @@ -21,7 +23,57 @@ func ResourceVPC() *schema.Resource { UpdateContext: ResourceVPCUpdate, DeleteContext: ResourceVPCDelete, Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, + StateContext: func( + ctx context.Context, + d *schema.ResourceData, + m interface{}, + ) ([]*schema.ResourceData, error) { + // If importing by ID (e.g. "fr-par/8cef…"), we just set the ID field to state, allowing the read to fill in the rest of the data + if d.Id() != "" { + return []*schema.ResourceData{d}, nil + } + + // Otherwise, we're importing by identity “identity = { id = ..., region = ... }” + identity, err := d.Identity() + if err != nil { + return nil, fmt.Errorf("error retrieving identity: %w", err) + } + + rawID := identity.Get("id").(string) + + regionVal := identity.Get("region").(string) + if regionVal == "" { + region, err := meta.ExtractRegion(d, m) + if err != nil { + return nil, errors.New("identity.region was not set") + } + + regionVal = region.String() + } + + localizedID := fmt.Sprintf("%s/%s", regionVal, rawID) + + d.SetId(localizedID) + + return []*schema.ResourceData{d}, nil + }, + }, + Identity: &schema.ResourceIdentity{ + Version: 0, + SchemaFunc: func() map[string]*schema.Schema { + return map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + RequiredForImport: true, + Description: "The VPC ID (e.g. `11111111-1111-1111-1111-111111111111`)", + }, + "region": { + Type: schema.TypeString, + OptionalForImport: true, + Description: "The region of the VPC. If omitted during import, defaults from provider", + }, + } + }, }, SchemaVersion: 0, Schema: map[string]*schema.Schema{ @@ -131,6 +183,18 @@ func ResourceVPCRead(ctx context.Context, d *schema.ResourceData, m interface{}) _ = d.Set("tags", res.Tags) } + identity, err := d.Identity() + if err != nil { + return diag.FromErr(err) + } + + if err = identity.Set("id", res.ID); err != nil { + return diag.FromErr(err) + } + if err = identity.Set("region", region); err != nil { + return diag.FromErr(err) + } + return nil }