diff --git a/client/secure_compute_network.go b/client/secure_compute_network.go new file mode 100644 index 00000000..ce13c20f --- /dev/null +++ b/client/secure_compute_network.go @@ -0,0 +1,62 @@ +package client + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-log/tflog" +) + +// SecureComputeNetworkAWS represents the AWS configuration part of a Secure Compute Network. +type SecureComputeNetworkAWS struct { + AccountID string `json:"AccountId"` + Region string `json:"Region"` + ElasticIpAddresses []string `json:"ElasticIpAddresses,omitempty"` + LambdaRoleArn *string `json:"LambdaRoleArn,omitempty"` + SecurityGroupId *string `json:"SecurityGroupId,omitempty"` + StackId *string `json:"StackId,omitempty"` + SubnetIds []string `json:"SubnetIds,omitempty"` + SubscriptionArn *string `json:"SubscriptionArn,omitempty"` + VpcId *string `json:"VpcId,omitempty"` +} + +// SecureComputeNetwork represents a Vercel Secure Compute Network configuration. +type SecureComputeNetwork struct { + DC string `json:"dc"` + ProjectIDs []string `json:"projectIds,omitempty"` + ProjectsCount *int `json:"projectsCount,omitempty"` + PeeringConnectionsCount *int `json:"peeringConnectionsCount,omitempty"` + + AWS SecureComputeNetworkAWS `json:"AWS"` + ConfigurationName string `json:"ConfigurationName"` + ID string `json:"Id"` + TeamID string `json:"TeamId"` + CIDRBlock *string `json:"CidrBlock,omitempty"` + AvailabilityZoneIDs []string `json:"AvailabilityZoneIds,omitempty"` + Version string `json:"Version"` + ConfigurationStatus string `json:"ConfigurationStatus"` +} + +// ListSecureComputeNetworks fetches all Secure Compute Networks for a team. +func (c *Client) ListSecureComputeNetworks( + ctx context.Context, + teamID string, +) ([]SecureComputeNetwork, error) { + url := fmt.Sprintf("%s/v1/connect/configurations", c.baseURL) + if c.TeamID(teamID) != "" { + url = fmt.Sprintf("%s?teamId=%s", url, c.TeamID(teamID)) + } + tflog.Info(ctx, "reading secure compute networks", map[string]any{ + "url": url, + "team_id": c.TeamID(teamID), + }) + + var networks []SecureComputeNetwork + err := c.doRequest(clientRequest{ + ctx: ctx, + method: "GET", + url: url, + }, &networks) + + return networks, err +} diff --git a/docs/data-sources/secure_compute_network.md b/docs/data-sources/secure_compute_network.md new file mode 100644 index 00000000..7be3eb7e --- /dev/null +++ b/docs/data-sources/secure_compute_network.md @@ -0,0 +1,55 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "vercel_secure_compute_network Data Source - terraform-provider-vercel" +subcategory: "" +description: |- + Provides information about an existing Vercel Secure Compute Network. + This data source allows you to retrieve details about a Secure Compute Network by its name and optional team ID. +--- + +# vercel_secure_compute_network (Data Source) + +Provides information about an existing Vercel Secure Compute Network. + +This data source allows you to retrieve details about a Secure Compute Network by its name and optional team ID. + + + + +## Schema + +### Required + +- `name` (String) The name of the Secure Compute Network configuration. + +### Optional + +- `team_id` (String) The ID of the Vercel team the Secure Compute Network belongs to. If omitted, the provider will use the team configured on the provider or the user's default team. + +### Read-Only + +- `availability_zone_ids` (Set of String) A set of AWS Availability Zone IDs where the Secure Compute Network resources are deployed. +- `aws` (Attributes) AWS configuration for the Secure Compute Network. (see [below for nested schema](#nestedatt--aws)) +- `cidr_block` (String) The CIDR block assigned to the Secure Compute Network. +- `configuration_status` (String) The operational status of the Secure Compute Network (e.g., 'ready', 'create_in_progress'). +- `dc` (String) The data center (region) associated with the Secure Compute Network. +- `id` (String) The unique identifier of the Secure Compute Network. +- `peering_connections_count` (Number) The number of peering connections established for this Secure Compute Network. +- `project_ids` (Set of String) A list of Vercel Project IDs connected to this Secure Compute Network. +- `projects_count` (Number) The number of Vercel Projects connected to this Secure Compute Network. +- `version` (String) The current version identifier of the Secure Compute Network configuration. + + +### Nested Schema for `aws` + +Read-Only: + +- `account_id` (String) The AWS account ID. +- `elastic_ip_addresses` (Set of String) A list of Elastic IP addresses. +- `lambda_role_arn` (String) The ARN of the Lambda role. +- `region` (String) The AWS region. +- `security_group_id` (String) The ID of the security group. +- `stack_id` (String) The ID of the CloudFormation stack. +- `subnet_ids` (Set of String) A list of subnet IDs. +- `subscription_arn` (String) The ARN of the subscription. +- `vpc_id` (String) The ID of the VPC. diff --git a/vercel/contains.go b/vercel/contains.go deleted file mode 100644 index 7441a49e..00000000 --- a/vercel/contains.go +++ /dev/null @@ -1,10 +0,0 @@ -package vercel - -func contains[T comparable](items []T, i T) bool { - for _, j := range items { - if j == i { - return true - } - } - return false -} diff --git a/vercel/convert.go b/vercel/convert.go new file mode 100644 index 00000000..2747efde --- /dev/null +++ b/vercel/convert.go @@ -0,0 +1,29 @@ +package vercel + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/types" +) + +func intPtrToInt64Ptr(i *int) *int64 { + if i == nil { + return nil + } + val := int64(*i) + return &val +} + +func stringsToSet(ctx context.Context, strings []string) (types.Set, diag.Diagnostics) { + diags := diag.Diagnostics{} + stringSet := []attr.Value{} + for _, s := range strings { + stringSet = append(stringSet, types.StringValue(s)) + } + + set, d := types.SetValueFrom(ctx, types.StringType, stringSet) + diags.Append(d...) + return set, diags +} diff --git a/vercel/data_source_secure_compute_network.go b/vercel/data_source_secure_compute_network.go new file mode 100644 index 00000000..5eb0eff6 --- /dev/null +++ b/vercel/data_source_secure_compute_network.go @@ -0,0 +1,312 @@ +package vercel + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/datasource" + "github.com/hashicorp/terraform-plugin-framework/datasource/schema" + "github.com/hashicorp/terraform-plugin-framework/diag" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-framework/types/basetypes" + "github.com/hashicorp/terraform-plugin-log/tflog" + "github.com/vercel/terraform-provider-vercel/v3/client" +) + +var ( + _ datasource.DataSource = &secureComputeNetworkDataSource{} + _ datasource.DataSourceWithConfigure = &secureComputeNetworkDataSource{} +) + +func newSecureComputeNetworkDataSource() datasource.DataSource { + return &secureComputeNetworkDataSource{} +} + +type secureComputeNetworkDataSource struct { + client *client.Client +} + +func (d *secureComputeNetworkDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { + resp.TypeName = req.ProviderTypeName + "_secure_compute_network" +} + +func (d *secureComputeNetworkDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { + // Prevent panic if the provider has not been configured. + if req.ProviderData == nil { + return + } + + client, ok := req.ProviderData.(*client.Client) + if !ok { + resp.Diagnostics.AddError( + "Unexpected Data Source Configure Type", + fmt.Sprintf("Expected *client.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData), + ) + return + } + + d.client = client +} + +// Schema returns the schema information for an secureComputeNetwork data source +func (r *secureComputeNetworkDataSource) Schema(_ context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) { + resp.Schema = schema.Schema{ + Description: ` +Provides information about an existing Vercel Secure Compute Network. + +This data source allows you to retrieve details about a Secure Compute Network by its name and optional team ID. +`, + Attributes: map[string]schema.Attribute{ + "name": schema.StringAttribute{ + Description: "The name of the Secure Compute Network configuration.", + Required: true, + }, + "team_id": schema.StringAttribute{ + Description: "The ID of the Vercel team the Secure Compute Network belongs to. " + + "If omitted, the provider will use the team configured on the provider or the user's default team.", + Optional: true, + }, + "id": schema.StringAttribute{ + Description: "The unique identifier of the Secure Compute Network.", + Computed: true, + }, + "dc": schema.StringAttribute{ + Description: "The data center (region) associated with the Secure Compute Network.", + Computed: true, + }, + "project_ids": schema.SetAttribute{ + Description: "A list of Vercel Project IDs connected to this Secure Compute Network.", + Computed: true, + ElementType: types.StringType, + }, + "projects_count": schema.Int64Attribute{ + Description: "The number of Vercel Projects connected to this Secure Compute Network.", + Computed: true, + }, + "peering_connections_count": schema.Int64Attribute{ + Description: "The number of peering connections established for this Secure Compute Network.", + Computed: true, + }, + "cidr_block": schema.StringAttribute{ + Description: "The CIDR block assigned to the Secure Compute Network.", + Computed: true, + }, + "availability_zone_ids": schema.SetAttribute{ + Description: "A set of AWS Availability Zone IDs where the Secure Compute Network resources are deployed.", + Computed: true, + ElementType: types.StringType, + }, + "version": schema.StringAttribute{ + Description: "The current version identifier of the Secure Compute Network configuration.", + Computed: true, + }, + "configuration_status": schema.StringAttribute{ + Description: "The operational status of the Secure Compute Network (e.g., 'ready', 'create_in_progress').", + Computed: true, + }, + "aws": schema.SingleNestedAttribute{ + Description: "AWS configuration for the Secure Compute Network.", + Computed: true, + Attributes: map[string]schema.Attribute{ + "account_id": schema.StringAttribute{ + Description: "The AWS account ID.", + Computed: true, + }, + "region": schema.StringAttribute{ + Description: "The AWS region.", + Computed: true, + }, + "elastic_ip_addresses": schema.SetAttribute{ + Description: "A list of Elastic IP addresses.", + Computed: true, + ElementType: types.StringType, + }, + "lambda_role_arn": schema.StringAttribute{ + Description: "The ARN of the Lambda role.", + Computed: true, + }, + "security_group_id": schema.StringAttribute{ + Description: "The ID of the security group.", + Computed: true, + }, + "stack_id": schema.StringAttribute{ + Description: "The ID of the CloudFormation stack.", + Computed: true, + }, + "subnet_ids": schema.SetAttribute{ + Description: "A list of subnet IDs.", + Computed: true, + ElementType: types.StringType, + }, + "subscription_arn": schema.StringAttribute{ + Description: "The ARN of the subscription.", + Computed: true, + }, + "vpc_id": schema.StringAttribute{ + Description: "The ID of the VPC.", + Computed: true, + }, + }, + }, + }, + } +} + +type SecureComputeNetworkAWS struct { + AccountID types.String `tfsdk:"account_id"` + Region types.String `tfsdk:"region"` + ElasticIpAddresses types.Set `tfsdk:"elastic_ip_addresses"` + LambdaRoleArn types.String `tfsdk:"lambda_role_arn"` + SecurityGroupID types.String `tfsdk:"security_group_id"` + StackID types.String `tfsdk:"stack_id"` + SubnetIDs types.Set `tfsdk:"subnet_ids"` + SubscriptionArn types.String `tfsdk:"subscription_arn"` + VPCID types.String `tfsdk:"vpc_id"` +} + +var secureComputeNetworkAWSAttrTypes = types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "account_id": types.StringType, + "region": types.StringType, + "elastic_ip_addresses": types.SetType{ + ElemType: types.StringType, + }, + "lambda_role_arn": types.StringType, + "security_group_id": types.StringType, + "stack_id": types.StringType, + "subnet_ids": types.SetType{ + ElemType: types.StringType, + }, + "subscription_arn": types.StringType, + "vpc_id": types.StringType, + }, +} + +type SecureComputeNetwork struct { + AvailabilityZoneIDs types.Set `tfsdk:"availability_zone_ids"` + CIDRBlock types.String `tfsdk:"cidr_block"` + ConfigurationStatus types.String `tfsdk:"configuration_status"` + DC types.String `tfsdk:"dc"` + ID types.String `tfsdk:"id"` + Name types.String `tfsdk:"name"` + PeeringConnectionsCount types.Int64 `tfsdk:"peering_connections_count"` + ProjectIDs types.Set `tfsdk:"project_ids"` + ProjectsCount types.Int64 `tfsdk:"projects_count"` + TeamID types.String `tfsdk:"team_id"` + Version types.String `tfsdk:"version"` + AWS types.Object `tfsdk:"aws"` +} + +func convertResponseToSecureComputeNetwork(ctx context.Context, response *client.SecureComputeNetwork) (out SecureComputeNetwork, diags diag.Diagnostics) { + projectIDs, ds := stringsToSet(ctx, response.ProjectIDs) + diags.Append(ds...) + if diags.HasError() { + return out, diags + } + + azIDs, ds := stringsToSet(ctx, response.AvailabilityZoneIDs) + diags.Append(ds...) + if diags.HasError() { + return SecureComputeNetwork{}, diags + } + + aws, ds := convertResponseToSecureComputeNetworkAWS(ctx, response.AWS) + diags.Append(ds...) + if diags.HasError() { + return SecureComputeNetwork{}, diags + } + + return SecureComputeNetwork{ + AvailabilityZoneIDs: azIDs, + CIDRBlock: types.StringPointerValue(response.CIDRBlock), + ConfigurationStatus: types.StringValue(response.ConfigurationStatus), + DC: types.StringValue(response.DC), + ID: types.StringValue(response.ID), + Name: types.StringValue(response.ConfigurationName), + PeeringConnectionsCount: types.Int64PointerValue(intPtrToInt64Ptr(response.PeeringConnectionsCount)), + ProjectIDs: projectIDs, + ProjectsCount: types.Int64PointerValue(intPtrToInt64Ptr(response.ProjectsCount)), + TeamID: types.StringValue(response.TeamID), + Version: types.StringValue(response.Version), + AWS: aws, + }, diags +} + +func convertResponseToSecureComputeNetworkAWS(ctx context.Context, aws client.SecureComputeNetworkAWS) (basetypes.ObjectValue, diag.Diagnostics) { + elasticIpAddresses, diags := stringsToSet(ctx, aws.ElasticIpAddresses) + if diags.HasError() { + return types.ObjectNull(secureComputeNetworkAWSAttrTypes.AttrTypes), diags + } + + subnetIDs, diags := stringsToSet(ctx, aws.SubnetIds) + if diags.HasError() { + return types.ObjectNull(secureComputeNetworkAWSAttrTypes.AttrTypes), diags + } + + return types.ObjectValueMust( + secureComputeNetworkAWSAttrTypes.AttrTypes, map[string]attr.Value{ + "account_id": types.StringValue(aws.AccountID), + "region": types.StringValue(aws.Region), + "elastic_ip_addresses": elasticIpAddresses, + "lambda_role_arn": types.StringPointerValue(aws.LambdaRoleArn), + "security_group_id": types.StringPointerValue(aws.SecurityGroupId), + "stack_id": types.StringPointerValue(aws.StackId), + "subnet_ids": subnetIDs, + "subscription_arn": types.StringPointerValue(aws.SubscriptionArn), + "vpc_id": types.StringPointerValue(aws.VpcId), + }), diags +} + +func (d *secureComputeNetworkDataSource) Read( + ctx context.Context, + req datasource.ReadRequest, + resp *datasource.ReadResponse, +) { + var config SecureComputeNetwork + diags := req.Config.Get(ctx, &config) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + tflog.Debug(ctx, "Reading Secure Compute Network data source", map[string]any{"name": config.Name.ValueString(), "team_id": config.TeamID.ValueString()}) + + networks, err := d.client.ListSecureComputeNetworks(ctx, config.TeamID.ValueString()) + if err != nil { + resp.Diagnostics.AddError( + "Client Error", + fmt.Sprintf( + "Unable to list Secure Compute Networks: %s", + err, + ), + ) + return + } + + var network *client.SecureComputeNetwork + for i, n := range networks { + if n.ConfigurationName == config.Name.ValueString() { + network = &networks[i] + break + } + } + + if network == nil { + resp.Diagnostics.AddError("Secure Compute Network Not Found", fmt.Sprintf( + "No Secure Compute Network found with name '%s' for team_id '%s'", + config.Name.ValueString(), + d.client.TeamID(config.TeamID.ValueString()), + )) + return + } + + out, diags := convertResponseToSecureComputeNetwork(ctx, network) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + diags = resp.State.Set(ctx, &out) + resp.Diagnostics.Append(diags...) +} diff --git a/vercel/data_source_secure_compute_network_test.go b/vercel/data_source_secure_compute_network_test.go new file mode 100644 index 00000000..c436c6d1 --- /dev/null +++ b/vercel/data_source_secure_compute_network_test.go @@ -0,0 +1,41 @@ +package vercel_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" +) + +func TestAcc_SecureComputeNetworkDataSource(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: cfg(` + data "vercel_secure_compute_network" "test" { + name = "network 1" + } + `), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.vercel_secure_compute_network.test", "name", "network 1"), + resource.TestCheckResourceAttr("data.vercel_secure_compute_network.test", "team_id", testTeam(t)), + resource.TestCheckResourceAttrSet("data.vercel_secure_compute_network.test", "id"), + resource.TestCheckResourceAttr("data.vercel_secure_compute_network.test", "dc", "sfo1"), + resource.TestCheckResourceAttrSet("data.vercel_secure_compute_network.test", "projects_count"), + resource.TestCheckResourceAttrSet("data.vercel_secure_compute_network.test", "peering_connections_count"), + resource.TestCheckResourceAttrSet("data.vercel_secure_compute_network.test", "cidr_block"), + resource.TestCheckResourceAttrSet("data.vercel_secure_compute_network.test", "version"), + resource.TestCheckResourceAttr("data.vercel_secure_compute_network.test", "configuration_status", "ready"), + resource.TestCheckResourceAttrSet("data.vercel_secure_compute_network.test", "aws.account_id"), + resource.TestCheckResourceAttr("data.vercel_secure_compute_network.test", "aws.region", "us-west-1"), + resource.TestCheckResourceAttr("data.vercel_secure_compute_network.test", "aws.elastic_ip_addresses.#", "2"), + resource.TestCheckResourceAttrSet("data.vercel_secure_compute_network.test", "aws.lambda_role_arn"), + resource.TestCheckResourceAttrSet("data.vercel_secure_compute_network.test", "aws.security_group_id"), + resource.TestCheckResourceAttrSet("data.vercel_secure_compute_network.test", "aws.stack_id"), + resource.TestCheckResourceAttr("data.vercel_secure_compute_network.test", "aws.subnet_ids.#", "2"), + resource.TestCheckResourceAttrSet("data.vercel_secure_compute_network.test", "aws.vpc_id"), + ), + }, + }, + }) +} diff --git a/vercel/data_source_shared_environment_variable.go b/vercel/data_source_shared_environment_variable.go index a0ab833d..45e410f9 100644 --- a/vercel/data_source_shared_environment_variable.go +++ b/vercel/data_source_shared_environment_variable.go @@ -3,6 +3,7 @@ package vercel import ( "context" "fmt" + "slices" "strings" "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator" @@ -148,7 +149,7 @@ func isSameTarget(a []string, b []types.String) bool { return false } for _, v := range b { - if !contains(a, v.ValueString()) { + if !slices.Contains(a, v.ValueString()) { return false } } diff --git a/vercel/provider.go b/vercel/provider.go index 2def273a..b1df39a7 100644 --- a/vercel/provider.go +++ b/vercel/provider.go @@ -56,29 +56,29 @@ func (p *vercelProvider) Resources(_ context.Context) []func() resource.Resource newAliasResource, newAttackChallengeModeResource, newCustomEnvironmentResource, - newDNSRecordResource, newDeploymentResource, + newDNSRecordResource, newEdgeConfigItemResource, newEdgeConfigResource, newEdgeConfigSchemaResource, newEdgeConfigTokenResource, - newFirewallConfigResource, newFirewallBypassResource, + newFirewallConfigResource, newIntegrationProjectAccessResource, newLogDrainResource, + newMicrofrontendGroupMembershipResource, + newMicrofrontendGroupResource, newProjectDeploymentRetentionResource, newProjectDomainResource, newProjectEnvironmentVariableResource, newProjectEnvironmentVariablesResource, newProjectMembersResource, newProjectResource, - newSharedEnvironmentVariableResource, newSharedEnvironmentVariableProjectLinkResource, + newSharedEnvironmentVariableResource, newTeamConfigResource, newTeamMemberResource, newWebhookResource, - newMicrofrontendGroupResource, - newMicrofrontendGroupMembershipResource, } } @@ -97,16 +97,17 @@ func (p *vercelProvider) DataSources(_ context.Context) []func() datasource.Data newEndpointVerificationDataSource, newFileDataSource, newLogDrainDataSource, + newMicrofrontendGroupDataSource, + newMicrofrontendGroupMembershipDataSource, newPrebuiltProjectDataSource, newProjectDataSource, newProjectDeploymentRetentionDataSource, newProjectDirectoryDataSource, newProjectMembersDataSource, + newSecureComputeNetworkDataSource, newSharedEnvironmentVariableDataSource, newTeamConfigDataSource, newTeamMemberDataSource, - newMicrofrontendGroupDataSource, - newMicrofrontendGroupMembershipDataSource, } } diff --git a/vercel/resource_project.go b/vercel/resource_project.go index bbd55e52..365d822f 100644 --- a/vercel/resource_project.go +++ b/vercel/resource_project.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "regexp" + "slices" "github.com/hashicorp/terraform-plugin-framework-validators/boolvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/int64validator" @@ -1196,7 +1197,7 @@ func isSameStringSet(a []string, b []string) bool { return false } for _, v := range a { - if !contains(b, v) { + if !slices.Contains(b, v) { return false } } diff --git a/vercel/resource_team_member.go b/vercel/resource_team_member.go index 9cc51934..710f13cd 100644 --- a/vercel/resource_team_member.go +++ b/vercel/resource_team_member.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "os" + "slices" "time" "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator" @@ -227,12 +228,12 @@ func areSameProjects(oldProjects, newProjects []TeamMemberProject) bool { return false } for _, p := range oldProjects { - if !contains(newProjects, p) { + if !slices.Contains(newProjects, p) { return false } } for _, p := range newProjects { - if !contains(oldProjects, p) { + if !slices.Contains(oldProjects, p) { return false } } @@ -241,12 +242,12 @@ func areSameProjects(oldProjects, newProjects []TeamMemberProject) bool { func diffAccessGroups(oldAgs, newAgs []string) (toAdd, toRemove []string) { for _, n := range newAgs { - if !contains(oldAgs, n) { + if !slices.Contains(oldAgs, n) { toAdd = append(toAdd, n) } } for _, n := range oldAgs { - if !contains(newAgs, n) { + if !slices.Contains(newAgs, n) { toRemove = append(toRemove, n) } } diff --git a/vercel/validator_serverless_function_region.go b/vercel/validator_serverless_function_region.go index 43d42a1b..2fe3d810 100644 --- a/vercel/validator_serverless_function_region.go +++ b/vercel/validator_serverless_function_region.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "net/http" + "slices" "strings" "github.com/hashicorp/terraform-plugin-framework/schema/validator" @@ -87,7 +88,7 @@ func (v validatorServerlessFunctionRegion) ValidateString(ctx context.Context, r } for region, regionInfo := range regions { - if contains(regionInfo.Caps, "V2_DEPLOYMENT_CREATE") { + if slices.Contains(regionInfo.Caps, "V2_DEPLOYMENT_CREATE") { if v.regions == nil { v.regions = map[string]struct{}{} }