diff --git a/client/project.go b/client/project.go
index 5b1cee76..98d7a87c 100644
--- a/client/project.go
+++ b/client/project.go
@@ -156,15 +156,23 @@ func (r *ProjectResponse) Repository() *Repository {
return nil
}
+type ConnectConfigurationResponse struct {
+ Environment string `json:"envId"`
+ ConnectConfigurationID string `json:"connectConfigurationId"`
+ Passive bool `json:"passive"`
+ BuildsEnabled bool `json:"buildsEnabled"`
+}
+
// ProjectResponse defines the information Vercel returns about a project.
type ProjectResponse struct {
- BuildCommand *string `json:"buildCommand"`
- CommandForIgnoringBuildStep *string `json:"commandForIgnoringBuildStep"`
- DevCommand *string `json:"devCommand"`
- Framework *string `json:"framework"`
- ID string `json:"id"`
- TeamID string `json:"-"`
- InstallCommand *string `json:"installCommand"`
+ BuildCommand *string `json:"buildCommand"`
+ CommandForIgnoringBuildStep *string `json:"commandForIgnoringBuildStep"`
+ ConnectConfigurations []ConnectConfigurationResponse `json:"connectConfigurations"`
+ DevCommand *string `json:"devCommand"`
+ Framework *string `json:"framework"`
+ ID string `json:"id"`
+ TeamID string `json:"-"`
+ InstallCommand *string `json:"installCommand"`
Link *struct {
Type string `json:"type"`
// github
@@ -346,6 +354,45 @@ func (c *Client) UpdateProject(ctx context.Context, projectID, teamID string, re
return r, err
}
+// UpdateProjectSecureComputeNetworksRequest allows updating the Secure Compute Networks for a project.
+type UpdateProjectSecureComputeNetworksRequest struct {
+ TeamID string `json:"-"`
+ ProjectID string `json:"-"`
+ SecureComputeNetworks []ConnectConfigurationRequest `json:"connectConfigurations"`
+}
+
+type ConnectConfigurationRequest struct {
+ Environment string `json:"envId"`
+ ConnectConfigurationID string `json:"connectConfigurationId"`
+ Passive bool `json:"passive"`
+ BuildsEnabled bool `json:"buildsEnabled"`
+}
+
+// UpdateProjectSecureComputeNetworks updates an existing projects connectConfigurations within Vercel.
+func (c *Client) UpdateProjectSecureComputeNetworks(ctx context.Context, request UpdateProjectSecureComputeNetworksRequest) (r ProjectResponse, err error) {
+ url := fmt.Sprintf("%s/v9/projects/%s", c.baseURL, request.ProjectID)
+ if c.TeamID(request.TeamID) != "" {
+ url = fmt.Sprintf("%s?teamId=%s", url, c.TeamID(request.TeamID))
+ }
+ payload := string(mustMarshal(request))
+ tflog.Info(ctx, "updating project", map[string]any{
+ "url": url,
+ "payload": payload,
+ })
+ err = c.doRequest(clientRequest{
+ ctx: ctx,
+ method: "PATCH",
+ url: url,
+ body: payload,
+ }, &r)
+ if err != nil {
+ return r, err
+ }
+
+ r.TeamID = c.TeamID(request.TeamID)
+ return r, err
+}
+
type UpdateProductionBranchRequest struct {
TeamID string `json:"-"`
ProjectID string `json:"-"`
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/project_secure_compute_networks.md b/docs/data-sources/project_secure_compute_networks.md
new file mode 100644
index 00000000..b330edfb
--- /dev/null
+++ b/docs/data-sources/project_secure_compute_networks.md
@@ -0,0 +1,48 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "vercel_project_secure_compute_networks Data Source - terraform-provider-vercel"
+subcategory: ""
+description: |-
+
+---
+
+# vercel_project_secure_compute_networks (Data Source)
+
+
+
+## Example Usage
+
+```terraform
+data "vercel_project" "example" {
+ name = "my-existing-project"
+}
+
+data "vercel_project_secure_compute_networks" "example" {
+ project_id = data.vercel_project.example.id
+}
+```
+
+
+## Schema
+
+### Required
+
+- `project_id` (String) The ID of the Project
+
+### Optional
+
+- `team_id` (String) The team ID. Required when configuring a team data source if a default team has not been set in the provider.
+
+### Read-Only
+
+- `secure_compute_networks` (Attributes Set) A set of Secure Compute Networks that the project should be configured with. (see [below for nested schema](#nestedatt--secure_compute_networks))
+
+
+### Nested Schema for `secure_compute_networks`
+
+Read-Only:
+
+- `builds_enabled` (Boolean) Whether the projects build container should be included in the Secure Compute Network.
+- `environment` (String) The environment being configured. Should be one of 'production', 'preview', or the ID of a Custom Environment
+- `network_id` (String) The ID of the Secure Compute Network to configure for this environment
+- `passive` (Boolean) Whether the Secure Compute Network should be configured as a passive network, meaning it is used for passive failover.
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/docs/resources/project_secure_compute_networks.md b/docs/resources/project_secure_compute_networks.md
new file mode 100644
index 00000000..da6e7ee0
--- /dev/null
+++ b/docs/resources/project_secure_compute_networks.md
@@ -0,0 +1,72 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "vercel_project_secure_compute_networks Resource - terraform-provider-vercel"
+subcategory: ""
+description: |-
+
+---
+
+# vercel_project_secure_compute_networks (Resource)
+
+
+
+## Example Usage
+
+```terraform
+resource "vercel_project" "example" {
+ name = "example-project"
+}
+
+data "vercel_secure_compute_network" "example" {
+ name = "Example Network"
+}
+
+resource "vercel_project_secure_compute_networks" "example" {
+ project_id = vercel_project.example.id
+ secure_compute_networks = [
+ {
+ environment = "production"
+ network_id = data.vercel_secure_compute_network.example.id
+ passive = false
+ builds_enabled = true
+ }
+ ]
+}
+```
+
+
+## Schema
+
+### Required
+
+- `project_id` (String) The ID of the Project
+- `secure_compute_networks` (Attributes Set) A set of Secure Compute Networks that the project should be configured with. (see [below for nested schema](#nestedatt--secure_compute_networks))
+
+### Optional
+
+- `team_id` (String) The team ID. Required when configuring a team resource if a default team has not been set in the provider.
+
+
+### Nested Schema for `secure_compute_networks`
+
+Required:
+
+- `builds_enabled` (Boolean) Whether the projects build container should be included in the Secure Compute Network.
+- `environment` (String) The environment being configured. Should be one of 'production', 'preview', or the ID of a Custom Environment
+- `network_id` (String) The ID of the Secure Compute Network to configure for this environment
+- `passive` (Boolean) Whether the Secure Compute Network should be configured as a passive network, meaning it is used for passive failover.
+
+## Import
+
+Import is supported using the following syntax:
+
+```shell
+# If importing with a team configured on the provider, simply use the project ID.
+# - project_id can be found in the project `settings` tab in the Vercel UI.
+terraform import vercel_project_secure_compute_networks.example prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
+
+# Alternatively, you can import via the team_id and project_id.
+# - team_id can be found in the team `settings` tab in the Vercel UI.
+# - project_id can be found in the project `settings` tab in the Vercel UI.
+terraform import vercel_project_secure_compute_networks.example team_xxxxxxxxxxxxxxxxxxxxxxxx/prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
+```
diff --git a/examples/data-sources/vercel_project_secure_compute_networks/data-source.tf b/examples/data-sources/vercel_project_secure_compute_networks/data-source.tf
new file mode 100644
index 00000000..a4c2e768
--- /dev/null
+++ b/examples/data-sources/vercel_project_secure_compute_networks/data-source.tf
@@ -0,0 +1,7 @@
+data "vercel_project" "example" {
+ name = "my-existing-project"
+}
+
+data "vercel_project_secure_compute_networks" "example" {
+ project_id = data.vercel_project.example.id
+}
diff --git a/examples/resources/vercel_project_secure_compute_networks/import.sh b/examples/resources/vercel_project_secure_compute_networks/import.sh
new file mode 100644
index 00000000..854ab596
--- /dev/null
+++ b/examples/resources/vercel_project_secure_compute_networks/import.sh
@@ -0,0 +1,8 @@
+# If importing with a team configured on the provider, simply use the project ID.
+# - project_id can be found in the project `settings` tab in the Vercel UI.
+terraform import vercel_project_secure_compute_networks.example prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
+
+# Alternatively, you can import via the team_id and project_id.
+# - team_id can be found in the team `settings` tab in the Vercel UI.
+# - project_id can be found in the project `settings` tab in the Vercel UI.
+terraform import vercel_project_secure_compute_networks.example team_xxxxxxxxxxxxxxxxxxxxxxxx/prj_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
diff --git a/examples/resources/vercel_project_secure_compute_networks/resource.tf b/examples/resources/vercel_project_secure_compute_networks/resource.tf
new file mode 100644
index 00000000..fa65dfe0
--- /dev/null
+++ b/examples/resources/vercel_project_secure_compute_networks/resource.tf
@@ -0,0 +1,19 @@
+resource "vercel_project" "example" {
+ name = "example-project"
+}
+
+data "vercel_secure_compute_network" "example" {
+ name = "Example Network"
+}
+
+resource "vercel_project_secure_compute_networks" "example" {
+ project_id = vercel_project.example.id
+ secure_compute_networks = [
+ {
+ environment = "production"
+ network_id = data.vercel_secure_compute_network.example.id
+ passive = false
+ builds_enabled = true
+ }
+ ]
+}
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_project_secure_compute_networks.go b/vercel/data_source_project_secure_compute_networks.go
new file mode 100644
index 00000000..7bd1c5a4
--- /dev/null
+++ b/vercel/data_source_project_secure_compute_networks.go
@@ -0,0 +1,115 @@
+package vercel
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/hashicorp/terraform-plugin-framework/datasource"
+ "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+ "github.com/vercel/terraform-provider-vercel/v3/client"
+)
+
+var (
+ _ datasource.DataSource = &projectSecureComputeNetworksDataSource{}
+ _ datasource.DataSourceWithConfigure = &projectSecureComputeNetworksDataSource{}
+)
+
+func newProjectSecureComputeNetworksDataSource() datasource.DataSource {
+ return &projectSecureComputeNetworksDataSource{}
+}
+
+type projectSecureComputeNetworksDataSource struct {
+ client *client.Client
+}
+
+func (r *projectSecureComputeNetworksDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+ resp.TypeName = req.ProviderTypeName + "_project_secure_compute_networks"
+}
+
+func (r *projectSecureComputeNetworksDataSource) 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 DataSource Configure Type",
+ fmt.Sprintf("Expected *client.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
+ )
+ return
+ }
+
+ r.client = client
+}
+
+// Schema returns the schema information for a microfrontendGroup data source.
+func (r *projectSecureComputeNetworksDataSource) Schema(_ context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+ resp.Schema = schema.Schema{
+ Description: ` `,
+ Attributes: map[string]schema.Attribute{
+ "project_id": schema.StringAttribute{
+ Description: "The ID of the Project",
+ Required: true,
+ },
+ "team_id": schema.StringAttribute{
+ Description: "The team ID. Required when configuring a team data source if a default team has not been set in the provider.",
+ Optional: true,
+ Computed: true,
+ },
+ "secure_compute_networks": schema.SetNestedAttribute{
+ Description: "A set of Secure Compute Networks that the project should be configured with.",
+ Computed: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "environment": schema.StringAttribute{
+ Description: "The environment being configured. Should be one of 'production', 'preview', or the ID of a Custom Environment",
+ Computed: true,
+ },
+ "network_id": schema.StringAttribute{
+ Description: "The ID of the Secure Compute Network to configure for this environment",
+ Computed: true,
+ },
+ "passive": schema.BoolAttribute{
+ Description: "Whether the Secure Compute Network should be configured as a passive network, meaning it is used for passive failover.",
+ Computed: true,
+ },
+ "builds_enabled": schema.BoolAttribute{
+ Description: "Whether the projects build container should be included in the Secure Compute Network.",
+ Computed: true,
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
+func (r *projectSecureComputeNetworksDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
+ var config ProjectSecureComputeNetworks
+ diags := req.Config.Get(ctx, &config)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ out, err := r.client.GetProject(ctx, config.ProjectID.ValueString(), config.TeamID.ValueString())
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error reading projject secure compute networks",
+ fmt.Sprintf("Could not get project secure compute networks %s %s, unexpected error: %s",
+ config.TeamID.ValueString(),
+ config.ProjectID.ValueString(),
+ err,
+ ),
+ )
+ return
+ }
+
+ diags = resp.State.Set(ctx, convertResponseToProjectSecureComputeNetworks(out))
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
diff --git a/vercel/data_source_project_secure_compute_networks_test.go b/vercel/data_source_project_secure_compute_networks_test.go
new file mode 100644
index 00000000..3fd8ea59
--- /dev/null
+++ b/vercel/data_source_project_secure_compute_networks_test.go
@@ -0,0 +1,57 @@
+package vercel_test
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-testing/helper/acctest"
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+)
+
+func TestAcc_ProjectSecureComputeNetworksDataSource(t *testing.T) {
+ name := acctest.RandString(16)
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
+ Steps: []resource.TestStep{
+ {
+ Config: cfg(fmt.Sprintf(`
+ resource "vercel_project" "test" {
+ name = "test-acc-project-%[1]s"
+ }
+ data "vercel_secure_compute_network" "test" {
+ name = "network 1"
+ }
+ data "vercel_secure_compute_network" "test_2" {
+ name = "network 2"
+ }
+ resource "vercel_project_secure_compute_networks" "test" {
+ project_id = vercel_project.test.id
+ secure_compute_networks = [
+ {
+ environment = "production"
+ network_id = data.vercel_secure_compute_network.test.id
+ passive = true
+ builds_enabled = false
+ },
+ {
+ environment = "preview"
+ network_id = data.vercel_secure_compute_network.test_2.id
+ passive = false
+ builds_enabled = false
+ }
+ ]
+ }
+
+ data "vercel_project_secure_compute_networks" "test" {
+ project_id = vercel_project_secure_compute_networks.test.project_id
+ }
+ `, name)),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttrSet("data.vercel_project_secure_compute_networks.test", "project_id"),
+ resource.TestCheckResourceAttrSet("data.vercel_project_secure_compute_networks.test", "team_id"),
+ resource.TestCheckResourceAttr("data.vercel_project_secure_compute_networks.test", "secure_compute_networks.#", "2"),
+ ),
+ },
+ },
+ })
+}
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..dbb6febc 100644
--- a/vercel/provider.go
+++ b/vercel/provider.go
@@ -56,29 +56,30 @@ 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,
+ newProjectSecureComputeNetworksResource,
newSharedEnvironmentVariableProjectLinkResource,
+ newSharedEnvironmentVariableResource,
newTeamConfigResource,
newTeamMemberResource,
newWebhookResource,
- newMicrofrontendGroupResource,
- newMicrofrontendGroupMembershipResource,
}
}
@@ -97,16 +98,18 @@ func (p *vercelProvider) DataSources(_ context.Context) []func() datasource.Data
newEndpointVerificationDataSource,
newFileDataSource,
newLogDrainDataSource,
+ newMicrofrontendGroupDataSource,
+ newMicrofrontendGroupMembershipDataSource,
newPrebuiltProjectDataSource,
newProjectDataSource,
newProjectDeploymentRetentionDataSource,
newProjectDirectoryDataSource,
newProjectMembersDataSource,
+ newProjectSecureComputeNetworksDataSource,
+ 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_project_secure_compute_networks.go b/vercel/resource_project_secure_compute_networks.go
new file mode 100644
index 00000000..f4b33652
--- /dev/null
+++ b/vercel/resource_project_secure_compute_networks.go
@@ -0,0 +1,332 @@
+package vercel
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/hashicorp/terraform-plugin-framework/attr"
+ "github.com/hashicorp/terraform-plugin-framework/diag"
+ "github.com/hashicorp/terraform-plugin-framework/resource"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
+ "github.com/hashicorp/terraform-plugin-framework/schema/validator"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/hashicorp/terraform-plugin-log/tflog"
+ "github.com/vercel/terraform-provider-vercel/v3/client"
+)
+
+var (
+ _ resource.Resource = &projectSecureComputeNetworksResource{}
+ _ resource.ResourceWithConfigure = &projectSecureComputeNetworksResource{}
+ _ resource.ResourceWithImportState = &projectSecureComputeNetworksResource{}
+)
+
+func newProjectSecureComputeNetworksResource() resource.Resource {
+ return &projectSecureComputeNetworksResource{}
+}
+
+type projectSecureComputeNetworksResource struct {
+ client *client.Client
+}
+
+func (r *projectSecureComputeNetworksResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
+ resp.TypeName = req.ProviderTypeName + "_project_secure_compute_networks"
+}
+
+func (r *projectSecureComputeNetworksResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.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 Resource Configure Type",
+ fmt.Sprintf("Expected *client.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
+ )
+ return
+ }
+
+ r.client = client
+}
+
+// Schema returns the schema information for a microfrontendGroup resource.
+func (r *projectSecureComputeNetworksResource) Schema(_ context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
+ resp.Schema = schema.Schema{
+ Description: ` `,
+ Attributes: map[string]schema.Attribute{
+ "project_id": schema.StringAttribute{
+ Description: "The ID of the Project",
+ Required: true,
+ PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplace()},
+ },
+ "team_id": schema.StringAttribute{
+ Description: "The team ID. Required when configuring a team resource if a default team has not been set in the provider.",
+ Optional: true,
+ Computed: true,
+ PlanModifiers: []planmodifier.String{stringplanmodifier.RequiresReplaceIfConfigured(), stringplanmodifier.UseStateForUnknown()},
+ },
+ "secure_compute_networks": schema.SetNestedAttribute{
+ Description: "A set of Secure Compute Networks that the project should be configured with.",
+ Required: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "environment": schema.StringAttribute{
+ Description: "The environment being configured. Should be one of 'production', 'preview', or the ID of a Custom Environment",
+ Required: true,
+ },
+ "network_id": schema.StringAttribute{
+ Description: "The ID of the Secure Compute Network to configure for this environment",
+ Required: true,
+ },
+ "passive": schema.BoolAttribute{
+ Description: "Whether the Secure Compute Network should be configured as a passive network, meaning it is used for passive failover.",
+ Required: true,
+ },
+ "builds_enabled": schema.BoolAttribute{
+ Description: "Whether the projects build container should be included in the Secure Compute Network.",
+ Required: true,
+ },
+ },
+ },
+ Validators: []validator.Set{
+ NewPassiveBuildsEnabledValidator(),
+ },
+ },
+ },
+ }
+}
+
+type ProjectSecureComputeNetwork struct {
+ Environment types.String `tfsdk:"environment"`
+ NetworkID types.String `tfsdk:"network_id"`
+ Passive types.Bool `tfsdk:"passive"`
+ BuildsEnabled types.Bool `tfsdk:"builds_enabled"`
+}
+
+var projectSecureComputeNetworkElemType = types.ObjectType{
+ AttrTypes: map[string]attr.Type{
+ "environment": types.StringType,
+ "network_id": types.StringType,
+ "passive": types.BoolType,
+ "builds_enabled": types.BoolType,
+ },
+}
+
+type ProjectSecureComputeNetworks struct {
+ TeamID types.String `tfsdk:"team_id"`
+ ProjectID types.String `tfsdk:"project_id"`
+ SecureComputeNetworks types.Set `tfsdk:"secure_compute_networks"`
+}
+
+func (p ProjectSecureComputeNetworks) toUpdateProjectSecureComputeNetworksRequest(ctx context.Context) (client.UpdateProjectSecureComputeNetworksRequest, diag.Diagnostics) {
+ var networks []ProjectSecureComputeNetwork
+ diags := p.SecureComputeNetworks.ElementsAs(ctx, &networks, false)
+ scNetworks := make([]client.ConnectConfigurationRequest, 0, len(networks))
+ for _, n := range networks {
+ scNetworks = append(scNetworks, client.ConnectConfigurationRequest{
+ Environment: n.Environment.ValueString(),
+ ConnectConfigurationID: n.NetworkID.ValueString(),
+ Passive: n.Passive.ValueBool(),
+ BuildsEnabled: n.BuildsEnabled.ValueBool(),
+ })
+ }
+ return client.UpdateProjectSecureComputeNetworksRequest{
+ TeamID: p.TeamID.ValueString(),
+ ProjectID: p.ProjectID.ValueString(),
+ SecureComputeNetworks: scNetworks,
+ }, diags
+}
+
+func convertResponseToProjectSecureComputeNetworks(response client.ProjectResponse) ProjectSecureComputeNetworks {
+ networks := make([]attr.Value, 0, len(response.ConnectConfigurations))
+ for _, n := range response.ConnectConfigurations {
+ networks = append(networks, types.ObjectValueMust(projectSecureComputeNetworkElemType.AttrTypes, map[string]attr.Value{
+ "environment": types.StringValue(n.Environment),
+ "network_id": types.StringValue(n.ConnectConfigurationID),
+ "passive": types.BoolValue(n.Passive),
+ "builds_enabled": types.BoolValue(n.BuildsEnabled),
+ }))
+ }
+
+ return ProjectSecureComputeNetworks{
+ TeamID: types.StringValue(response.TeamID),
+ ProjectID: types.StringValue(response.ID),
+ SecureComputeNetworks: types.SetValueMust(projectSecureComputeNetworkElemType, networks),
+ }
+}
+
+func (r *projectSecureComputeNetworksResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
+ var plan ProjectSecureComputeNetworks
+ diags := req.Plan.Get(ctx, &plan)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ resp.Diagnostics.AddError(
+ "Error getting project secure compute networks plan",
+ "Error getting project secure compute networks plan",
+ )
+ return
+ }
+
+ tflog.Info(ctx, "creating project secure compute networks", map[string]any{
+ "team_id": plan.TeamID.ValueString(),
+ "project_id": plan.ProjectID.ValueString(),
+ })
+
+ request, diags := plan.toUpdateProjectSecureComputeNetworksRequest(ctx)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+ out, err := r.client.UpdateProjectSecureComputeNetworks(ctx, request)
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error creating project secure compute networks",
+ "Could not create project secure compute networks, unexpected error: "+err.Error(),
+ )
+ return
+ }
+
+ tflog.Info(ctx, "created secure compute networks", map[string]any{
+ "team_id": plan.TeamID.ValueString(),
+ "project_id": plan.ProjectID.ValueString(),
+ })
+
+ diags = resp.State.Set(ctx, convertResponseToProjectSecureComputeNetworks(out))
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+func (r *projectSecureComputeNetworksResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
+ var state ProjectSecureComputeNetworks
+ diags := req.State.Get(ctx, &state)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ out, err := r.client.GetProject(ctx, state.ProjectID.ValueString(), state.TeamID.ValueString())
+ if client.NotFound(err) {
+ resp.State.RemoveResource(ctx)
+ return
+ }
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error reading projject secure compute networks",
+ fmt.Sprintf("Could not get project secure compute networks %s %s, unexpected error: %s",
+ state.TeamID.ValueString(),
+ state.ProjectID.ValueString(),
+ err,
+ ),
+ )
+ return
+ }
+
+ diags = resp.State.Set(ctx, convertResponseToProjectSecureComputeNetworks(out))
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+func (r *projectSecureComputeNetworksResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
+ var plan ProjectSecureComputeNetworks
+ diags := req.Plan.Get(ctx, &plan)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ resp.Diagnostics.AddError(
+ "Error getting project secure compute networks plan",
+ "Error getting project secure compute networks plan",
+ )
+ return
+ }
+
+ request, diags := plan.toUpdateProjectSecureComputeNetworksRequest(ctx)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+ out, err := r.client.UpdateProjectSecureComputeNetworks(ctx, request)
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error updating project secure compute networks",
+ "Could not update project secure compute networks, unexpected error: "+err.Error(),
+ )
+ return
+ }
+
+ tflog.Info(ctx, "created secure compute networks", map[string]any{
+ "team_id": plan.TeamID.ValueString(),
+ "project_id": plan.ProjectID.ValueString(),
+ })
+
+ diags = resp.State.Set(ctx, convertResponseToProjectSecureComputeNetworks(out))
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
+
+func (r *projectSecureComputeNetworksResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
+ var state ProjectSecureComputeNetworks
+ diags := req.State.Get(ctx, &state)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ tflog.Info(ctx, "deleting secure compute networks", map[string]any{
+ "project_id": state.ProjectID.ValueString(),
+ "team_id": state.TeamID.ValueString(),
+ })
+
+ _, err := r.client.UpdateProjectSecureComputeNetworks(ctx, client.UpdateProjectSecureComputeNetworksRequest{
+ TeamID: state.TeamID.ValueString(),
+ ProjectID: state.ProjectID.ValueString(),
+ SecureComputeNetworks: nil,
+ })
+
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error deleting project secure compute networks",
+ "Could not delete project secure compute networks, unexpected error: "+err.Error(),
+ )
+ return
+ }
+}
+
+func (r *projectSecureComputeNetworksResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
+ teamID, projectID, ok := splitInto1Or2(req.ID)
+ if !ok {
+ resp.Diagnostics.AddError(
+ "Error importing Project Secure Compute Networks",
+ fmt.Sprintf("Invalid id '%s' specified. should be in format \"team_id/project_id\" or \"project_id\"", req.ID),
+ )
+ }
+ out, err := r.client.GetProject(ctx, projectID, teamID)
+ if client.NotFound(err) {
+ resp.State.RemoveResource(ctx)
+ return
+ }
+ if err != nil {
+ resp.Diagnostics.AddError(
+ "Error reading project secure compute networks",
+ fmt.Sprintf("Could not get project secure compute networks %s %s, unexpected error: %s",
+ teamID,
+ projectID,
+ err,
+ ),
+ )
+ return
+ }
+
+ diags := resp.State.Set(ctx, convertResponseToProjectSecureComputeNetworks(out))
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+}
diff --git a/vercel/resource_project_secure_compute_networks_test.go b/vercel/resource_project_secure_compute_networks_test.go
new file mode 100644
index 00000000..5a83ebd2
--- /dev/null
+++ b/vercel/resource_project_secure_compute_networks_test.go
@@ -0,0 +1,149 @@
+package vercel_test
+
+import (
+ "context"
+ "fmt"
+ "regexp"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-testing/helper/acctest"
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+ "github.com/hashicorp/terraform-plugin-testing/terraform"
+ "github.com/vercel/terraform-provider-vercel/v3/client"
+)
+
+func testCheckProjectSecureComputeNetworksDeleted(testClient *client.Client, n, teamID string) resource.TestCheckFunc {
+ return func(s *terraform.State) error {
+ rs, ok := s.RootModule().Resources[n]
+ if !ok {
+ return fmt.Errorf("not found: %s", n)
+ }
+
+ if rs.Primary.ID == "" {
+ return fmt.Errorf("no project ID is set")
+ }
+
+ project, err := testClient.GetProject(context.TODO(), rs.Primary.ID, teamID)
+ if err != nil {
+ return fmt.Errorf("unexpected error %w", err)
+ }
+
+ if len(project.ConnectConfigurations) > 0 {
+ return fmt.Errorf("expected no connect configurations, but got %d", len(project.ConnectConfigurations))
+ }
+
+ return nil
+ }
+}
+
+func TestAcc_ProjectSecureComputeNetworksResource(t *testing.T) {
+ name := acctest.RandString(16)
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: testAccProtoV6ProviderFactories,
+ Steps: []resource.TestStep{
+ {
+ Config: cfg(fmt.Sprintf(`
+ resource "vercel_project" "test" {
+ name = "test-acc-project-%[1]s"
+ }
+ data "vercel_secure_compute_network" "test" {
+ name = "network 1"
+ }
+ resource "vercel_project_secure_compute_networks" "test" {
+ project_id = vercel_project.test.id
+ secure_compute_networks = [
+ {
+ environment = "production"
+ network_id = data.vercel_secure_compute_network.test.id
+ passive = true
+ builds_enabled = true
+ }
+ ]
+ }
+ `, name)),
+ ExpectError: regexp.MustCompile("builds_enabled cannot be `true` if passive is `true`"),
+ },
+ {
+ Config: cfg(fmt.Sprintf(`
+ resource "vercel_project" "test" {
+ name = "test-acc-project-%[1]s"
+ }
+ data "vercel_secure_compute_network" "test" {
+ name = "network 1"
+ }
+ resource "vercel_project_secure_compute_networks" "test" {
+ project_id = vercel_project.test.id
+ secure_compute_networks = [
+ {
+ environment = "production"
+ network_id = data.vercel_secure_compute_network.test.id
+ passive = false
+ builds_enabled = true
+ }
+ ]
+ }
+ `, name)),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttrSet("vercel_project_secure_compute_networks.test", "project_id"),
+ resource.TestCheckResourceAttr("vercel_project_secure_compute_networks.test", "secure_compute_networks.#", "1"),
+ resource.TestCheckResourceAttr("vercel_project_secure_compute_networks.test", "secure_compute_networks.0.environment", "production"),
+ resource.TestCheckResourceAttrSet("vercel_project_secure_compute_networks.test", "secure_compute_networks.0.network_id"),
+ resource.TestCheckResourceAttr("vercel_project_secure_compute_networks.test", "secure_compute_networks.0.passive", "false"),
+ resource.TestCheckResourceAttr("vercel_project_secure_compute_networks.test", "secure_compute_networks.0.builds_enabled", "true"),
+ ),
+ },
+ {
+ Config: cfg(fmt.Sprintf(`
+ resource "vercel_project" "test" {
+ name = "test-acc-project-%[1]s"
+ }
+ data "vercel_secure_compute_network" "test" {
+ name = "network 1"
+ }
+ data "vercel_secure_compute_network" "test_2" {
+ name = "network 2"
+ }
+ resource "vercel_project_secure_compute_networks" "test" {
+ project_id = vercel_project.test.id
+ secure_compute_networks = [
+ {
+ environment = "preview"
+ network_id = data.vercel_secure_compute_network.test.id
+ passive = true
+ builds_enabled = false
+ },
+ {
+ environment = "production"
+ network_id = data.vercel_secure_compute_network.test_2.id
+ passive = true
+ builds_enabled = false
+ },
+ ]
+ }
+ `, name)),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttrSet("vercel_project_secure_compute_networks.test", "project_id"),
+ resource.TestCheckResourceAttr("vercel_project_secure_compute_networks.test", "secure_compute_networks.#", "2"),
+ resource.TestCheckResourceAttrSet("vercel_project_secure_compute_networks.test", "secure_compute_networks.0.environment"),
+ resource.TestCheckResourceAttrSet("vercel_project_secure_compute_networks.test", "secure_compute_networks.0.network_id"),
+ resource.TestCheckResourceAttr("vercel_project_secure_compute_networks.test", "secure_compute_networks.0.passive", "true"),
+ resource.TestCheckResourceAttr("vercel_project_secure_compute_networks.test", "secure_compute_networks.0.builds_enabled", "false"),
+ resource.TestCheckResourceAttrSet("vercel_project_secure_compute_networks.test", "secure_compute_networks.1.environment"),
+ resource.TestCheckResourceAttrSet("vercel_project_secure_compute_networks.test", "secure_compute_networks.1.network_id"),
+ resource.TestCheckResourceAttr("vercel_project_secure_compute_networks.test", "secure_compute_networks.1.passive", "true"),
+ resource.TestCheckResourceAttr("vercel_project_secure_compute_networks.test", "secure_compute_networks.1.builds_enabled", "false"),
+ ),
+ },
+ {
+ Config: cfg(fmt.Sprintf(`
+ resource "vercel_project" "test" {
+ name = "test-acc-project-%[1]s"
+ }
+ `, name)),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ testCheckProjectSecureComputeNetworksDeleted(testClient(t), "vercel_project.test", testTeam(t)),
+ ),
+ },
+ },
+ })
+}
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_passive_builds_enabled.go b/vercel/validator_passive_builds_enabled.go
new file mode 100644
index 00000000..a6dfd86b
--- /dev/null
+++ b/vercel/validator_passive_builds_enabled.go
@@ -0,0 +1,42 @@
+package vercel
+
+import (
+ "context"
+
+ "github.com/hashicorp/terraform-plugin-framework/schema/validator"
+)
+
+// passiveBuildsEnabledValidator is a validator that ensures builds_enabled is false if passive is true.
+type passiveBuildsEnabledValidator struct{}
+
+func (v passiveBuildsEnabledValidator) Description(_ context.Context) string {
+ return "builds_enabled cannot be true if passive is true"
+}
+
+func (v passiveBuildsEnabledValidator) MarkdownDescription(_ context.Context) string {
+ return "builds_enabled cannot be true if passive is true"
+}
+
+func (v passiveBuildsEnabledValidator) ValidateSet(ctx context.Context, req validator.SetRequest, resp *validator.SetResponse) {
+ // Iterate through each element in the set
+ var networks []ProjectSecureComputeNetwork
+ diags := req.ConfigValue.ElementsAs(ctx, &networks, false)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+ for _, network := range networks {
+ // Check the condition
+ if network.Passive.ValueBool() && network.BuildsEnabled.ValueBool() {
+ resp.Diagnostics.AddError(
+ "Invalid Secure Compute Network Configuration",
+ "builds_enabled cannot be `true` if passive is `true`.",
+ )
+ }
+ }
+}
+
+// NewPassiveBuildsEnabledValidator returns a validator that ensures builds_enabled is false if passive is true.
+func NewPassiveBuildsEnabledValidator() validator.Set {
+ return passiveBuildsEnabledValidator{}
+}
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{}{}
}