From ffd3a5e13c5b7d9fc4698539f7c778f6f685f4e6 Mon Sep 17 00:00:00 2001 From: Micah Martin Date: Fri, 8 Oct 2021 11:59:51 -0400 Subject: [PATCH] Refactored usage scopes --- docs/data-sources/encrypted_text.md | 9 +- docs/data-sources/secret_manager.md | 9 +- docs/guides/usage-scopes.md | 58 ++++++++++ docs/resources/cloudprovider_aws.md | 9 +- docs/resources/cloudprovider_datacenter.md | 9 +- docs/resources/cloudprovider_gcp.md | 9 +- docs/resources/cloudprovider_kubernetes.md | 9 +- docs/resources/encrypted_text.md | 11 +- docs/resources/git_connector.md | 9 +- docs/resources/ssh_credential.md | 9 +- .../harness_encrypted_text/resource.tf | 2 - examples/usage_scope/complex.tf | 25 +++++ examples/usage_scope/simple.tf | 12 +++ .../data_source_encrypted_text_test.go | 2 - .../resource_cloudprovider_datacenter_test.go | 2 - .../resource_cloudprovider_gcp_test.go | 2 - .../resource_cloudprovider_kubernetes_test.go | 68 +++++++++++- .../provider/resource_encrypted_text_test.go | 6 -- .../provider/resource_git_connector_test.go | 3 - .../provider/resource_ssh_credential_test.go | 4 - internal/provider/usage_scope.go | 102 +++++++++++------- internal/provider/usage_scopes_test.go | 1 - templates/guides/usage-scopes.md.tmpl | 19 ++++ 23 files changed, 278 insertions(+), 111 deletions(-) create mode 100644 docs/guides/usage-scopes.md create mode 100644 examples/usage_scope/complex.tf create mode 100644 examples/usage_scope/simple.tf create mode 100644 templates/guides/usage-scopes.md.tmpl diff --git a/docs/data-sources/encrypted_text.md b/docs/data-sources/encrypted_text.md index edc1d62a7..2d6382f86 100644 --- a/docs/data-sources/encrypted_text.md +++ b/docs/data-sources/encrypted_text.md @@ -19,7 +19,7 @@ Data source for retrieving a Harness application - **id** (String) Unique identifier of the encrypted secret - **name** (String) The name of the encrypted secret -- **usage_scope** (Block Set) Usage scopes (see [below for nested schema](#nestedblock--usage_scope)) +- **usage_scope** (Block Set) This block is used for scoping the resource to a specific set of applications or environments. (see [below for nested schema](#nestedblock--usage_scope)) ### Read-Only @@ -30,9 +30,8 @@ Data source for retrieving a Harness application Optional: -- **application_filter_type** (String) Type of application filter applied. ALL if not application id supplied, otherwise NULL -- **application_id** (String) Id of the application scoping -- **environment_filter_type** (String) Type of environment filter applied. ALL if not filter applied -- **environment_id** (String) Id of the environment scoping +- **application_id** (String) Id of the application to scope to. If empty then this scope applies to all applications. +- **environment_filter_type** (String) Type of environment filter applied. Cannot be used with `environment_id`. Valid options are NON_PRODUCTION_ENVIRONMENTS, PRODUCTION_ENVIRONMENTS. +- **environment_id** (String) Id of the id of the specific environment to scope to. Cannot be used with `environment_filter_type`. diff --git a/docs/data-sources/secret_manager.md b/docs/data-sources/secret_manager.md index f1eef6a7b..e1c63051e 100644 --- a/docs/data-sources/secret_manager.md +++ b/docs/data-sources/secret_manager.md @@ -20,16 +20,15 @@ Data source for retrieving a Harness secret manager - **default** (Boolean) True to lookup the id of the default secret manager - **id** (String) Unique identifier of the secret manager - **name** (String) The name of the secret manager -- **usage_scope** (Block Set) Usage scopes (see [below for nested schema](#nestedblock--usage_scope)) +- **usage_scope** (Block Set) This block is used for scoping the resource to a specific set of applications or environments. (see [below for nested schema](#nestedblock--usage_scope)) ### Nested Schema for `usage_scope` Optional: -- **application_filter_type** (String) Type of application filter applied. ALL if not application id supplied, otherwise NULL -- **application_id** (String) Id of the application scoping -- **environment_filter_type** (String) Type of environment filter applied. ALL if not filter applied -- **environment_id** (String) Id of the environment scoping +- **application_id** (String) Id of the application to scope to. If empty then this scope applies to all applications. +- **environment_filter_type** (String) Type of environment filter applied. Cannot be used with `environment_id`. Valid options are NON_PRODUCTION_ENVIRONMENTS, PRODUCTION_ENVIRONMENTS. +- **environment_id** (String) Id of the id of the specific environment to scope to. Cannot be used with `environment_filter_type`. diff --git a/docs/guides/usage-scopes.md b/docs/guides/usage-scopes.md new file mode 100644 index 000000000..420844abe --- /dev/null +++ b/docs/guides/usage-scopes.md @@ -0,0 +1,58 @@ +--- +subcategory: "" +page_title: "Configuring usage scopes - Harness Provider" +description: |- + An example of how to apply usage scopes to a resource. +--- + +# Configure usage scopes for a resource + +There are a number of resources that can be scoped to a specific set of applications and environments. These include cloud providers, secrets, connectors, and more. Configuring it is the same across all of these resources. + +In this example we are configuring a cloud provider to be used by any application in any environment. + +```terraform +resource "harness_cloudprovider_kubernetes" "test" { + name = "test" + + usage_scope { + environment_filter_type = "NON_PRODUCTION_ENVIRONMENTS" + } + + usage_scope { + environment_filter_type = "PRODUCTION_ENVIRONMENTS" + } + +} +``` + +In this more advanced scenario we show how you can scope the cloud provider to a specific application or to a specific environment. + +```terraform +resource "harness_application" "example" { + name = "myapp" +} + +resource "harness_environment" "qa" { + name = "qa" + app_id = harness_application.example.id + type = "NON_PROD" +} + +resource "harness_cloudprovider_kubernetes" "k8s" { + name = "k8s" + + // Example of scoping to all non-prod environments of a specific application + usage_scope { + application_id = harness_application.example.id + environment_filter_type = "NON_PRODUCTION_ENVIRONMENTS" + } + + // Example of scoping to a specific environment + usage_scope { + application_id = harness_application.example.id + environment_id = harness_environment.qa.id + } +} +``` + diff --git a/docs/resources/cloudprovider_aws.md b/docs/resources/cloudprovider_aws.md index 619f1179e..0689e59c1 100644 --- a/docs/resources/cloudprovider_aws.md +++ b/docs/resources/cloudprovider_aws.md @@ -50,7 +50,7 @@ resource "harness_cloudprovider_aws" "aws" { - **assume_cross_account_role** (Block List, Max: 1) Configuration for assuming a cross account role. (see [below for nested schema](#nestedblock--assume_cross_account_role)) - **delegate_selector** (String) Select the Delegate to use via one of its Selectors. - **secret_access_key_secret_name** (String) The name of the Harness secret containing the AWS secret access key. -- **usage_scope** (Block Set) Usage scopes (see [below for nested schema](#nestedblock--usage_scope)) +- **usage_scope** (Block Set) This block is used for scoping the resource to a specific set of applications or environments. (see [below for nested schema](#nestedblock--usage_scope)) - **use_ec2_iam_credentials** (Boolean) Use the EC2 Instance Profile for Service Accounts. - **use_irsa** (Boolean) Use the AWS IAM Role for Service Accounts. @@ -75,10 +75,9 @@ Optional: Optional: -- **application_filter_type** (String) Type of application filter applied. ALL if not application id supplied, otherwise NULL -- **application_id** (String) Id of the application scoping -- **environment_filter_type** (String) Type of environment filter applied. ALL if not filter applied -- **environment_id** (String) Id of the environment scoping +- **application_id** (String) Id of the application to scope to. If empty then this scope applies to all applications. +- **environment_filter_type** (String) Type of environment filter applied. Cannot be used with `environment_id`. Valid options are NON_PRODUCTION_ENVIRONMENTS, PRODUCTION_ENVIRONMENTS. +- **environment_id** (String) Id of the id of the specific environment to scope to. Cannot be used with `environment_filter_type`. ## Import diff --git a/docs/resources/cloudprovider_datacenter.md b/docs/resources/cloudprovider_datacenter.md index 8f8cf312d..ca5a4e692 100644 --- a/docs/resources/cloudprovider_datacenter.md +++ b/docs/resources/cloudprovider_datacenter.md @@ -27,7 +27,7 @@ resource "harness_cloudprovider_datacenter" "example" { ### Optional -- **usage_scope** (Block Set) Usage scopes (see [below for nested schema](#nestedblock--usage_scope)) +- **usage_scope** (Block Set) This block is used for scoping the resource to a specific set of applications or environments. (see [below for nested schema](#nestedblock--usage_scope)) ### Read-Only @@ -38,10 +38,9 @@ resource "harness_cloudprovider_datacenter" "example" { Optional: -- **application_filter_type** (String) Type of application filter applied. ALL if not application id supplied, otherwise NULL -- **application_id** (String) Id of the application scoping -- **environment_filter_type** (String) Type of environment filter applied. ALL if not filter applied -- **environment_id** (String) Id of the environment scoping +- **application_id** (String) Id of the application to scope to. If empty then this scope applies to all applications. +- **environment_filter_type** (String) Type of environment filter applied. Cannot be used with `environment_id`. Valid options are NON_PRODUCTION_ENVIRONMENTS, PRODUCTION_ENVIRONMENTS. +- **environment_id** (String) Id of the id of the specific environment to scope to. Cannot be used with `environment_filter_type`. ## Import diff --git a/docs/resources/cloudprovider_gcp.md b/docs/resources/cloudprovider_gcp.md index 6df851f71..181ae4cee 100644 --- a/docs/resources/cloudprovider_gcp.md +++ b/docs/resources/cloudprovider_gcp.md @@ -24,7 +24,7 @@ Resource for creating a GCP cloud provider - **delegate_selectors** (List of String) Delegate selectors to use for this provider. - **secret_file_id** (String) The id of the secret containing the GCP credentials - **skip_validation** (Boolean) Skip validation of GCP configuration. -- **usage_scope** (Block Set) Usage scopes (see [below for nested schema](#nestedblock--usage_scope)) +- **usage_scope** (Block Set) This block is used for scoping the resource to a specific set of applications or environments. (see [below for nested schema](#nestedblock--usage_scope)) ### Read-Only @@ -35,9 +35,8 @@ Resource for creating a GCP cloud provider Optional: -- **application_filter_type** (String) Type of application filter applied. ALL if not application id supplied, otherwise NULL -- **application_id** (String) Id of the application scoping -- **environment_filter_type** (String) Type of environment filter applied. ALL if not filter applied -- **environment_id** (String) Id of the environment scoping +- **application_id** (String) Id of the application to scope to. If empty then this scope applies to all applications. +- **environment_filter_type** (String) Type of environment filter applied. Cannot be used with `environment_id`. Valid options are NON_PRODUCTION_ENVIRONMENTS, PRODUCTION_ENVIRONMENTS. +- **environment_id** (String) Id of the id of the specific environment to scope to. Cannot be used with `environment_filter_type`. diff --git a/docs/resources/cloudprovider_kubernetes.md b/docs/resources/cloudprovider_kubernetes.md index 671379730..505ae0c72 100644 --- a/docs/resources/cloudprovider_kubernetes.md +++ b/docs/resources/cloudprovider_kubernetes.md @@ -54,7 +54,7 @@ resource "harness_cloudprovider_kubernetes" "example" { ### Optional - **skip_validation** (Boolean) Skip validation of Kubernetes configuration. -- **usage_scope** (Block Set) Usage scopes (see [below for nested schema](#nestedblock--usage_scope)) +- **usage_scope** (Block Set) This block is used for scoping the resource to a specific set of applications or environments. (see [below for nested schema](#nestedblock--usage_scope)) ### Read-Only @@ -120,10 +120,9 @@ Optional: Optional: -- **application_filter_type** (String) Type of application filter applied. ALL if not application id supplied, otherwise NULL -- **application_id** (String) Id of the application scoping -- **environment_filter_type** (String) Type of environment filter applied. ALL if not filter applied -- **environment_id** (String) Id of the environment scoping +- **application_id** (String) Id of the application to scope to. If empty then this scope applies to all applications. +- **environment_filter_type** (String) Type of environment filter applied. Cannot be used with `environment_id`. Valid options are NON_PRODUCTION_ENVIRONMENTS, PRODUCTION_ENVIRONMENTS. +- **environment_id** (String) Id of the id of the specific environment to scope to. Cannot be used with `environment_filter_type`. ## Import diff --git a/docs/resources/encrypted_text.md b/docs/resources/encrypted_text.md index 707cb2e14..ae47d4d1b 100644 --- a/docs/resources/encrypted_text.md +++ b/docs/resources/encrypted_text.md @@ -23,12 +23,10 @@ resource "harness_encrypted_text" "example" { secret_manager_id = data.harness_secret_manager.default.id usage_scope { - application_filter_type = "ALL" environment_filter_type = "PRODUCTION_ENVIRONMENTS" } usage_scope { - application_filter_type = "ALL" environment_filter_type = "NON_PRODUCTION_ENVIRONMENTS" } } @@ -47,7 +45,7 @@ resource "harness_encrypted_text" "example" { - **inherit_scopes_from_secret_manager** (Boolean) Boolean that indicates whether or not to inherit the usage scopes from the secret manager - **scoped_to_account** (Boolean) Boolean that indicates whether or not the secret is scoped to the account -- **usage_scope** (Block Set) Usage scopes (see [below for nested schema](#nestedblock--usage_scope)) +- **usage_scope** (Block Set) This block is used for scoping the resource to a specific set of applications or environments. (see [below for nested schema](#nestedblock--usage_scope)) ### Read-Only @@ -58,10 +56,9 @@ resource "harness_encrypted_text" "example" { Optional: -- **application_filter_type** (String) Type of application filter applied. ALL if not application id supplied, otherwise NULL -- **application_id** (String) Id of the application scoping -- **environment_filter_type** (String) Type of environment filter applied. ALL if not filter applied -- **environment_id** (String) Id of the environment scoping +- **application_id** (String) Id of the application to scope to. If empty then this scope applies to all applications. +- **environment_filter_type** (String) Type of environment filter applied. Cannot be used with `environment_id`. Valid options are NON_PRODUCTION_ENVIRONMENTS, PRODUCTION_ENVIRONMENTS. +- **environment_id** (String) Id of the id of the specific environment to scope to. Cannot be used with `environment_filter_type`. ## Import diff --git a/docs/resources/git_connector.md b/docs/resources/git_connector.md index 48329ae03..1f0dcdab2 100644 --- a/docs/resources/git_connector.md +++ b/docs/resources/git_connector.md @@ -51,7 +51,7 @@ resource "harness_git_connector" "example" { - **generate_webhook_url** (Boolean) Boolean indicating whether or not to generate a webhook url. - **password_secret_id** (String) The id of the secret for connecting to the git repository. - **ssh_setting_id** (String) The id of the SSH secret to use -- **usage_scope** (Block Set) Usage scopes (see [below for nested schema](#nestedblock--usage_scope)) +- **usage_scope** (Block Set) This block is used for scoping the resource to a specific set of applications or environments. (see [below for nested schema](#nestedblock--usage_scope)) - **username** (String) The name of the user used to connect to the git repository ### Read-Only @@ -75,10 +75,9 @@ Optional: Optional: -- **application_filter_type** (String) Type of application filter applied. ALL if not application id supplied, otherwise NULL -- **application_id** (String) Id of the application scoping -- **environment_filter_type** (String) Type of environment filter applied. ALL if not filter applied -- **environment_id** (String) Id of the environment scoping +- **application_id** (String) Id of the application to scope to. If empty then this scope applies to all applications. +- **environment_filter_type** (String) Type of environment filter applied. Cannot be used with `environment_id`. Valid options are NON_PRODUCTION_ENVIRONMENTS, PRODUCTION_ENVIRONMENTS. +- **environment_id** (String) Id of the id of the specific environment to scope to. Cannot be used with `environment_filter_type`. ## Import diff --git a/docs/resources/ssh_credential.md b/docs/resources/ssh_credential.md index 04b79f9de..030704458 100644 --- a/docs/resources/ssh_credential.md +++ b/docs/resources/ssh_credential.md @@ -58,7 +58,7 @@ resource "harness_ssh_credential" "ssh_creds" { - **kerberos_authentication** (Block List, Max: 1) Kerberos authentication for SSH. Cannot be used if ssh_authentication is specified (see [below for nested schema](#nestedblock--kerberos_authentication)) - **ssh_authentication** (Block List, Max: 1) Authentication method for SSH. Cannot be used if kerberos_authentication is specified. Only one of `inline_ssh`, `server_password`, or `ssh_key_file` should be set (see [below for nested schema](#nestedblock--ssh_authentication)) -- **usage_scope** (Block Set) Usage scopes (see [below for nested schema](#nestedblock--usage_scope)) +- **usage_scope** (Block Set) This block is used for scoping the resource to a specific set of applications or environments. (see [below for nested schema](#nestedblock--usage_scope)) ### Read-Only @@ -139,10 +139,9 @@ Optional: Optional: -- **application_filter_type** (String) Type of application filter applied. ALL if not application id supplied, otherwise NULL -- **application_id** (String) Id of the application scoping -- **environment_filter_type** (String) Type of environment filter applied. ALL if not filter applied -- **environment_id** (String) Id of the environment scoping +- **application_id** (String) Id of the application to scope to. If empty then this scope applies to all applications. +- **environment_filter_type** (String) Type of environment filter applied. Cannot be used with `environment_id`. Valid options are NON_PRODUCTION_ENVIRONMENTS, PRODUCTION_ENVIRONMENTS. +- **environment_id** (String) Id of the id of the specific environment to scope to. Cannot be used with `environment_filter_type`. ## Import diff --git a/examples/resources/harness_encrypted_text/resource.tf b/examples/resources/harness_encrypted_text/resource.tf index b829be1cb..22afcce6b 100644 --- a/examples/resources/harness_encrypted_text/resource.tf +++ b/examples/resources/harness_encrypted_text/resource.tf @@ -8,12 +8,10 @@ resource "harness_encrypted_text" "example" { secret_manager_id = data.harness_secret_manager.default.id usage_scope { - application_filter_type = "ALL" environment_filter_type = "PRODUCTION_ENVIRONMENTS" } usage_scope { - application_filter_type = "ALL" environment_filter_type = "NON_PRODUCTION_ENVIRONMENTS" } } diff --git a/examples/usage_scope/complex.tf b/examples/usage_scope/complex.tf new file mode 100644 index 000000000..1d04ad5e5 --- /dev/null +++ b/examples/usage_scope/complex.tf @@ -0,0 +1,25 @@ +resource "harness_application" "example" { + name = "myapp" +} + +resource "harness_environment" "qa" { + name = "qa" + app_id = harness_application.example.id + type = "NON_PROD" +} + +resource "harness_cloudprovider_kubernetes" "k8s" { + name = "k8s" + + // Example of scoping to all non-prod environments of a specific application + usage_scope { + application_id = harness_application.example.id + environment_filter_type = "NON_PRODUCTION_ENVIRONMENTS" + } + + // Example of scoping to a specific environment + usage_scope { + application_id = harness_application.example.id + environment_id = harness_environment.qa.id + } +} diff --git a/examples/usage_scope/simple.tf b/examples/usage_scope/simple.tf new file mode 100644 index 000000000..e0315a897 --- /dev/null +++ b/examples/usage_scope/simple.tf @@ -0,0 +1,12 @@ +resource "harness_cloudprovider_kubernetes" "test" { + name = "test" + + usage_scope { + environment_filter_type = "NON_PRODUCTION_ENVIRONMENTS" + } + + usage_scope { + environment_filter_type = "PRODUCTION_ENVIRONMENTS" + } + +} diff --git a/internal/provider/data_source_encrypted_text_test.go b/internal/provider/data_source_encrypted_text_test.go index 0846fa529..1e1391665 100644 --- a/internal/provider/data_source_encrypted_text_test.go +++ b/internal/provider/data_source_encrypted_text_test.go @@ -25,7 +25,6 @@ func TestAccDataSourceEncryptedTextByName(t *testing.T) { Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(resourceName, "name", expectedName), resource.TestCheckResourceAttrSet(resourceName, "secret_manager_id"), - resource.TestCheckResourceAttr(resourceName, "usage_scope.0.application_filter_type", "ALL"), resource.TestCheckResourceAttr(resourceName, "usage_scope.0.environment_filter_type", "NON_PRODUCTION_ENVIRONMENTS"), ), }, @@ -45,7 +44,6 @@ func testAccDataSourceEncryptedTextByName(name string) string { secret_manager_id = data.harness_secret_manager.test.id usage_scope { - application_filter_type = "ALL" environment_filter_type = "NON_PRODUCTION_ENVIRONMENTS" } } diff --git a/internal/provider/resource_cloudprovider_datacenter_test.go b/internal/provider/resource_cloudprovider_datacenter_test.go index 4deb77ae3..965e509ac 100644 --- a/internal/provider/resource_cloudprovider_datacenter_test.go +++ b/internal/provider/resource_cloudprovider_datacenter_test.go @@ -91,12 +91,10 @@ func testAccResourceDataCenterCloudProvider(name string) string { name = "%[1]s" usage_scope { - application_filter_type = "ALL" environment_filter_type = "NON_PRODUCTION_ENVIRONMENTS" } usage_scope { - application_filter_type = "ALL" environment_filter_type = "PRODUCTION_ENVIRONMENTS" } } diff --git a/internal/provider/resource_cloudprovider_gcp_test.go b/internal/provider/resource_cloudprovider_gcp_test.go index 9ddfd1365..cbe7e95f5 100644 --- a/internal/provider/resource_cloudprovider_gcp_test.go +++ b/internal/provider/resource_cloudprovider_gcp_test.go @@ -92,12 +92,10 @@ func testAccResourceGcpCloudProvider(name string) string { delegate_selectors = ["testing"] usage_scope { - application_filter_type = "ALL" environment_filter_type = "NON_PRODUCTION_ENVIRONMENTS" } usage_scope { - application_filter_type = "ALL" environment_filter_type = "PRODUCTION_ENVIRONMENTS" } } diff --git a/internal/provider/resource_cloudprovider_kubernetes_test.go b/internal/provider/resource_cloudprovider_kubernetes_test.go index bdc3c7551..a46f04f71 100644 --- a/internal/provider/resource_cloudprovider_kubernetes_test.go +++ b/internal/provider/resource_cloudprovider_kubernetes_test.go @@ -40,6 +40,34 @@ func TestAccResourceK8sCloudProviderConnector_delegate(t *testing.T) { }) } +func TestAccResourceK8sCloudProviderConnector_usagescope_application(t *testing.T) { + + var ( + name = fmt.Sprintf("%s_%s", t.Name(), utils.RandStringBytes(4)) + resourceName = "harness_cloudprovider_kubernetes.test" + ) + + resource.UnitTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ProviderFactories: providerFactories, + CheckDestroy: testAccCloudProviderDestroy(resourceName), + Steps: []resource.TestStep{ + { + Config: testAccResourceK8sCloudProvider_usagescope_application(name), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", name), + testAccCheckK8sCloudProviderExists(t, resourceName, name), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccResourceK8sCloudProviderConnector_username_password(t *testing.T) { var ( @@ -180,18 +208,54 @@ func testAccResourceK8sCloudProvider_delegate(name string) string { } usage_scope { - application_filter_type = "ALL" environment_filter_type = "NON_PRODUCTION_ENVIRONMENTS" } usage_scope { - application_filter_type = "ALL" environment_filter_type = "PRODUCTION_ENVIRONMENTS" } } `, name) } +func testAccResourceK8sCloudProvider_usagescope_application(name string) string { + return fmt.Sprintf(` + resource "harness_application" "test" { + name = "%[1]s" + } + + resource "harness_environment" "test" { + name = "%[1]s" + app_id = harness_application.test.id + type = "NON_PROD" + } + + resource "harness_cloudprovider_kubernetes" "test" { + name = "%[1]s" + skip_validation = true + + authentication { + delegate_selectors = ["test"] + } + + usage_scope { + application_id = harness_application.test.id + environment_filter_type = "NON_PRODUCTION_ENVIRONMENTS" + } + + usage_scope { + environment_filter_type = "NON_PRODUCTION_ENVIRONMENTS" + } + + usage_scope { + application_id = harness_application.test.id + environment_id = harness_environment.test.id + } + + } +`, name) +} + func testAccResourceK8sCloudProvider_username_password(name string) string { return fmt.Sprintf(` data "harness_secret_manager" "default" { diff --git a/internal/provider/resource_encrypted_text_test.go b/internal/provider/resource_encrypted_text_test.go index aa5a3ff12..600cbb141 100644 --- a/internal/provider/resource_encrypted_text_test.go +++ b/internal/provider/resource_encrypted_text_test.go @@ -166,9 +166,7 @@ func TestAccResourceEncryptedText_UsageScopes(t *testing.T) { Config: testAccResourceEncryptedText_UsageScopes(name), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(resourceName, "name", name), - resource.TestCheckResourceAttr(resourceName, "usage_scope.1.application_filter_type", graphql.ApplicationFilterTypes.All.String()), resource.TestCheckResourceAttr(resourceName, "usage_scope.1.environment_filter_type", graphql.EnvironmentFilterTypes.Production.String()), - resource.TestCheckResourceAttr(resourceName, "usage_scope.0.application_filter_type", graphql.ApplicationFilterTypes.All.String()), resource.TestCheckResourceAttr(resourceName, "usage_scope.0.environment_filter_type", graphql.EnvironmentFilterTypes.NonProduction.String()), func(state *terraform.State) error { et, err := testAccGetEncryptedText(resourceName, state) @@ -184,7 +182,6 @@ func TestAccResourceEncryptedText_UsageScopes(t *testing.T) { Config: testAccResourceEncryptedText_usageScopes_update(updatedName), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr(resourceName, "name", updatedName), - resource.TestCheckResourceAttr(resourceName, "usage_scope.0.application_filter_type", graphql.ApplicationFilterTypes.All.String()), resource.TestCheckResourceAttr(resourceName, "usage_scope.0.environment_filter_type", graphql.EnvironmentFilterTypes.Production.String()), resource.TestCheckNoResourceAttr(resourceName, "usage_scope.1"), func(state *terraform.State) error { @@ -236,12 +233,10 @@ func testAccResourceEncryptedText_UsageScopes(name string) string { secret_manager_id = data.harness_secret_manager.default.id usage_scope { - application_filter_type = "ALL" environment_filter_type = "PRODUCTION_ENVIRONMENTS" } usage_scope { - application_filter_type = "ALL" environment_filter_type = "NON_PRODUCTION_ENVIRONMENTS" } } @@ -259,7 +254,6 @@ func testAccResourceEncryptedText_usageScopes_update(name string) string { value = "someval" secret_manager_id = data.harness_secret_manager.default.id usage_scope { - application_filter_type = "ALL" environment_filter_type = "PRODUCTION_ENVIRONMENTS" } } diff --git a/internal/provider/resource_git_connector_test.go b/internal/provider/resource_git_connector_test.go index 254215a43..f7586f68e 100644 --- a/internal/provider/resource_git_connector_test.go +++ b/internal/provider/resource_git_connector_test.go @@ -257,12 +257,10 @@ func testAccResourceGitConnector(name string, generateWebhook bool, withCommitDe // value = "someval" // usage_scope { -// application_filter_type = "ALL" // environment_filter_type = "PRODUCTION_ENVIRONMENTS" // } // usage_scope { -// application_filter_type = "ALL" // environment_filter_type = "NON_PRODUCTION_ENVIRONMENTS" // } // } @@ -277,7 +275,6 @@ func testAccResourceGitConnector(name string, generateWebhook bool, withCommitDe // value = "someval" // usage_scope { -// application_filter_type = "ALL" // environment_filter_type = "PRODUCTION_ENVIRONMENTS" // } // } diff --git a/internal/provider/resource_ssh_credential_test.go b/internal/provider/resource_ssh_credential_test.go index 9fa8a928e..978e37720 100644 --- a/internal/provider/resource_ssh_credential_test.go +++ b/internal/provider/resource_ssh_credential_test.go @@ -56,7 +56,6 @@ func TestAccResourceSSHCredential_SSHAuthentication(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "ssh_authentication.0.port", "22"), resource.TestCheckResourceAttr(resourceName, "ssh_authentication.0.username", "testuser"), resource.TestCheckResourceAttr(resourceName, "usage_scope.0.environment_filter_type", string(graphql.EnvironmentFilterTypes.NonProduction)), - resource.TestCheckResourceAttr(resourceName, "usage_scope.0.application_filter_type", string(graphql.ApplicationFilterTypes.All)), ), }, { @@ -120,7 +119,6 @@ func TestAccResourceSSHCredential_KerberosAuthentication(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "kerberos_authentication.0.principal", "testuser"), resource.TestCheckResourceAttr(resourceName, "kerberos_authentication.0.realm", "domain.com"), resource.TestCheckResourceAttr(resourceName, "usage_scope.0.environment_filter_type", string(graphql.EnvironmentFilterTypes.NonProduction)), - resource.TestCheckResourceAttr(resourceName, "usage_scope.0.application_filter_type", string(graphql.ApplicationFilterTypes.All)), testAccSShCredentialCreation(t, resourceName, graphql.SSHAuthenticationSchemes.Kerberos), ), }, @@ -143,7 +141,6 @@ func TestAccResourceSSHCredential_KerberosAuthentication(t *testing.T) { // resource.TestCheckResourceAttr(resourceName, "ssh_authentication.0.port", "22"), // resource.TestCheckResourceAttr(resourceName, "ssh_authentication.0.username", "testuser"), // resource.TestCheckResourceAttr(resourceName, "usage_scope.0.environment_filter_type", string(graphql.EnvironmentFilterTypes.NonProduction)), -// resource.TestCheckResourceAttr(resourceName, "usage_scope.0.application_filter_type", string(graphql.ApplicationFilterTypes.All)), // testAccSShCredentialCreation(t, resourceName, graphql.SSHAuthenticationSchemes.SSH), // ), // }, @@ -155,7 +152,6 @@ func TestAccResourceSSHCredential_KerberosAuthentication(t *testing.T) { // resource.TestCheckResourceAttr(resourceName, "kerberos_authentication.0.principal", "testuser"), // resource.TestCheckResourceAttr(resourceName, "kerberos_authentication.0.realm", "domain.com"), // resource.TestCheckResourceAttr(resourceName, "usage_scope.0.environment_filter_type", string(graphql.EnvironmentFilterTypes.NonProduction)), -// resource.TestCheckResourceAttr(resourceName, "usage_scope.0.application_filter_type", string(graphql.ApplicationFilterTypes.All)), // testAccSShCredentialCreation(t, resourceName, graphql.SSHAuthenticationSchemes.Kerberos), // ), // }, diff --git a/internal/provider/usage_scope.go b/internal/provider/usage_scope.go index a083ceb01..3d138a6a6 100644 --- a/internal/provider/usage_scope.go +++ b/internal/provider/usage_scope.go @@ -2,6 +2,8 @@ package provider import ( "errors" + "fmt" + "strings" "github.com/harness-io/harness-go-sdk/harness/api" "github.com/harness-io/harness-go-sdk/harness/api/cac" @@ -11,30 +13,29 @@ import ( func usageScopeSchema() *schema.Schema { return &schema.Schema{ - Description: "Usage scopes", + Description: "This block is used for scoping the resource to a specific set of applications or environments.", Type: schema.TypeSet, Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "application_id": { - Description: "Id of the application scoping", - Type: schema.TypeString, - Optional: true, - }, - "application_filter_type": { - Description: "Type of application filter applied. ALL if not application id supplied, otherwise NULL", + Description: "Id of the application to scope to. If empty then this scope applies to all applications.", Type: schema.TypeString, Optional: true, }, "environment_id": { - Description: "Id of the environment scoping", + Description: "Id of the id of the specific environment to scope to. Cannot be used with `environment_filter_type`.", Type: schema.TypeString, Optional: true, + // ConflictsWith: []string{"usage_scope.0.environment_filter_type"}, + // ExactlyOneOf: []string{"usage_scope.0.environment_id", "usage_scope.0.environment_filter_type"}, }, "environment_filter_type": { - Description: "Type of environment filter applied. ALL if not filter applied", + Description: fmt.Sprintf("Type of environment filter applied. Cannot be used with `environment_id`. Valid options are %s.", strings.Join(graphql.EnvFiltersSlice, ", ")), Type: schema.TypeString, Optional: true, + // ConflictsWith: []string{"usage_scope.0.environment_id"}, + // ExactlyOneOf: []string{"usage_scope.0.environment_id", "usage_scope.0.environment_filter_type"}, }, }, }, @@ -53,12 +54,14 @@ func expandUsageScope(d []interface{}) (*graphql.UsageScope, error) { Environment: &graphql.EnvScopeFilter{}, } - if attr, ok := scopeData["application_filter_type"]; ok && attr != "" { - scope.Application.FilterType = graphql.ApplicationFilterType(attr.(string)) + if ok, err := validateUsageScopeSettings(scopeData); !ok { + return nil, err } if attr, ok := scopeData["application_id"]; ok && attr != "" { scope.Application.AppId = attr.(string) + } else { + scope.Application.FilterType = graphql.ApplicationFilterTypes.All } if attr, ok := scopeData["environment_filter_type"]; ok && attr != "" { @@ -77,6 +80,22 @@ func expandUsageScope(d []interface{}) (*graphql.UsageScope, error) { return us, nil } +func validateUsageScopeSettings(scopeData map[string]interface{}) (bool, error) { + if scopeData["environment_id"] != "" && scopeData["application_id"] == "" { + return false, errors.New("`application_id` must be set when using `environment_id`") + } + + if scopeData["environment_filter_type"] != "" && scopeData["environment_id"] != "" { + return false, fmt.Errorf("only one of environment_filter_type or environment_id must be set") + } + + if scopeData["environment_filter_type"] == "" && scopeData["environment_id"] == "" { + return false, fmt.Errorf("at least one of environment_filter_type or environment_id must be set") + } + + return true, nil +} + func expandUsageRestrictions(c *api.Client, d []interface{}, ur *cac.UsageRestrictions) error { if len(d) == 0 { return nil @@ -91,23 +110,38 @@ func expandUsageRestrictions(c *api.Client, d []interface{}, ur *cac.UsageRestri EnvFilter: &cac.EnvFilter{}, } - if attr, ok := scopeData["application_filter_type"]; ok && attr != "" { - if graphql.ApplicationFilterType(attr.(string)) == graphql.ApplicationFilterTypes.All { - scope.AppFilter.FilterType = cac.ApplicationFilterTypes.All - } else { - scope.AppFilter.FilterType = cac.ApplicationFilterTypes.Selected - } + if ok, err := validateUsageScopeSettings(scopeData); !ok { + return err } - var app *cac.Application + var app *graphql.Application + var err error + if attr, ok := scopeData["application_id"]; ok && attr != "" { - app, err := c.Applications().GetApplicationById(attr.(string)) + app, err = c.Applications().GetApplicationById(attr.(string)) if err != nil { return nil - } else if app == nil { + } + + if app == nil { return errors.New("application not found") } + scope.AppFilter.EntityNames = []string{app.Name} + scope.AppFilter.FilterType = cac.ApplicationFilterTypes.Selected + } else { + scope.AppFilter.FilterType = cac.ApplicationFilterTypes.All + } + + if attr, ok := scopeData["environment_id"]; ok && attr != "" { + env, err := c.ConfigAsCode().GetEnvironmentById(app.Id, attr.(string)) + if err != nil { + return err + } else if env.IsEmpty() { + return errors.New("environment not found") + } + scope.EnvFilter.EntityNames = []string{env.Name} + scope.EnvFilter.FilterTypes = []cac.EnvironmentFilterType{cac.EnvironmentFilterTypes.Selected} } if attr, ok := scopeData["environment_filter_type"]; ok && attr != "" { @@ -119,16 +153,6 @@ func expandUsageRestrictions(c *api.Client, d []interface{}, ur *cac.UsageRestri default: return errors.New("could not parse environment_filter_type '" + attr.(string) + "'") } - - if attr, ok := scopeData["environment_id"]; ok && attr != "" { - env, err := c.ConfigAsCode().GetEnvironmentById(app.Id, attr.(string)) - if err != nil { - return err - } else if env.IsEmpty() { - return errors.New("environment not found") - } - scope.EnvFilter.EntityNames = []string{env.Name} - } } restrictions = append(restrictions, scope) @@ -150,7 +174,6 @@ func flattenUsageScope(uc *graphql.UsageScope) []map[string]interface{} { for i, scope := range uc.AppEnvScopes { results[i] = map[string]interface{}{ "application_id": scope.Application.AppId, - "application_filter_type": scope.Application.FilterType, "environment_id": scope.Environment.EnvId, "environment_filter_type": scope.Environment.FilterType, } @@ -179,7 +202,6 @@ func flattenUsageRestrictions(c *api.Client, ur *cac.UsageRestrictions) ([]map[s results[i] = map[string]interface{}{ "application_id": appId, - "application_filter_type": flattenAppFilterType(scope.AppFilter), "environment_id": envId, "environment_filter_type": flattenEnvFilterTypes(scope.EnvFilter), } @@ -201,14 +223,14 @@ func flattenAppFilterEntityName(c *api.Client, filter *cac.AppFilter) (string, e return app.Id, nil } -func flattenAppFilterType(filter *cac.AppFilter) string { - switch filter.FilterType { - case cac.ApplicationFilterTypes.All: - return string(graphql.ApplicationFilterTypes.All) - default: - return "" - } -} +// func flattenAppFilterType(filter *cac.AppFilter) string { +// switch filter.FilterType { +// case cac.ApplicationFilterTypes.All: +// return string(graphql.ApplicationFilterTypes.All) +// default: +// return "" +// } +// } func flattenEnvFilterEntityName(c *api.Client, filter *cac.EnvFilter, applicationId string) (string, error) { if len(filter.EntityNames) == 0 { diff --git a/internal/provider/usage_scopes_test.go b/internal/provider/usage_scopes_test.go index 49e0ec3ae..e82fe9e0c 100644 --- a/internal/provider/usage_scopes_test.go +++ b/internal/provider/usage_scopes_test.go @@ -2,7 +2,6 @@ package provider const testAccDefaultUsageScope = ` usage_scope { - application_filter_type = "ALL" environment_filter_type = "NON_PRODUCTION_ENVIRONMENTS" } ` diff --git a/templates/guides/usage-scopes.md.tmpl b/templates/guides/usage-scopes.md.tmpl new file mode 100644 index 000000000..b0ffb3704 --- /dev/null +++ b/templates/guides/usage-scopes.md.tmpl @@ -0,0 +1,19 @@ +--- +subcategory: "" +page_title: "Configuring usage scopes - Harness Provider" +description: |- + An example of how to apply usage scopes to a resource. +--- + +# Configure usage scopes for resources + +There are a number of resources that can be scoped to a specific set of applications and environments. These include cloud providers, secrets, connectors, and more. Configuring it is the same across all of these resources. + +In this example we are configuring a cloud provider to be used by any application in any environment. + +{{ tffile "examples/usage_scope/simple.tf" }} + +In this more advanced scenario we show how you can scope the cloud provider to a specific application or to a specific environment. + +{{ tffile "examples/usage_scope/complex.tf" }} +