diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ceb80fb5..7b66e9fe 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -39,6 +39,23 @@ jobs: run: task build - name: Format run: task lint + + docs: + name: Docs Test + timeout-minutes: 5 + runs-on: ubuntu-latest + steps: + - name: Set up Go + uses: actions/setup-go@v2 + with: + go-version: "1.20" + id: go + - name: Install Task + uses: arduino/setup-task@v1 + with: + version: "3.x" + - name: Check out code into the Go module directory + uses: actions/checkout@v2 - name: Check if docs are up-to-date run: | task docs @@ -48,7 +65,7 @@ jobs: test: name: Matrix Test - needs: [ build ] + needs: [build] timeout-minutes: 15 strategy: max-parallel: 1 @@ -80,7 +97,7 @@ jobs: env: TF_ACC: "true" VERCEL_API_TOKEN: ${{ secrets.VERCEL_API_TOKEN }} - VERCEL_TERRAFORM_TESTING_TEAM: "team_Q8MH2GVRnqKLtxicP2HWPgLi" + VERCEL_TERRAFORM_TESTING_TEAM: "team_RvxIb1z0pi9RSsQ13p3ES4cK" VERCEL_TERRAFORM_TESTING_GITHUB_REPO: "dglsparsons/test" VERCEL_TERRAFORM_TESTING_GITLAB_REPO: "dglsparsons/test" VERCEL_TERRAFORM_TESTING_BITBUCKET_REPO: "dglsparsons-test/test" diff --git a/README.md b/README.md index 545c2633..82e05b0d 100644 --- a/README.md +++ b/README.md @@ -23,12 +23,30 @@ If you wish to work on the provider, you'll first need [Go](http://www.golang.or To compile the provider, run `task build`. This will build the provider and put the provider binary in the repository root. -In addition, you can run `task install` to set up a developer overrides in your ~/.terraformrc. This will then allow you -to use your locally built provider binary. +```sh +$ task build +``` + +In addition, you can run `task install` to set up a developer overrides in your ~/.terraformrc. This will then allow you to use your locally built provider binary. + +```sh +$ task install +``` + +Create a `main.tf` file on your machine and use the [terraform cli](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli#install-terraform) to test + +```sh +$ terraform plan +$ terraform apply +``` When you are finished using a local version of the provider, running `task uninstall` will remove _all_ developer overrides. +```sh +$ task uninstall +``` + - HashiCorp - [Development Overrides for Provider developers](https://www.terraform.io/docs/cli/config/config-file.html#development-overrides-for-provider-developers). ## Testing @@ -42,6 +60,8 @@ The acceptance tests require a few environment variables to be set: * `VERCEL_TERRAFORM_TESTING_TEAM` - a Vercel team_id where resources can be created and destroyed * `VERCEL_TERRAFORM_TESTING_GITHUB_REPO` - a GitHub repository in the form 'org/repo' that can be used to trigger deployments * `VERCEL_TERRAFORM_TESTING_BITBUCKET_REPO` - a Bitbucket repository in the form 'project/repo' that can be used to trigger deployments +* `VERCEL_TERRAFORM_TESTING_GITLAB_REPO` - a GitLab repository in the form 'project/repo' that can be used to trigger deployments +* `VERCEL_TERRAFORM_TESTING_DOMAIN` - a Vercel testing domain that can be used for testing ```sh $ task test @@ -59,6 +79,7 @@ To run a specific set of tests, use the `-run` flag and specify a regex pattern $ task test -- -run 'TestAcc_Project*' ``` + ## Building The Documentation The documentation is autogenerated from Description fields within the provider, and the `examples` directory. diff --git a/Taskfile.yml b/Taskfile.yml index 076a96b9..a46cb24c 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -23,7 +23,7 @@ tasks: install-tfplugindocs: desc: "Install the tfplugindocs tool" cmds: - - go install github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs@v0.7.0 + - go install github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs@v0.16.0 status: - which tfplugindocs diff --git a/client/deployment_protection.go b/client/deployment_protection.go new file mode 100644 index 00000000..eba07053 --- /dev/null +++ b/client/deployment_protection.go @@ -0,0 +1,28 @@ +package client + +type VercelAuthentication struct { + DeploymentType string `json:"deploymentType"` +} + +type PasswordProtection struct { + DeploymentType string `json:"deploymentType"` +} +type PasswordProtectionWithPassword struct { + DeploymentType string `json:"deploymentType"` + Password string `json:"password"` +} + +type TrustedIpAddress struct { + Value string `json:"value"` + Note string `json:"note"` +} + +type TrustedIps struct { + DeploymentType string `json:"deploymentType"` + Addresses []TrustedIpAddress `json:"addresses"` + ProtectionMode string `json:"protectionMode"` +} + +type ProtectionBypass struct { + Scope string `json:"scope"` +} diff --git a/client/must_marshal.go b/client/must_marshal.go index 27f85dec..d4989fd5 100644 --- a/client/must_marshal.go +++ b/client/must_marshal.go @@ -1,6 +1,20 @@ package client -import "encoding/json" +import ( + "encoding/json" +) + +func (v *VercelAuthentication) MarshalJSON() ([]byte, error) { + if v.DeploymentType == "none" { + return []byte(`null`), nil + } + + return json.Marshal(&struct { + DeploymentType string `json:"deploymentType"` + }{ + DeploymentType: v.DeploymentType, + }) +} // mustMarshal is a helper to remove unnecessary error checking when marshaling a Go // struct to json. There are only a few instances where marshaling can fail, and they diff --git a/client/project_get.go b/client/project_get.go index 874637cc..b82c69c5 100644 --- a/client/project_get.go +++ b/client/project_get.go @@ -54,14 +54,6 @@ func (r *ProjectResponse) Repository() *Repository { return nil } -type Protection struct { - DeploymentType string `json:"deploymentType"` -} - -type ProtectionBypass struct { - Scope string `json:"scope"` -} - // ProjectResponse defines the information Vercel returns about a project. type ProjectResponse struct { BuildCommand *string `json:"buildCommand"` @@ -92,8 +84,9 @@ type ProjectResponse struct { PublicSource *bool `json:"publicSource"` RootDirectory *string `json:"rootDirectory"` ServerlessFunctionRegion *string `json:"serverlessFunctionRegion"` - SSOProtection *Protection `json:"ssoProtection"` - PasswordProtection *Protection `json:"passwordProtection"` + VercelAuthentication *VercelAuthentication `json:"ssoProtection"` + PasswordProtection *PasswordProtection `json:"passwordProtection"` + TrustedIps *TrustedIps `json:"trustedIps"` ProtectionBypass map[string]ProtectionBypass `json:"protectionBypass"` } diff --git a/client/project_update.go b/client/project_update.go index 95f2e19d..b77a43dd 100644 --- a/client/project_update.go +++ b/client/project_update.go @@ -7,11 +7,6 @@ import ( "github.com/hashicorp/terraform-plugin-log/tflog" ) -type PasswordProtectionRequest struct { - DeploymentType string `json:"deploymentType"` - Password string `json:"password"` -} - // UpdateProjectRequest defines the possible fields that can be updated within a vercel project. // note that the values are all pointers, with many containing `omitempty` for serialisation. // This is because the Vercel API behaves in the following manner: @@ -19,18 +14,19 @@ type PasswordProtectionRequest struct { // - setting the field to an empty value (e.g. "") will remove the setting for that field. // - omitting the value entirely from the request will _not_ update the field. type UpdateProjectRequest struct { - BuildCommand *string `json:"buildCommand"` - CommandForIgnoringBuildStep *string `json:"commandForIgnoringBuildStep"` - DevCommand *string `json:"devCommand"` - Framework *string `json:"framework"` - InstallCommand *string `json:"installCommand"` - Name *string `json:"name,omitempty"` - OutputDirectory *string `json:"outputDirectory"` - PublicSource *bool `json:"publicSource"` - RootDirectory *string `json:"rootDirectory"` - ServerlessFunctionRegion *string `json:"serverlessFunctionRegion"` - SSOProtection *Protection `json:"ssoProtection"` - PasswordProtection *PasswordProtectionRequest `json:"passwordProtection"` + BuildCommand *string `json:"buildCommand"` + CommandForIgnoringBuildStep *string `json:"commandForIgnoringBuildStep"` + DevCommand *string `json:"devCommand"` + Framework *string `json:"framework"` + InstallCommand *string `json:"installCommand"` + Name *string `json:"name,omitempty"` + OutputDirectory *string `json:"outputDirectory"` + PublicSource *bool `json:"publicSource"` + RootDirectory *string `json:"rootDirectory"` + ServerlessFunctionRegion *string `json:"serverlessFunctionRegion"` + VercelAuthentication *VercelAuthentication `json:"ssoProtection"` + PasswordProtection *PasswordProtectionWithPassword `json:"passwordProtection"` + TrustedIps *TrustedIps `json:"trustedIps"` } // UpdateProject updates an existing projects configuration within Vercel. diff --git a/docs/data-sources/alias.md b/docs/data-sources/alias.md index 3b7c7fa6..ec7e0b3e 100644 --- a/docs/data-sources/alias.md +++ b/docs/data-sources/alias.md @@ -30,5 +30,3 @@ An Alias allows a `vercel_deployment` to be accessed through a different URL. - `deployment_id` (String) The ID of the Deployment the Alias is associated with. - `id` (String) The ID of this resource. - - diff --git a/docs/data-sources/file.md b/docs/data-sources/file.md index 9ba4a959..06843acc 100644 --- a/docs/data-sources/file.md +++ b/docs/data-sources/file.md @@ -44,5 +44,3 @@ resource "vercel_deployment" "example" { - `file` (Map of String) A map of filename to metadata about the file. The metadata contains the file size and hash, and allows a deployment to be created if the file changes. - `id` (String) The ID of this resource. - - diff --git a/docs/data-sources/prebuilt_project.md b/docs/data-sources/prebuilt_project.md index 32e316eb..e335951d 100644 --- a/docs/data-sources/prebuilt_project.md +++ b/docs/data-sources/prebuilt_project.md @@ -65,5 +65,3 @@ resource "vercel_deployment" "example" { - `id` (String) The ID of this resource. - `output` (Map of String) A map of output file to metadata about the file. The metadata contains the file size and hash, and allows a deployment to be created if the file changes. - - diff --git a/docs/data-sources/project.md b/docs/data-sources/project.md index cc165bdb..c7dbec3c 100644 --- a/docs/data-sources/project.md +++ b/docs/data-sources/project.md @@ -38,7 +38,6 @@ output "project_id" { ### Optional -- `password_protection` (Attributes) Ensures visitors of your Preview Deployments must enter a password in order to gain access. (see [below for nested schema](#nestedatt--password_protection)) - `team_id` (String) The team ID the project exists beneath. Required when configuring a team resource if a default team has not been set in the provider. ### Read-Only @@ -52,19 +51,13 @@ output "project_id" { - `ignore_command` (String) When a commit is pushed to the Git repository that is connected with your Project, its SHA will determine if a new Build has to be issued. If the SHA was deployed before, no new Build will be issued. You can customize this behavior with a command that exits with code 1 (new Build needed) or code 0. - `install_command` (String) The install command for this project. If omitted, this value will be automatically detected. - `output_directory` (String) The output directory of the project. When null is used this value will be automatically detected. +- `password_protection` (Attributes) Ensures visitors of your Preview Deployments must enter a password in order to gain access. (see [below for nested schema](#nestedatt--password_protection)) - `public_source` (Boolean) Specifies whether the source code and logs of the deployments for this project should be public or not. - `root_directory` (String) The name of a directory or relative path to the source code of your project. When null is used it will default to the project root. - `serverless_function_region` (String) The region on Vercel's network to which your Serverless Functions are deployed. It should be close to any data source your Serverless Function might depend on. A new Deployment is required for your changes to take effect. Please see [Vercel's documentation](https://vercel.com/docs/concepts/edge-network/regions) for a full list of regions. +- `trusted_ips` (Attributes) Ensures only visitors from an allowed IP address can access your deployment. (see [below for nested schema](#nestedatt--trusted_ips)) - `vercel_authentication` (Attributes) Ensures visitors to your Preview Deployments are logged into Vercel and have a minimum of Viewer access on your team. (see [below for nested schema](#nestedatt--vercel_authentication)) - -### Nested Schema for `password_protection` - -Optional: - -- `protect_production` (Boolean) If true, production deployments will also be protected - - ### Nested Schema for `environment` @@ -87,11 +80,36 @@ Read-Only: - `type` (String) The git provider of the repository. Must be either `github`, `gitlab`, or `bitbucket`. - -### Nested Schema for `vercel_authentication` + +### Nested Schema for `password_protection` Read-Only: -- `protect_production` (Boolean) If true, production deployments will also be protected +- `deployment_type` (String) The deployment environment that will be protected. + + + +### Nested Schema for `trusted_ips` +Read-Only: + +- `addresses` (List of Object) The allowed IP addressses and CIDR ranges with optional descriptions. (see [below for nested schema](#nestedatt--trusted_ips--addresses)) +- `deployment_type` (String) The deployment environment that will be protected. +- `protection_mode` (String) Whether or not Trusted IPs is required or optional to access a deployment. + + +### Nested Schema for `trusted_ips.addresses` + +Read-Only: + +- `note` (String) +- `value` (String) + + + + +### Nested Schema for `vercel_authentication` + +Read-Only: +- `deployment_type` (String) The deployment environment that will be protected. diff --git a/docs/data-sources/project_directory.md b/docs/data-sources/project_directory.md index eed9655f..751a708c 100644 --- a/docs/data-sources/project_directory.md +++ b/docs/data-sources/project_directory.md @@ -58,5 +58,3 @@ resource "vercel_deployment" "example" { - `files` (Map of String) A map of filename to metadata about the file. The metadata contains the file size and hash, and allows a deployment to be created if the file changes. - `id` (String) The ID of this resource. - - diff --git a/docs/index.md b/docs/index.md index 7a517e1a..f872eb00 100644 --- a/docs/index.md +++ b/docs/index.md @@ -25,7 +25,7 @@ terraform { required_providers { vercel = { source = "vercel/vercel" - version = "~> 0.4" + version = "~> 1.0" } } } diff --git a/docs/resources/alias.md b/docs/resources/alias.md index 62b03922..1a8d9cbf 100644 --- a/docs/resources/alias.md +++ b/docs/resources/alias.md @@ -30,5 +30,3 @@ An Alias allows a `vercel_deployment` to be accessed through a different URL. ### Read-Only - `id` (String) The ID of this resource. - - diff --git a/docs/resources/deployment.md b/docs/resources/deployment.md index 92399b46..629a5940 100644 --- a/docs/resources/deployment.md +++ b/docs/resources/deployment.md @@ -129,5 +129,3 @@ Optional: - `install_command` (String) The install command for this deployment. If omitted, this value will be taken from the project or automatically detected. - `output_directory` (String) The output directory of the deployment. If omitted, this value will be taken from the project or automatically detected. - `root_directory` (String) The name of a directory or relative path to the source code of your project. When null is used it will default to the project root. - - diff --git a/docs/resources/dns_record.md b/docs/resources/dns_record.md index 88a8e821..171e937f 100644 --- a/docs/resources/dns_record.md +++ b/docs/resources/dns_record.md @@ -124,7 +124,7 @@ For 'TXT' records, this can contain arbitrary text. ### Nested Schema for `srv` -Optional: +Required: - `port` (Number) The TCP or UDP port on which the service is to be found. - `priority` (Number) The priority of the target host, lower value means more preferred. diff --git a/docs/resources/project.md b/docs/resources/project.md index 069a86cd..dabfb28e 100644 --- a/docs/resources/project.md +++ b/docs/resources/project.md @@ -69,6 +69,7 @@ resource "vercel_project" "example" { - `root_directory` (String) The name of a directory or relative path to the source code of your project. If omitted, it will default to the project root. - `serverless_function_region` (String) The region on Vercel's network to which your Serverless Functions are deployed. It should be close to any data source your Serverless Function might depend on. A new Deployment is required for your changes to take effect. Please see [Vercel's documentation](https://vercel.com/docs/concepts/edge-network/regions) for a full list of regions. - `team_id` (String) The team ID to add the project to. Required when configuring a team resource if a default team has not been set in the provider. +- `trusted_ips` (Attributes) Ensures only visitors from an allowed IP address can access your deployment. (see [below for nested schema](#nestedatt--trusted_ips)) - `vercel_authentication` (Attributes) Ensures visitors to your Preview Deployments are logged into Vercel and have a minimum of Viewer access on your team. (see [below for nested schema](#nestedatt--vercel_authentication)) ### Read-Only @@ -79,40 +80,74 @@ resource "vercel_project" "example" { ### Nested Schema for `environment` -Optional: +Required: -- `git_branch` (String) The git branch of the Environment Variable. -- `id` (String) The ID of the Environment Variable. - `key` (String) The name of the Environment Variable. - `target` (Set of String) The environments that the Environment Variable should be present on. Valid targets are either `production`, `preview`, or `development`. - `value` (String, Sensitive) The value of the Environment Variable. +Optional: + +- `git_branch` (String) The git branch of the Environment Variable. + +Read-Only: + +- `id` (String) The ID of the Environment Variable. + ### Nested Schema for `git_repository` -Optional: +Required: -- `production_branch` (String) By default, every commit pushed to the main branch will trigger a Production Deployment instead of the usual Preview Deployment. You can switch to a different branch here. - `repo` (String) The name of the git repository. For example: `vercel/next.js`. - `type` (String) The git provider of the repository. Must be either `github`, `gitlab`, or `bitbucket`. +Optional: + +- `production_branch` (String) By default, every commit pushed to the main branch will trigger a Production Deployment instead of the usual Preview Deployment. You can switch to a different branch here. + ### Nested Schema for `password_protection` -Optional: +Required: +- `deployment_type` (String) The deployment environment to protect. Must be one of `standard_protection`, `all_deployments`, or `only_preview_deployments`. - `password` (String, Sensitive) The password that visitors must enter to gain access to your Preview Deployments. Drift detection is not possible for this field. -- `protect_production` (Boolean) If true, production deployments will also be protected + + + +### Nested Schema for `trusted_ips` + +Required: + +- `addresses` (Attributes Set) The allowed IP addressses and CIDR ranges with optional descriptions. (see [below for nested schema](#nestedatt--trusted_ips--addresses)) +- `deployment_type` (String) The deployment environment to protect. Must be one of `standard_protection`, `all_deployments`, `only_production_deployments`, or `only_preview_deployments`. + +Optional: + +- `protection_mode` (String) Whether or not Trusted IPs is optional to access a deployment. Must be either `trusted_ip_required` or `trusted_ip_optional`. `trusted_ip_optional` is only available with Standalone Trusted IPs. + + +### Nested Schema for `trusted_ips.addresses` + +Required: + +- `value` (String, Sensitive) The address or CIDR range that can access deployments. + +Optional: + +- `note` (String) A description for the value + ### Nested Schema for `vercel_authentication` -Optional: +Required: -- `protect_production` (Boolean) If true, production deployments will also be protected +- `deployment_type` (String) The deployment environment to protect. Must be one of `standard_protection`, `all_deployments`, `only_preview_deployments`, or `none`. ## Import diff --git a/examples/provider/provider.tf b/examples/provider/provider.tf index ce63b5fc..a82ab0fb 100644 --- a/examples/provider/provider.tf +++ b/examples/provider/provider.tf @@ -5,7 +5,7 @@ terraform { required_providers { vercel = { source = "vercel/vercel" - version = "~> 0.4" + version = "~> 1.0" } } } diff --git a/vercel/data_source_project.go b/vercel/data_source_project.go index 2c6bb7b4..ff92a180 100644 --- a/vercel/data_source_project.go +++ b/vercel/data_source_project.go @@ -5,6 +5,7 @@ import ( "fmt" "regexp" + "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/schema/validator" @@ -149,18 +150,42 @@ For more detailed information, please see the [Vercel documentation](https://ver Description: "Ensures visitors to your Preview Deployments are logged into Vercel and have a minimum of Viewer access on your team.", Computed: true, Attributes: map[string]schema.Attribute{ - "protect_production": schema.BoolAttribute{ - Description: "If true, production deployments will also be protected", + "deployment_type": schema.StringAttribute{ + Description: "The deployment environment that will be protected.", Computed: true, }, }, }, "password_protection": schema.SingleNestedAttribute{ Description: "Ensures visitors of your Preview Deployments must enter a password in order to gain access.", - Optional: true, + Computed: true, Attributes: map[string]schema.Attribute{ - "protect_production": schema.BoolAttribute{ - Description: "If true, production deployments will also be protected", + "deployment_type": schema.StringAttribute{ + Description: "The deployment environment that will be protected.", + Computed: true, + }, + }, + }, + "trusted_ips": schema.SingleNestedAttribute{ + Description: "Ensures only visitors from an allowed IP address can access your deployment.", + Computed: true, + Attributes: map[string]schema.Attribute{ + "deployment_type": schema.StringAttribute{ + Description: "The deployment environment that will be protected.", + Computed: true, + }, + "addresses": schema.ListAttribute{ + Description: "The allowed IP addressses and CIDR ranges with optional descriptions.", + Computed: true, + ElementType: types.ObjectType{ + AttrTypes: map[string]attr.Type{ + "value": types.StringType, + "note": types.StringType, + }, + }, + }, + "protection_mode": schema.StringAttribute{ + Description: "Whether or not Trusted IPs is required or optional to access a deployment.", Computed: true, }, }, diff --git a/vercel/data_source_project_model.go b/vercel/data_source_project_model.go index 451807cb..6e2d780f 100644 --- a/vercel/data_source_project_model.go +++ b/vercel/data_source_project_model.go @@ -7,35 +7,32 @@ import ( // Project reflects the state terraform stores internally for a project. type ProjectDataSource struct { - BuildCommand types.String `tfsdk:"build_command"` - DevCommand types.String `tfsdk:"dev_command"` - Environment types.Set `tfsdk:"environment"` - Framework types.String `tfsdk:"framework"` - GitRepository *GitRepository `tfsdk:"git_repository"` - ID types.String `tfsdk:"id"` - IgnoreCommand types.String `tfsdk:"ignore_command"` - InstallCommand types.String `tfsdk:"install_command"` - Name types.String `tfsdk:"name"` - OutputDirectory types.String `tfsdk:"output_directory"` - PublicSource types.Bool `tfsdk:"public_source"` - RootDirectory types.String `tfsdk:"root_directory"` - ServerlessFunctionRegion types.String `tfsdk:"serverless_function_region"` - TeamID types.String `tfsdk:"team_id"` - VercelAuthentication *VercelAuthentication `tfsdk:"vercel_authentication"` - PasswordProtection *PasswordProtectionDataSource `tfsdk:"password_protection"` -} - -type PasswordProtectionDataSource struct { - ProtectProduction types.Bool `tfsdk:"protect_production"` + BuildCommand types.String `tfsdk:"build_command"` + DevCommand types.String `tfsdk:"dev_command"` + Environment types.Set `tfsdk:"environment"` + Framework types.String `tfsdk:"framework"` + GitRepository *GitRepository `tfsdk:"git_repository"` + ID types.String `tfsdk:"id"` + IgnoreCommand types.String `tfsdk:"ignore_command"` + InstallCommand types.String `tfsdk:"install_command"` + Name types.String `tfsdk:"name"` + OutputDirectory types.String `tfsdk:"output_directory"` + PublicSource types.Bool `tfsdk:"public_source"` + RootDirectory types.String `tfsdk:"root_directory"` + ServerlessFunctionRegion types.String `tfsdk:"serverless_function_region"` + TeamID types.String `tfsdk:"team_id"` + VercelAuthentication *VercelAuthentication `tfsdk:"vercel_authentication"` + PasswordProtection *PasswordProtection `tfsdk:"password_protection"` + TrustedIps *TrustedIps `tfsdk:"trusted_ips"` } func convertResponseToProjectDataSource(response client.ProjectResponse, plan Project) ProjectDataSource { project := convertResponseToProject(response, plan) - var pp *PasswordProtectionDataSource + var pp *PasswordProtection if project.PasswordProtection != nil { - pp = &PasswordProtectionDataSource{ - ProtectProduction: project.PasswordProtection.ProtectProduction, + pp = &PasswordProtection{ + DeploymentType: project.PasswordProtection.DeploymentType, } } return ProjectDataSource{ @@ -55,5 +52,6 @@ func convertResponseToProjectDataSource(response client.ProjectResponse, plan Pr TeamID: project.TeamID, VercelAuthentication: project.VercelAuthentication, PasswordProtection: pp, + TrustedIps: project.TrustedIps, } } diff --git a/vercel/data_source_project_test.go b/vercel/data_source_project_test.go index b199f751..e107a21a 100644 --- a/vercel/data_source_project_test.go +++ b/vercel/data_source_project_test.go @@ -25,8 +25,16 @@ func TestAcc_ProjectDataSource(t *testing.T) { resource.TestCheckResourceAttr("data.vercel_project.test", "output_directory", ".output"), resource.TestCheckResourceAttr("data.vercel_project.test", "public_source", "true"), resource.TestCheckResourceAttr("data.vercel_project.test", "root_directory", "ui/src"), - resource.TestCheckResourceAttr("data.vercel_project.test", "vercel_authentication.protect_production", "true"), - resource.TestCheckResourceAttr("data.vercel_project.test", "password_protection.protect_production", "true"), + resource.TestCheckResourceAttr("data.vercel_project.test", "vercel_authentication.deployment_type", "standard_protection"), + resource.TestCheckResourceAttr("data.vercel_project.test", "password_protection.deployment_type", "standard_protection"), + resource.TestCheckResourceAttr("data.vercel_project.test", "trusted_ips.addresses.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs("data.vercel_project.test", "trusted_ips.addresses.*", map[string]string{ + "value": "1.1.1.1", + "note": "notey note", + }), + resource.TestCheckResourceAttr("data.vercel_project.test", "trusted_ips.deployment_type", "only_production_deployments"), + resource.TestCheckResourceAttr("data.vercel_project.test", "trusted_ips.protection_mode", "trusted_ip_required"), + resource.TestCheckTypeSetElemNestedAttrs("data.vercel_project.test", "environment.*", map[string]string{ "key": "foo", "value": "bar", @@ -50,11 +58,21 @@ resource "vercel_project" "test" { public_source = true root_directory = "ui/src" vercel_authentication = { - protect_production = true + deployment_type = "standard_protection" } password_protection = { password = "foo" - protect_production = true + deployment_type = "standard_protection" + } + trusted_ips = { + addresses = [ + { + value = "1.1.1.1" + note = "notey note" + } + ] + deployment_type = "only_production_deployments" + protection_mode = "trusted_ip_required" } %s environment = [ diff --git a/vercel/deployment_protection.go b/vercel/deployment_protection.go new file mode 100644 index 00000000..6bb8f354 --- /dev/null +++ b/vercel/deployment_protection.go @@ -0,0 +1,30 @@ +package vercel + +import "github.com/hashicorp/terraform-plugin-framework/types" + +type VercelAuthentication struct { + DeploymentType types.String `tfsdk:"deployment_type"` +} + +type PasswordProtection struct { + DeploymentType types.String `tfsdk:"deployment_type"` +} +type PasswordProtectionWithPassword struct { + DeploymentType types.String `tfsdk:"deployment_type"` + Password types.String `tfsdk:"password"` +} + +type TrustedIpAddress struct { + Value types.String `tfsdk:"value"` + Note types.String `tfsdk:"note"` +} + +type TrustedIps struct { + DeploymentType types.String `tfsdk:"deployment_type"` + Addresses []TrustedIpAddress `tfsdk:"addresses"` + ProtectionMode types.String `tfsdk:"protection_mode"` +} + +type ProtectionBypass struct { + Scope types.String `tfsdk:"scope"` +} diff --git a/vercel/resource_project.go b/vercel/resource_project.go index 2dc4d810..bb1d79f1 100644 --- a/vercel/resource_project.go +++ b/vercel/resource_project.go @@ -9,10 +9,11 @@ import ( "github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" - "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectdefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/objectplanmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/setplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringdefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" @@ -174,23 +175,26 @@ At this time you cannot use a Vercel Project resource with in-line ` + "`environ }, }, "vercel_authentication": schema.SingleNestedAttribute{ - Description: "Ensures visitors to your Preview Deployments are logged into Vercel and have a minimum of Viewer access on your team.", - Optional: true, - Computed: true, + Description: "Ensures visitors to your Preview Deployments are logged into Vercel and have a minimum of Viewer access on your team.", + Optional: true, + Computed: true, + PlanModifiers: []planmodifier.Object{objectplanmodifier.UseStateForUnknown()}, Default: objectdefault.StaticValue(types.ObjectValueMust( map[string]attr.Type{ - "protect_production": types.BoolType, + "deployment_type": types.StringType, }, map[string]attr.Value{ - "protect_production": types.BoolValue(false), + "deployment_type": types.StringValue("standard_protection"), }, )), Attributes: map[string]schema.Attribute{ - "protect_production": schema.BoolAttribute{ - Description: "If true, production deployments will also be protected", - Optional: true, - Computed: true, - Default: booldefault.StaticBool(false), + "deployment_type": schema.StringAttribute{ + Required: true, + Description: "The deployment environment to protect. Must be one of `standard_protection`, `all_deployments`, `only_preview_deployments`, or `none`.", + PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, + Validators: []validator.String{ + stringOneOf("standard_protection", "all_deployments", "only_preview_deployments", "none"), + }, }, }, }, @@ -206,11 +210,58 @@ At this time you cannot use a Vercel Project resource with in-line ` + "`environ stringLengthBetween(1, 72), }, }, - "protect_production": schema.BoolAttribute{ - Description: "If true, production deployments will also be protected", - Optional: true, - Computed: true, - Default: booldefault.StaticBool(false), + "deployment_type": schema.StringAttribute{ + Required: true, + Description: "The deployment environment to protect. Must be one of `standard_protection`, `all_deployments`, or `only_preview_deployments`.", + PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, + Validators: []validator.String{ + stringOneOf("standard_protection", "all_deployments", "only_preview_deployments"), + }, + }, + }, + }, + "trusted_ips": schema.SingleNestedAttribute{ + Description: "Ensures only visitors from an allowed IP address can access your deployment.", + Optional: true, + Attributes: map[string]schema.Attribute{ + "addresses": schema.SetNestedAttribute{ + Description: "The allowed IP addressses and CIDR ranges with optional descriptions.", + Required: true, + PlanModifiers: []planmodifier.Set{setplanmodifier.UseStateForUnknown()}, + NestedObject: schema.NestedAttributeObject{ + Attributes: map[string]schema.Attribute{ + "value": schema.StringAttribute{ + Description: "The address or CIDR range that can access deployments.", + Required: true, + Sensitive: true, + }, + "note": schema.StringAttribute{ + Description: "A description for the value", + Optional: true, + }, + }, + }, + Validators: []validator.Set{ + stringSetMinCount(1), + }, + }, + "deployment_type": schema.StringAttribute{ + Required: true, + Description: "The deployment environment to protect. Must be one of `standard_protection`, `all_deployments`, `only_production_deployments`, or `only_preview_deployments`.", + PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, + Validators: []validator.String{ + stringOneOf("standard_protection", "all_deployments", "only_production_deployments", "only_preview_deployments"), + }, + }, + "protection_mode": schema.StringAttribute{ + Optional: true, + Computed: true, + Default: stringdefault.StaticString("trusted_ip_required"), + Description: "Whether or not Trusted IPs is optional to access a deployment. Must be either `trusted_ip_required` or `trusted_ip_optional`. `trusted_ip_optional` is only available with Standalone Trusted IPs.", + PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()}, + Validators: []validator.String{ + stringOneOf("trusted_ip_required", "trusted_ip_optional"), + }, }, }, }, @@ -285,7 +336,7 @@ func (r *projectResource) Create(ctx context.Context, req resource.CreateRequest return } - if plan.PasswordProtection != nil || plan.VercelAuthentication != nil { + if plan.PasswordProtection != nil || plan.VercelAuthentication != nil || plan.TrustedIps != nil { out, err = r.client.UpdateProject(ctx, result.ID.ValueString(), plan.TeamID.ValueString(), plan.toUpdateProjectRequest(plan.Name.ValueString()), !plan.Environment.IsNull()) if err != nil { resp.Diagnostics.AddError( diff --git a/vercel/resource_project_model.go b/vercel/resource_project_model.go index 911cd31c..ae4d8669 100644 --- a/vercel/resource_project_model.go +++ b/vercel/resource_project_model.go @@ -11,24 +11,25 @@ import ( // Project reflects the state terraform stores internally for a project. type Project struct { - BuildCommand types.String `tfsdk:"build_command"` - DevCommand types.String `tfsdk:"dev_command"` - Environment types.Set `tfsdk:"environment"` - Framework types.String `tfsdk:"framework"` - GitRepository *GitRepository `tfsdk:"git_repository"` - ID types.String `tfsdk:"id"` - IgnoreCommand types.String `tfsdk:"ignore_command"` - InstallCommand types.String `tfsdk:"install_command"` - Name types.String `tfsdk:"name"` - OutputDirectory types.String `tfsdk:"output_directory"` - PublicSource types.Bool `tfsdk:"public_source"` - RootDirectory types.String `tfsdk:"root_directory"` - ServerlessFunctionRegion types.String `tfsdk:"serverless_function_region"` - TeamID types.String `tfsdk:"team_id"` - VercelAuthentication *VercelAuthentication `tfsdk:"vercel_authentication"` - PasswordProtection *PasswordProtection `tfsdk:"password_protection"` - ProtectionBypassForAutomation types.Bool `tfsdk:"protection_bypass_for_automation"` - ProtectionBypassForAutomationSecret types.String `tfsdk:"protection_bypass_for_automation_secret"` + BuildCommand types.String `tfsdk:"build_command"` + DevCommand types.String `tfsdk:"dev_command"` + Environment types.Set `tfsdk:"environment"` + Framework types.String `tfsdk:"framework"` + GitRepository *GitRepository `tfsdk:"git_repository"` + ID types.String `tfsdk:"id"` + IgnoreCommand types.String `tfsdk:"ignore_command"` + InstallCommand types.String `tfsdk:"install_command"` + Name types.String `tfsdk:"name"` + OutputDirectory types.String `tfsdk:"output_directory"` + PublicSource types.Bool `tfsdk:"public_source"` + RootDirectory types.String `tfsdk:"root_directory"` + ServerlessFunctionRegion types.String `tfsdk:"serverless_function_region"` + TeamID types.String `tfsdk:"team_id"` + VercelAuthentication *VercelAuthentication `tfsdk:"vercel_authentication"` + PasswordProtection *PasswordProtectionWithPassword `tfsdk:"password_protection"` + TrustedIps *TrustedIps `tfsdk:"trusted_ips"` + ProtectionBypassForAutomation types.Bool `tfsdk:"protection_bypass_for_automation"` + ProtectionBypassForAutomationSecret types.String `tfsdk:"protection_bypass_for_automation_secret"` } var nullProject = Project{ @@ -109,7 +110,8 @@ func (p *Project) toUpdateProjectRequest(oldName string) client.UpdateProjectReq RootDirectory: toStrPointer(p.RootDirectory), ServerlessFunctionRegion: toStrPointer(p.ServerlessFunctionRegion), PasswordProtection: p.PasswordProtection.toUpdateProjectRequest(), - SSOProtection: p.VercelAuthentication.toUpdateProjectRequest(), + VercelAuthentication: p.VercelAuthentication.toUpdateProjectRequest(), + TrustedIps: p.TrustedIps.toUpdateProjectRequest(), } } @@ -153,43 +155,96 @@ func (g *GitRepository) toCreateProjectRequest() *client.GitRepository { } } -type VercelAuthentication struct { - ProtectProduction types.Bool `tfsdk:"protect_production"` +func toApiDeploymentProtectionType(dt types.String) string { + switch dt { + case types.StringValue("standard_protection"): + return "prod_deployment_urls_and_all_previews" + case types.StringValue("all_deployments"): + return "all" + case types.StringValue("only_preview_deployments"): + return "preview" + case types.StringValue("only_production_deployments"): + return "production" + default: + return dt.ValueString() + } +} + +func fromApiDeploymentProtectionType(dt string) types.String { + switch dt { + case "prod_deployment_urls_and_all_previews": + return types.StringValue("standard_protection") + case "all": + return types.StringValue("all_deployments") + case "preview": + return types.StringValue("only_preview_deployments") + case "production": + return types.StringValue("only_production_deployments") + default: + return types.StringValue(dt) + } } -func (v *VercelAuthentication) toUpdateProjectRequest() *client.Protection { +func (v *VercelAuthentication) toUpdateProjectRequest() *client.VercelAuthentication { if v == nil { return nil } - deploymentType := "preview" - if v.ProtectProduction.ValueBool() { - deploymentType = "all" + return &client.VercelAuthentication{ + DeploymentType: toApiDeploymentProtectionType(v.DeploymentType), + } +} + +func (p *PasswordProtectionWithPassword) toUpdateProjectRequest() *client.PasswordProtectionWithPassword { + if p == nil { + return nil + } + + return &client.PasswordProtectionWithPassword{ + DeploymentType: toApiDeploymentProtectionType(p.DeploymentType), + Password: p.Password.ValueString(), } +} - return &client.Protection{ - DeploymentType: deploymentType, +func toApiTrustedIpProtectionMode(dt types.String) string { + switch dt { + case types.StringValue("trusted_ip_required"): + return "additional" + case types.StringValue("trusted_ip_optional"): + return "exclusive" + default: + return dt.ValueString() } } -type PasswordProtection struct { - Password types.String `tfsdk:"password"` - ProtectProduction types.Bool `tfsdk:"protect_production"` +func fromApiTrustedIpProtectionMode(dt string) types.String { + switch dt { + case "additional": + return types.StringValue("trusted_ip_required") + case "exclusive": + return types.StringValue("trusted_ip_optional") + default: + return types.StringValue(dt) + } } -func (p *PasswordProtection) toUpdateProjectRequest() *client.PasswordProtectionRequest { - if p == nil { +func (t *TrustedIps) toUpdateProjectRequest() *client.TrustedIps { + if t == nil { return nil } - deploymentType := "preview" - if p.ProtectProduction.ValueBool() { - deploymentType = "all" + var addresses = []client.TrustedIpAddress{} + for _, address := range t.Addresses { + addresses = append(addresses, client.TrustedIpAddress{ + Value: address.Value.ValueString(), + Note: address.Note.ValueString(), + }) } - return &client.PasswordProtectionRequest{ - DeploymentType: deploymentType, - Password: p.Password.ValueString(), + return &client.TrustedIps{ + Addresses: addresses, + DeploymentType: toApiDeploymentProtectionType(t.DeploymentType), + ProtectionMode: toApiTrustedIpProtectionMode(t.ProtectionMode), } } @@ -266,22 +321,40 @@ func convertResponseToProject(response client.ProjectResponse, plan Project) Pro } } - var pp *PasswordProtection + var pp *PasswordProtectionWithPassword if response.PasswordProtection != nil { pass := types.StringValue("") if plan.PasswordProtection != nil { pass = plan.PasswordProtection.Password } - pp = &PasswordProtection{ - Password: pass, - ProtectProduction: types.BoolValue(response.PasswordProtection.DeploymentType == "all"), + pp = &PasswordProtectionWithPassword{ + Password: pass, + DeploymentType: fromApiDeploymentProtectionType(response.PasswordProtection.DeploymentType), } } - var va *VercelAuthentication - if response.SSOProtection != nil { + var va *VercelAuthentication = &VercelAuthentication{ + DeploymentType: types.StringValue("none"), + } + if response.VercelAuthentication != nil { va = &VercelAuthentication{ - ProtectProduction: types.BoolValue(response.SSOProtection.DeploymentType == "all"), + DeploymentType: fromApiDeploymentProtectionType(response.VercelAuthentication.DeploymentType), + } + } + + var tip *TrustedIps + if response.TrustedIps != nil { + var addresses []TrustedIpAddress + for _, address := range response.TrustedIps.Addresses { + addresses = append(addresses, TrustedIpAddress{ + Value: types.StringValue(address.Value), + Note: types.StringValue(address.Note), + }) + } + tip = &TrustedIps{ + DeploymentType: fromApiDeploymentProtectionType(response.TrustedIps.DeploymentType), + Addresses: addresses, + ProtectionMode: fromApiTrustedIpProtectionMode(response.TrustedIps.ProtectionMode), } } @@ -346,6 +419,7 @@ func convertResponseToProject(response client.ProjectResponse, plan Project) Pro TeamID: toTeamID(response.TeamID), PasswordProtection: pp, VercelAuthentication: va, + TrustedIps: tip, ProtectionBypassForAutomation: protectionBypass, ProtectionBypassForAutomationSecret: protectionBypassSecret, } diff --git a/vercel/resource_project_test.go b/vercel/resource_project_test.go index 41964115..11a7a3da 100644 --- a/vercel/resource_project_test.go +++ b/vercel/resource_project_test.go @@ -146,7 +146,7 @@ func TestAcc_ProjectWithGitRepository(t *testing.T) { }) } -func TestAcc_ProjectWithSSOAndPasswordProtection(t *testing.T) { +func TestAcc_ProjectWithVercelAuthAndPasswordProtectionAndTrustedIps(t *testing.T) { projectSuffix := acctest.RandString(16) resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -154,44 +154,74 @@ func TestAcc_ProjectWithSSOAndPasswordProtection(t *testing.T) { CheckDestroy: testAccProjectDestroy("vercel_project.enabled_to_start", testTeam()), Steps: []resource.TestStep{ { - Config: testAccProjectConfigWithSSOAndPassword(projectSuffix, teamIDConfig()), + Config: testAccProjectConfigWithVercelAuthAndPasswordAndTrustedIps(projectSuffix, teamIDConfig()), Check: resource.ComposeAggregateTestCheckFunc( testAccProjectExists("vercel_project.enabled_to_start", testTeam()), - resource.TestCheckResourceAttr("vercel_project.enabled_to_start", "vercel_authentication.protect_production", "true"), - resource.TestCheckResourceAttr("vercel_project.enabled_to_start", "password_protection.protect_production", "true"), + resource.TestCheckResourceAttr("vercel_project.enabled_to_start", "vercel_authentication.deployment_type", "all_deployments"), + resource.TestCheckResourceAttr("vercel_project.enabled_to_start", "password_protection.deployment_type", "all_deployments"), resource.TestCheckResourceAttr("vercel_project.enabled_to_start", "password_protection.password", "password"), + resource.TestCheckResourceAttr("vercel_project.enabled_to_start", "trusted_ips.addresses.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs("vercel_project.enabled_to_start", "trusted_ips.addresses.*", map[string]string{ + "value": "1.1.1.1", + "note": "notey note", + }), + resource.TestCheckResourceAttr("vercel_project.enabled_to_start", "trusted_ips.deployment_type", "all_deployments"), + resource.TestCheckResourceAttr("vercel_project.enabled_to_start", "trusted_ips.protection_mode", "trusted_ip_optional"), resource.TestCheckResourceAttr("vercel_project.enabled_to_start", "protection_bypass_for_automation", "true"), resource.TestCheckResourceAttrSet("vercel_project.enabled_to_start", "protection_bypass_for_automation_secret"), testAccProjectExists("vercel_project.disabled_to_start", testTeam()), - resource.TestCheckNoResourceAttr("vercel_project.disabled_to_start", "vercel_authentication"), + resource.TestCheckResourceAttr("vercel_project.disabled_to_start", "vercel_authentication.deployment_type", "standard_protection"), resource.TestCheckNoResourceAttr("vercel_project.disabled_to_start", "password_protection"), + resource.TestCheckNoResourceAttr("vercel_project.disabled_to_start", "trusted_ips"), resource.TestCheckNoResourceAttr("vercel_project.disabled_to_start", "protection_bypass_for_automation"), resource.TestCheckNoResourceAttr("vercel_project.disabled_to_start", "protection_bypass_for_automation_secret"), testAccProjectExists("vercel_project.enabled_to_update", testTeam()), - resource.TestCheckResourceAttr("vercel_project.enabled_to_update", "vercel_authentication.protect_production", "false"), - resource.TestCheckResourceAttr("vercel_project.enabled_to_update", "password_protection.protect_production", "false"), + resource.TestCheckResourceAttr("vercel_project.enabled_to_update", "vercel_authentication.deployment_type", "only_preview_deployments"), + resource.TestCheckResourceAttr("vercel_project.enabled_to_update", "password_protection.deployment_type", "only_preview_deployments"), resource.TestCheckResourceAttr("vercel_project.enabled_to_update", "password_protection.password", "password"), + resource.TestCheckResourceAttr("vercel_project.enabled_to_update", "trusted_ips.addresses.#", "2"), + resource.TestCheckTypeSetElemNestedAttrs("vercel_project.enabled_to_update", "trusted_ips.addresses.*", map[string]string{ + "value": "1.1.1.3", + "note": "notey notey note", + }), + resource.TestCheckResourceAttr("vercel_project.enabled_to_update", "trusted_ips.deployment_type", "only_production_deployments"), + resource.TestCheckResourceAttr("vercel_project.enabled_to_update", "trusted_ips.protection_mode", "trusted_ip_required"), resource.TestCheckResourceAttr("vercel_project.enabled_to_update", "protection_bypass_for_automation", "true"), resource.TestCheckResourceAttrSet("vercel_project.enabled_to_update", "protection_bypass_for_automation_secret"), ), }, { - Config: testAccProjectConfigWithSSOAndPasswordUpdated(projectSuffix, teamIDConfig()), + Config: testAccProjectConfigWithVercelAuthAndPasswordAndTrustedIpsUpdated(projectSuffix, teamIDConfig()), Check: resource.ComposeAggregateTestCheckFunc( - resource.TestCheckNoResourceAttr("vercel_project.enabled_to_start", "vercel_authentication"), + resource.TestCheckResourceAttr("vercel_project.enabled_to_start", "vercel_authentication.deployment_type", "standard_protection"), resource.TestCheckNoResourceAttr("vercel_project.enabled_to_start", "password_protection"), resource.TestCheckNoResourceAttr("vercel_project.enabled_to_start", "protection_bypass_for_automation"), + resource.TestCheckNoResourceAttr("vercel_project.enabled_to_start", "trusted_ips"), resource.TestCheckNoResourceAttr("vercel_project.enabled_to_start", "protection_bypass_for_automation_secret"), - resource.TestCheckResourceAttr("vercel_project.disabled_to_start", "vercel_authentication.protect_production", "true"), - resource.TestCheckResourceAttr("vercel_project.disabled_to_start", "password_protection.protect_production", "true"), + resource.TestCheckResourceAttr("vercel_project.disabled_to_start", "vercel_authentication.deployment_type", "standard_protection"), + resource.TestCheckResourceAttr("vercel_project.disabled_to_start", "password_protection.deployment_type", "standard_protection"), resource.TestCheckResourceAttr("vercel_project.disabled_to_start", "password_protection.password", "password"), + resource.TestCheckResourceAttr("vercel_project.disabled_to_start", "trusted_ips.addresses.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs("vercel_project.disabled_to_start", "trusted_ips.addresses.*", map[string]string{ + "value": "1.1.1.1", + "note": "notey note", + }), + resource.TestCheckResourceAttr("vercel_project.disabled_to_start", "trusted_ips.deployment_type", "standard_protection"), + resource.TestCheckResourceAttr("vercel_project.disabled_to_start", "trusted_ips.protection_mode", "trusted_ip_required"), resource.TestCheckResourceAttr("vercel_project.disabled_to_start", "protection_bypass_for_automation", "true"), resource.TestCheckResourceAttrSet("vercel_project.disabled_to_start", "protection_bypass_for_automation_secret"), - resource.TestCheckResourceAttr("vercel_project.enabled_to_update", "vercel_authentication.protect_production", "true"), - resource.TestCheckResourceAttr("vercel_project.enabled_to_update", "password_protection.protect_production", "true"), + resource.TestCheckResourceAttr("vercel_project.enabled_to_update", "vercel_authentication.deployment_type", "standard_protection"), + resource.TestCheckResourceAttr("vercel_project.enabled_to_update", "password_protection.deployment_type", "standard_protection"), resource.TestCheckResourceAttr("vercel_project.enabled_to_update", "password_protection.password", "password2"), + resource.TestCheckResourceAttr("vercel_project.enabled_to_update", "trusted_ips.addresses.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs("vercel_project.enabled_to_update", "trusted_ips.addresses.*", map[string]string{ + "value": "1.1.1.3", + "note": "notey notey", + }), + resource.TestCheckResourceAttr("vercel_project.enabled_to_update", "trusted_ips.deployment_type", "all_deployments"), + resource.TestCheckResourceAttr("vercel_project.enabled_to_update", "trusted_ips.protection_mode", "trusted_ip_optional"), resource.TestCheckResourceAttr("vercel_project.enabled_to_update", "protection_bypass_for_automation", "false"), resource.TestCheckNoResourceAttr("vercel_project.enabled_to_update", "protection_bypass_for_automation_secret"), ), @@ -330,18 +360,28 @@ resource "vercel_project" "test" { `, projectSuffix, teamID) } -func testAccProjectConfigWithSSOAndPassword(projectSuffix, teamID string) string { +func testAccProjectConfigWithVercelAuthAndPasswordAndTrustedIps(projectSuffix, teamID string) string { return fmt.Sprintf(` resource "vercel_project" "enabled_to_start" { name = "test-acc-protection-one-%[1]s" %[2]s vercel_authentication = { - protect_production = true + deployment_type = "all_deployments" } password_protection = { - protect_production = true + deployment_type = "all_deployments" password = "password" } + trusted_ips = { + addresses = [ + { + value = "1.1.1.1" + note = "notey note" + } + ] + deployment_type = "all_deployments" + protection_mode = "trusted_ip_optional" + } protection_bypass_for_automation = true } @@ -354,18 +394,31 @@ resource "vercel_project" "enabled_to_update" { name = "test-acc-protection-three-%[1]s" %[2]s vercel_authentication = { - protect_production = false + deployment_type = "only_preview_deployments" } password_protection = { - protect_production = false + deployment_type = "only_preview_deployments" password = "password" } + trusted_ips = { + addresses = [ + { + value = "1.1.1.1" + note = "notey notey" + }, + { + value = "1.1.1.3" + note = "notey notey note" + } + ] + deployment_type = "only_production_deployments" + } protection_bypass_for_automation = true } `, projectSuffix, teamID) } -func testAccProjectConfigWithSSOAndPasswordUpdated(projectSuffix, teamID string) string { +func testAccProjectConfigWithVercelAuthAndPasswordAndTrustedIpsUpdated(projectSuffix, teamID string) string { return fmt.Sprintf(` resource "vercel_project" "enabled_to_start" { name = "test-acc-protection-one-%[1]s" @@ -376,12 +429,21 @@ resource "vercel_project" "disabled_to_start" { name = "test-acc-protection-two-%[1]s" %[2]s vercel_authentication = { - protect_production = true + deployment_type = "standard_protection" } password_protection = { - protect_production = true + deployment_type = "standard_protection" password = "password" } + trusted_ips = { + addresses = [ + { + value = "1.1.1.1" + note = "notey note" + } + ] + deployment_type = "standard_protection" + } protection_bypass_for_automation = true } @@ -389,12 +451,22 @@ resource "vercel_project" "enabled_to_update" { name = "test-acc-protection-three-%[1]s" %[2]s vercel_authentication = { - protect_production = true + deployment_type = "standard_protection" } password_protection = { - protect_production = true + deployment_type = "standard_protection" password = "password2" } + trusted_ips = { + addresses = [ + { + value = "1.1.1.3" + note = "notey notey" + } + ] + deployment_type = "all_deployments" + protection_mode = "trusted_ip_optional" + } protection_bypass_for_automation = false } `, projectSuffix, teamID)