diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 5b9f3c9171..0d1c894bb4 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -8,6 +8,16 @@ across different versions. ### old grant resources removal Following the [announcement](https://github.com/Snowflake-Labs/terraform-provider-snowflake/discussions/2736) we have removed the old grant resources. The two resources [snowflake_role_ownership_grant](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/role_ownership_grant) and [snowflake_user_ownership_grant](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/user_ownership_grant) were not listed in the announcement, but they were also marked as deprecated ones. We are removing them too to conclude the grants redesign saga. +### *(new feature)* snowflake_security_integrations datasource +Added a new datasource enabling querying and filtering all types of security integrations. Notes: +- all results are stored in `security_integrations` field. +- `like` field enables security integrations filtering. +- SHOW SECURITY INTEGRATIONS output is enclosed in `show_output` field inside `security_integrations`. +- Output from **DESC SECURITY INTEGRATION** (which can be turned off by declaring `with_describe = false`, **it's turned on by default**) is enclosed in `describe_output` field inside `security_integrations`. + **DESC SECURITY INTEGRATION** returns different properties based on the integration type. Consult the documentation to check which ones will be filled for which integration. + The additional parameters call **DESC SECURITY INTEGRATION** (with `with_describe` turned on) **per security integration** returned by **SHOW SECURITY INTEGRATIONS**. + It's important to limit the records and calls to Snowflake to the minimum. That's why we recommend assessing which information you need from the data source and then providing strong filters and turning off additional fields for better plan performance. + ### snowflake_scim_integration resource changes #### *(behavior change)* Changed behavior of `sync_password` @@ -82,7 +92,7 @@ To easily handle three-value logic (true, false, unknown) in provider's configs, #### *(note)* `resource_monitor` validation and diff suppression `resource_monitor` is an identifier and handling logic may be still slightly changed as part of https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/ROADMAP.md#identifiers-rework. It should be handled automatically (without needed manual actions on user side), though, but it is not guaranteed. -#### *(behavior change)* snowflake_databases datasource +#### *(behavior change)* snowflake_warehouses datasource - Added `like` field to enable warehouse filtering - Added missing fields returned by SHOW WAREHOUSES and enclosed its output in `show_output` field. - Added outputs from **DESC WAREHOUSE** and **SHOW PARAMETERS IN WAREHOUSE** (they can be turned off by declaring `with_describe = false` and `with_parameters = false`, **they're turned on by default**). diff --git a/docs/data-sources/databases.md b/docs/data-sources/databases.md index 700b8d6bbc..8de06ace20 100644 --- a/docs/data-sources/databases.md +++ b/docs/data-sources/databases.md @@ -2,12 +2,12 @@ page_title: "snowflake_databases Data Source - terraform-provider-snowflake" subcategory: "" description: |- - Datasource used to get details of filtered databases. Filtering is aligned with the current possibilities for SHOW DATABASES (https://docs.snowflake.com/en/sql-reference/sql/show-databases) query (`like`, 'starts_with', and `limit` are all supported. The results of SHOW, DESCRIBE, and SHOW PARAMETERS IN are encapsulated in one output collection. + Datasource used to get details of filtered databases. Filtering is aligned with the current possibilities for SHOW DATABASES https://docs.snowflake.com/en/sql-reference/sql/show-databases query (like, starts_with, and limit are all supported). The results of SHOW, DESCRIBE, and SHOW PARAMETERS IN are encapsulated in one output collection. --- # snowflake_databases (Data Source) -Datasource used to get details of filtered databases. Filtering is aligned with the current possibilities for [SHOW DATABASES]((https://docs.snowflake.com/en/sql-reference/sql/show-databases) query (`like`, 'starts_with', and `limit` are all supported). The results of SHOW, DESCRIBE, and SHOW PARAMETERS IN are encapsulated in one output collection. +Datasource used to get details of filtered databases. Filtering is aligned with the current possibilities for [SHOW DATABASES](https://docs.snowflake.com/en/sql-reference/sql/show-databases) query (`like`, `starts_with`, and `limit` are all supported). The results of SHOW, DESCRIBE, and SHOW PARAMETERS IN are encapsulated in one output collection. ## Example Usage diff --git a/docs/data-sources/security_integrations.md b/docs/data-sources/security_integrations.md new file mode 100644 index 0000000000..e3423a59bf --- /dev/null +++ b/docs/data-sources/security_integrations.md @@ -0,0 +1,125 @@ +--- +page_title: "snowflake_security_integrations Data Source - terraform-provider-snowflake" +subcategory: "" +description: |- + Datasource used to get details of filtered security integrations. Filtering is aligned with the current possibilities for SHOW SECURITY INTEGRATIONS https://docs.snowflake.com/en/sql-reference/sql/show-integrations query (only like is supported). The results of SHOW and DESCRIBE are encapsulated in one output collection security_integrations. +--- + +# snowflake_security_integrations (Data Source) + +Datasource used to get details of filtered security integrations. Filtering is aligned with the current possibilities for [SHOW SECURITY INTEGRATIONS](https://docs.snowflake.com/en/sql-reference/sql/show-integrations) query (only `like` is supported). The results of SHOW and DESCRIBE are encapsulated in one output collection `security_integrations`. + +## Example Usage + +```terraform +# Simple usage +data "snowflake_security_integrations" "simple" { +} + +output "simple_output" { + value = data.snowflake_security_integrations.simple.security_integrations +} + +# Filtering (like) +data "snowflake_security_integrations" "like" { + like = "security-integration-name" +} + +output "like_output" { + value = data.snowflake_security_integrations.like.security_integrations +} + +# Filtering by prefix (like) +data "snowflake_security_integrations" "like_prefix" { + like = "prefix%" +} + +output "like_prefix_output" { + value = data.snowflake_security_integrations.like_prefix.security_integrations +} + +# Without additional data (to limit the number of calls make for every found security integration) +data "snowflake_security_integrations" "only_show" { + # with_describe is turned on by default and it calls DESCRIBE SECURITY INTEGRATION for every security integration found and attaches its output to security_integrations.*.describe_output field + with_describe = false +} + +output "only_show_output" { + value = data.snowflake_security_integrations.only_show.security_integrations +} + +# Ensure the number of security_integrations is equal to at least one element (with the use of postcondition) +data "snowflake_security_integrations" "assert_with_postcondition" { + like = "security-integration-name%" + lifecycle { + postcondition { + condition = length(self.security_integrations) > 0 + error_message = "there should be at least one security integration" + } + } +} + +# Ensure the number of security_integrations is equal to at exactly one element (with the use of check block) +check "security_integration_check" { + data "snowflake_security_integrations" "assert_with_check_block" { + like = "security-integration-name" + } + + assert { + condition = length(data.snowflake_security_integrations.assert_with_check_block.security_integrations) == 1 + error_message = "security integrations filtered by '${data.snowflake_security_integrations.assert_with_check_block.like}' returned ${length(data.snowflake_security_integrations.assert_with_check_block.security_integrations)} security integrations where one was expected" + } +} +``` + + +## Schema + +### Optional + +- `like` (String) Filters the output with **case-insensitive** pattern, with support for SQL wildcard characters (`%` and `_`). +- `with_describe` (Boolean) Runs DESC SECURITY INTEGRATION for each security integration returned by SHOW SECURITY INTEGRATIONS. The output of describe is saved to the description field. By default this value is set to true. + +### Read-Only + +- `id` (String) The ID of this resource. +- `security_integrations` (List of Object) Holds the aggregated output of all security integrations details queries. (see [below for nested schema](#nestedatt--security_integrations)) + + +### Nested Schema for `security_integrations` + +Read-Only: + +- `describe_output` (List of Object) (see [below for nested schema](#nestedobjatt--security_integrations--describe_output)) +- `show_output` (List of Object) (see [below for nested schema](#nestedobjatt--security_integrations--show_output)) + + +### Nested Schema for `security_integrations.describe_output` + +Read-Only: + +- `todo` (List of Object) (see [below for nested schema](#nestedobjatt--security_integrations--describe_output--todo)) + + +### Nested Schema for `security_integrations.describe_output.todo` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + + +### Nested Schema for `security_integrations.show_output` + +Read-Only: + +- `category` (String) +- `comment` (String) +- `created_on` (String) +- `enabled` (Boolean) +- `integration_type` (String) +- `name` (String) diff --git a/docs/data-sources/warehouses.md b/docs/data-sources/warehouses.md index e5f84c0c27..6c548f36a8 100644 --- a/docs/data-sources/warehouses.md +++ b/docs/data-sources/warehouses.md @@ -2,12 +2,12 @@ page_title: "snowflake_warehouses Data Source - terraform-provider-snowflake" subcategory: "" description: |- - Datasource used to get details of filtered warehouses. Filtering is aligned with the current possibilities for [SHOW WAREHOUSES]((https://docs.snowflake.com/en/sql-reference/sql/show-warehouses) query (only like is supported). The results of SHOW, DESCRIBE, and SHOW PARAMETERS IN are encapsulated in one output collection. + Datasource used to get details of filtered warehouses. Filtering is aligned with the current possibilities for SHOW WAREHOUSES https://docs.snowflake.com/en/sql-reference/sql/show-warehouses query (only like is supported). The results of SHOW, DESCRIBE, and SHOW PARAMETERS IN are encapsulated in one output collection. --- # snowflake_warehouses (Data Source) -Datasource used to get details of filtered warehouses. Filtering is aligned with the current possibilities for [SHOW WAREHOUSES]((https://docs.snowflake.com/en/sql-reference/sql/show-warehouses) query (only `like` is supported). The results of SHOW, DESCRIBE, and SHOW PARAMETERS IN are encapsulated in one output collection. +Datasource used to get details of filtered warehouses. Filtering is aligned with the current possibilities for [SHOW WAREHOUSES](https://docs.snowflake.com/en/sql-reference/sql/show-warehouses) query (only `like` is supported). The results of SHOW, DESCRIBE, and SHOW PARAMETERS IN are encapsulated in one output collection. ## Example Usage diff --git a/examples/data-sources/snowflake_security_integrations/data-source.tf b/examples/data-sources/snowflake_security_integrations/data-source.tf new file mode 100644 index 0000000000..38b8f8c682 --- /dev/null +++ b/examples/data-sources/snowflake_security_integrations/data-source.tf @@ -0,0 +1,58 @@ +# Simple usage +data "snowflake_security_integrations" "simple" { +} + +output "simple_output" { + value = data.snowflake_security_integrations.simple.security_integrations +} + +# Filtering (like) +data "snowflake_security_integrations" "like" { + like = "security-integration-name" +} + +output "like_output" { + value = data.snowflake_security_integrations.like.security_integrations +} + +# Filtering by prefix (like) +data "snowflake_security_integrations" "like_prefix" { + like = "prefix%" +} + +output "like_prefix_output" { + value = data.snowflake_security_integrations.like_prefix.security_integrations +} + +# Without additional data (to limit the number of calls make for every found security integration) +data "snowflake_security_integrations" "only_show" { + # with_describe is turned on by default and it calls DESCRIBE SECURITY INTEGRATION for every security integration found and attaches its output to security_integrations.*.describe_output field + with_describe = false +} + +output "only_show_output" { + value = data.snowflake_security_integrations.only_show.security_integrations +} + +# Ensure the number of security_integrations is equal to at least one element (with the use of postcondition) +data "snowflake_security_integrations" "assert_with_postcondition" { + like = "security-integration-name%" + lifecycle { + postcondition { + condition = length(self.security_integrations) > 0 + error_message = "there should be at least one security integration" + } + } +} + +# Ensure the number of security_integrations is equal to at exactly one element (with the use of check block) +check "security_integration_check" { + data "snowflake_security_integrations" "assert_with_check_block" { + like = "security-integration-name" + } + + assert { + condition = length(data.snowflake_security_integrations.assert_with_check_block.security_integrations) == 1 + error_message = "security integrations filtered by '${data.snowflake_security_integrations.assert_with_check_block.like}' returned ${length(data.snowflake_security_integrations.assert_with_check_block.security_integrations)} security integrations where one was expected" + } +} diff --git a/pkg/datasources/databases.go b/pkg/datasources/databases.go index 679c1948e8..cc61d32795 100644 --- a/pkg/datasources/databases.go +++ b/pkg/datasources/databases.go @@ -90,12 +90,11 @@ var databasesSchema = map[string]*schema.Schema{ }, } -// Databases the Snowflake current account resource. func Databases() *schema.Resource { return &schema.Resource{ ReadContext: ReadDatabases, Schema: databasesSchema, - Description: "Datasource used to get details of filtered databases. Filtering is aligned with the current possibilities for [SHOW DATABASES]((https://docs.snowflake.com/en/sql-reference/sql/show-databases) query (`like`, 'starts_with', and `limit` are all supported). The results of SHOW, DESCRIBE, and SHOW PARAMETERS IN are encapsulated in one output collection.", + Description: "Datasource used to get details of filtered databases. Filtering is aligned with the current possibilities for [SHOW DATABASES](https://docs.snowflake.com/en/sql-reference/sql/show-databases) query (`like`, `starts_with`, and `limit` are all supported). The results of SHOW, DESCRIBE, and SHOW PARAMETERS IN are encapsulated in one output collection.", } } diff --git a/pkg/datasources/security_integrations.go b/pkg/datasources/security_integrations.go new file mode 100644 index 0000000000..18a8fd1305 --- /dev/null +++ b/pkg/datasources/security_integrations.go @@ -0,0 +1,102 @@ +package datasources + +import ( + "context" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/schemas" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +var securityIntegrationsSchema = map[string]*schema.Schema{ + "with_describe": { + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "Runs DESC SECURITY INTEGRATION for each security integration returned by SHOW SECURITY INTEGRATIONS. The output of describe is saved to the description field. By default this value is set to true.", + }, + "like": { + Type: schema.TypeString, + Optional: true, + Description: "Filters the output with **case-insensitive** pattern, with support for SQL wildcard characters (`%` and `_`).", + }, + "security_integrations": { + Type: schema.TypeList, + Computed: true, + Description: "Holds the aggregated output of all security integrations details queries.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "show_output": { + Type: schema.TypeList, + Computed: true, + Description: "Holds the output of SHOW SECURITY INTEGRATIONS.", + Elem: &schema.Resource{ + Schema: schemas.ShowSecurityIntegrationSchema, + }, + }, + "describe_output": { + Type: schema.TypeList, + Computed: true, + Description: "Holds the output of DESCRIBE SECURITY INTEGRATIONS.", + Elem: &schema.Resource{ + Schema: schemas.SecurityIntegrationDescribeSchema, + }, + }, + }, + }, + }, +} + +func SecurityIntegrations() *schema.Resource { + return &schema.Resource{ + ReadContext: ReadSecurityIntegrations, + Schema: securityIntegrationsSchema, + Description: "Datasource used to get details of filtered security integrations. Filtering is aligned with the current possibilities for [SHOW SECURITY INTEGRATIONS](https://docs.snowflake.com/en/sql-reference/sql/show-integrations) query (only `like` is supported). The results of SHOW and DESCRIBE are encapsulated in one output collection `security_integrations`.", + } +} + +func ReadSecurityIntegrations(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics { + client := meta.(*provider.Context).Client + showRequest := sdk.NewShowSecurityIntegrationRequest() + + if likePattern, ok := d.GetOk("like"); ok { + showRequest.WithLike(sdk.Like{ + Pattern: sdk.String(likePattern.(string)), + }) + } + + securityIntegrations, err := client.SecurityIntegrations.Show(ctx, showRequest) + if err != nil { + return diag.FromErr(err) + } + d.SetId("security_integrations_read") + + flattenedSecurityIntegrations := make([]map[string]any, len(securityIntegrations)) + + for i, securityIntegration := range securityIntegrations { + securityIntegration := securityIntegration + var securityIntegrationDescriptions []map[string]any + if d.Get("with_describe").(bool) { + descriptions, err := client.SecurityIntegrations.Describe(ctx, securityIntegration.ID()) + if err != nil { + return diag.FromErr(err) + } + securityIntegrationDescriptions = make([]map[string]any, 1) + securityIntegrationDescriptions[0] = schemas.SecurityIntegrationsDescriptionsToSchema(descriptions) + } + + flattenedSecurityIntegrations[i] = map[string]any{ + "show_output": []map[string]any{schemas.SecurityIntegrationToSchema(&securityIntegration)}, + "describe_output": securityIntegrationDescriptions, + } + } + + err = d.Set("security_integrations", flattenedSecurityIntegrations) + if err != nil { + return diag.FromErr(err) + } + + return nil +} diff --git a/pkg/datasources/security_integrations_acceptance_test.go b/pkg/datasources/security_integrations_acceptance_test.go new file mode 100644 index 0000000000..245b4abc3c --- /dev/null +++ b/pkg/datasources/security_integrations_acceptance_test.go @@ -0,0 +1,136 @@ +package datasources_test + +import ( + "maps" + "regexp" + "testing" + + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testenvs" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/hashicorp/terraform-plugin-testing/config" + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/tfversion" +) + +// TODO [SNOW-1348100]: add other security integrations when they are ready +// TODO [SNOW-1348100]: test specific describe properties +func TestAcc_SecurityIntegrations_Scim(t *testing.T) { + _ = testenvs.GetOrSkipTest(t, testenvs.ConfigureClientOnce) + id := acc.TestClient().Ids.RandomAccountObjectIdentifier() + comment := random.Comment() + + configVariables := config.Variables{ + "name": config.StringVariable(id.Name()), + "comment": config.StringVariable(comment), + } + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.ScimSecurityIntegration), + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_SecurityIntegrations/optionals_set"), + ConfigVariables: configVariables, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.snowflake_security_integrations.test", "security_integrations.#", "1"), + + resource.TestCheckResourceAttr("data.snowflake_security_integrations.test", "security_integrations.0.show_output.0.name", id.Name()), + resource.TestCheckResourceAttr("data.snowflake_security_integrations.test", "security_integrations.0.show_output.0.integration_type", "SCIM - GENERIC"), + resource.TestCheckResourceAttr("data.snowflake_security_integrations.test", "security_integrations.0.show_output.0.category", sdk.SecurityIntegrationCategory), + resource.TestCheckResourceAttr("data.snowflake_security_integrations.test", "security_integrations.0.show_output.0.enabled", "false"), + resource.TestCheckResourceAttr("data.snowflake_security_integrations.test", "security_integrations.0.show_output.0.comment", comment), + resource.TestCheckResourceAttrSet("data.snowflake_security_integrations.test", "security_integrations.0.show_output.0.created_on"), + + resource.TestCheckResourceAttr("data.snowflake_security_integrations.test", "security_integrations.0.describe_output.#", "1"), + resource.TestCheckResourceAttr("data.snowflake_security_integrations.test", "security_integrations.0.describe_output.0.todo.#", "1"), + resource.TestCheckResourceAttrSet("data.snowflake_security_integrations.test", "security_integrations.0.describe_output.0.todo.0.value"), + ), + }, + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_SecurityIntegrations/optionals_unset"), + ConfigVariables: configVariables, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.snowflake_security_integrations.test", "security_integrations.#", "1"), + + resource.TestCheckResourceAttr("data.snowflake_security_integrations.test", "security_integrations.0.show_output.0.name", id.Name()), + resource.TestCheckResourceAttr("data.snowflake_security_integrations.test", "security_integrations.0.show_output.0.integration_type", "SCIM - GENERIC"), + resource.TestCheckResourceAttr("data.snowflake_security_integrations.test", "security_integrations.0.show_output.0.category", sdk.SecurityIntegrationCategory), + resource.TestCheckResourceAttr("data.snowflake_security_integrations.test", "security_integrations.0.show_output.0.enabled", "false"), + resource.TestCheckResourceAttr("data.snowflake_security_integrations.test", "security_integrations.0.show_output.0.comment", comment), + resource.TestCheckResourceAttrSet("data.snowflake_security_integrations.test", "security_integrations.0.show_output.0.created_on"), + + resource.TestCheckResourceAttr("data.snowflake_security_integrations.test", "security_integrations.0.describe_output.#", "0"), + ), + }, + }, + }) +} + +func TestAcc_SecurityIntegrations_Filtering(t *testing.T) { + prefix := random.AlphaN(4) + idOne := acc.TestClient().Ids.RandomAccountObjectIdentifierWithPrefix(prefix) + idTwo := acc.TestClient().Ids.RandomAccountObjectIdentifierWithPrefix(prefix) + idThree := acc.TestClient().Ids.RandomAccountObjectIdentifier() + + commonVariables := config.Variables{ + "name_1": config.StringVariable(idOne.Name()), + "name_2": config.StringVariable(idTwo.Name()), + "name_3": config.StringVariable(idThree.Name()), + } + + likeConfig := config.Variables{ + "like": config.StringVariable(idOne.Name()), + } + maps.Copy(likeConfig, commonVariables) + + likeConfig2 := config.Variables{ + "like": config.StringVariable(prefix + "%"), + } + maps.Copy(likeConfig2, commonVariables) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.ScimSecurityIntegration), + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_SecurityIntegrations/like"), + ConfigVariables: likeConfig, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.snowflake_security_integrations.test", "security_integrations.#", "1"), + ), + }, + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_SecurityIntegrations/like"), + ConfigVariables: likeConfig2, + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.snowflake_security_integrations.test", "security_integrations.#", "2"), + ), + }, + }, + }) +} + +func TestAcc_SecurityIntegrations_SecurityIntegrationNotFound_WithPostConditions(t *testing.T) { + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_SecurityIntegrations/non_existing"), + ExpectError: regexp.MustCompile("there should be at least one security integration"), + }, + }, + }) +} diff --git a/pkg/datasources/testdata/TestAcc_SecurityIntegrations/like/test.tf b/pkg/datasources/testdata/TestAcc_SecurityIntegrations/like/test.tf new file mode 100644 index 0000000000..fa33c87d06 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_SecurityIntegrations/like/test.tf @@ -0,0 +1,26 @@ +resource "snowflake_scim_integration" "test_1" { + name = var.name_1 + enabled = false + scim_client = "GENERIC" + run_as_role = "GENERIC_SCIM_PROVISIONER" +} + +resource "snowflake_scim_integration" "test_2" { + name = var.name_2 + enabled = false + scim_client = "GENERIC" + run_as_role = "GENERIC_SCIM_PROVISIONER" +} + +resource "snowflake_scim_integration" "test_3" { + name = var.name_3 + enabled = false + scim_client = "GENERIC" + run_as_role = "GENERIC_SCIM_PROVISIONER" +} + +data "snowflake_security_integrations" "test" { + depends_on = [snowflake_scim_integration.test_1, snowflake_scim_integration.test_2, snowflake_scim_integration.test_3] + + like = var.like +} diff --git a/pkg/datasources/testdata/TestAcc_SecurityIntegrations/like/variables.tf b/pkg/datasources/testdata/TestAcc_SecurityIntegrations/like/variables.tf new file mode 100644 index 0000000000..6bd0278080 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_SecurityIntegrations/like/variables.tf @@ -0,0 +1,15 @@ +variable "name_1" { + type = string +} + +variable "name_2" { + type = string +} + +variable "name_3" { + type = string +} + +variable "like" { + type = string +} diff --git a/pkg/datasources/testdata/TestAcc_SecurityIntegrations/non_existing/test.tf b/pkg/datasources/testdata/TestAcc_SecurityIntegrations/non_existing/test.tf new file mode 100644 index 0000000000..c64655e50a --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_SecurityIntegrations/non_existing/test.tf @@ -0,0 +1,10 @@ +data "snowflake_security_integrations" "test" { + like = "non-existing-security-integration" + + lifecycle { + postcondition { + condition = length(self.security_integrations) > 0 + error_message = "there should be at least one security integration" + } + } +} diff --git a/pkg/datasources/testdata/TestAcc_SecurityIntegrations/optionals_set/test.tf b/pkg/datasources/testdata/TestAcc_SecurityIntegrations/optionals_set/test.tf new file mode 100644 index 0000000000..dbd9db0930 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_SecurityIntegrations/optionals_set/test.tf @@ -0,0 +1,13 @@ +resource "snowflake_scim_integration" "test" { + name = var.name + enabled = false + scim_client = "GENERIC" + run_as_role = "GENERIC_SCIM_PROVISIONER" + comment = var.comment +} + +data "snowflake_security_integrations" "test" { + depends_on = [snowflake_scim_integration.test] + + like = var.name +} diff --git a/pkg/datasources/testdata/TestAcc_SecurityIntegrations/optionals_set/variables.tf b/pkg/datasources/testdata/TestAcc_SecurityIntegrations/optionals_set/variables.tf new file mode 100644 index 0000000000..821eeebe89 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_SecurityIntegrations/optionals_set/variables.tf @@ -0,0 +1,7 @@ +variable "name" { + type = string +} + +variable "comment" { + type = string +} diff --git a/pkg/datasources/testdata/TestAcc_SecurityIntegrations/optionals_unset/test.tf b/pkg/datasources/testdata/TestAcc_SecurityIntegrations/optionals_unset/test.tf new file mode 100644 index 0000000000..e5cc5bddb8 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_SecurityIntegrations/optionals_unset/test.tf @@ -0,0 +1,14 @@ +resource "snowflake_scim_integration" "test" { + name = var.name + enabled = false + scim_client = "GENERIC" + run_as_role = "GENERIC_SCIM_PROVISIONER" + comment = var.comment +} + +data "snowflake_security_integrations" "test" { + depends_on = [snowflake_scim_integration.test] + + with_describe = false + like = var.name +} \ No newline at end of file diff --git a/pkg/datasources/testdata/TestAcc_SecurityIntegrations/optionals_unset/variables.tf b/pkg/datasources/testdata/TestAcc_SecurityIntegrations/optionals_unset/variables.tf new file mode 100644 index 0000000000..821eeebe89 --- /dev/null +++ b/pkg/datasources/testdata/TestAcc_SecurityIntegrations/optionals_unset/variables.tf @@ -0,0 +1,7 @@ +variable "name" { + type = string +} + +variable "comment" { + type = string +} diff --git a/pkg/datasources/warehouses.go b/pkg/datasources/warehouses.go index ed3bd1d504..bd431093b2 100644 --- a/pkg/datasources/warehouses.go +++ b/pkg/datasources/warehouses.go @@ -67,7 +67,7 @@ func Warehouses() *schema.Resource { return &schema.Resource{ ReadContext: ReadWarehouses, Schema: warehousesSchema, - Description: "Datasource used to get details of filtered warehouses. Filtering is aligned with the current possibilities for [SHOW WAREHOUSES]((https://docs.snowflake.com/en/sql-reference/sql/show-warehouses) query (only `like` is supported). The results of SHOW, DESCRIBE, and SHOW PARAMETERS IN are encapsulated in one output collection.", + Description: "Datasource used to get details of filtered warehouses. Filtering is aligned with the current possibilities for [SHOW WAREHOUSES](https://docs.snowflake.com/en/sql-reference/sql/show-warehouses) query (only `like` is supported). The results of SHOW, DESCRIBE, and SHOW PARAMETERS IN are encapsulated in one output collection.", } } diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index 0c4686249a..3dadac4473 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -513,6 +513,7 @@ func getDataSources() map[string]*schema.Resource { "snowflake_roles": datasources.Roles(), "snowflake_row_access_policies": datasources.RowAccessPolicies(), "snowflake_schemas": datasources.Schemas(), + "snowflake_security_integrations": datasources.SecurityIntegrations(), "snowflake_sequences": datasources.Sequences(), "snowflake_shares": datasources.Shares(), "snowflake_stages": datasources.Stages(), diff --git a/pkg/schemas/common_types.go b/pkg/schemas/common_types.go index 36db0c3692..3193186928 100644 --- a/pkg/schemas/common_types.go +++ b/pkg/schemas/common_types.go @@ -18,25 +18,6 @@ var DescribePropertyListSchema = &schema.Schema{ Type: schema.TypeList, Computed: true, Elem: &schema.Resource{ - Schema: DescribePropertySchema, - }, -} - -var DescribePropertySchema = map[string]*schema.Schema{ - "name": { - Type: schema.TypeString, - Computed: true, - }, - "type": { - Type: schema.TypeString, - Computed: true, - }, - "value": { - Type: schema.TypeString, - Computed: true, - }, - "default": { - Type: schema.TypeString, - Computed: true, + Schema: ShowSecurityIntegrationPropertySchema, }, } diff --git a/pkg/schemas/gen/main/main.go b/pkg/schemas/gen/main/main.go index cac8db67bc..23e68ef4f3 100644 --- a/pkg/schemas/gen/main/main.go +++ b/pkg/schemas/gen/main/main.go @@ -19,8 +19,9 @@ func main() { file := os.Getenv("GOFILE") fmt.Printf("Running generator on %s with args %#v\n", file, os.Args[1:]) - allStructsDetails := make([]gen.Struct, len(gen.SdkShowResultStructs)) - for idx, s := range gen.SdkShowResultStructs { + allObjects := append(gen.SdkShowResultStructs, gen.AdditionalStructs...) + allStructsDetails := make([]gen.Struct, len(allObjects)) + for idx, s := range allObjects { allStructsDetails[idx] = gen.ExtractStructDetails(s) } diff --git a/pkg/schemas/gen/sdk_show_result_structs.go b/pkg/schemas/gen/sdk_show_result_structs.go index 537c1fcfd8..ea2e977160 100644 --- a/pkg/schemas/gen/sdk_show_result_structs.go +++ b/pkg/schemas/gen/sdk_show_result_structs.go @@ -52,3 +52,9 @@ var SdkShowResultStructs = []any{ sdk.View{}, sdk.Warehouse{}, } + +// TODO [SNOW-1501905]: currently all this structs have the "Show" added to the schema, while these are not show outputs +// TODO [SNOW-1501905]: temporary struct, may be refactored with addition to generation of describe results; for now used to some structs needing a schema representation +var AdditionalStructs = []any{ + sdk.SecurityIntegrationProperty{}, +} diff --git a/pkg/schemas/scim_security_integration.go b/pkg/schemas/scim_security_integration.go index a07f9ffb61..f21083bd4e 100644 --- a/pkg/schemas/scim_security_integration.go +++ b/pkg/schemas/scim_security_integration.go @@ -21,20 +21,14 @@ var _ = DescribeScimSecurityIntegrationSchema func ScimSecurityIntegrationPropertiesToSchema(securityIntegrationProperties []sdk.SecurityIntegrationProperty) map[string]any { securityIntegrationSchema := make(map[string]any) for _, securityIntegrationProperty := range securityIntegrationProperties { + securityIntegrationProperty := securityIntegrationProperty switch securityIntegrationProperty.Name { case "ENABLED", "NETWORK_POLICY", "RUN_AS_ROLE", "SYNC_PASSWORD", "COMMENT": - securityIntegrationSchema[strings.ToLower(securityIntegrationProperty.Name)] = []map[string]any{ - { - "name": securityIntegrationProperty.Name, - "type": securityIntegrationProperty.Type, - "value": securityIntegrationProperty.Value, - "default": securityIntegrationProperty.Default, - }, - } + securityIntegrationSchema[strings.ToLower(securityIntegrationProperty.Name)] = []map[string]any{SecurityIntegrationPropertyToSchema(&securityIntegrationProperty)} } } return securityIntegrationSchema diff --git a/pkg/schemas/security_integration.go b/pkg/schemas/security_integration.go new file mode 100644 index 0000000000..e278a293a5 --- /dev/null +++ b/pkg/schemas/security_integration.go @@ -0,0 +1,21 @@ +package schemas + +import ( + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// TODO [SNOW-1348100]: multiple PRs touching the security integrations are in progress, this should be filled by all the possible properties (the mapping method below should be too) +var SecurityIntegrationDescribeSchema = map[string]*schema.Schema{ + "todo": DescribePropertyListSchema, +} + +func SecurityIntegrationsDescriptionsToSchema(descriptions []sdk.SecurityIntegrationProperty) map[string]any { + securityIntegrationProperties := make(map[string]any) + for _, desc := range descriptions { + desc := desc + propertySchema := SecurityIntegrationPropertyToSchema(&desc) + securityIntegrationProperties["todo"] = []map[string]any{propertySchema} + } + return securityIntegrationProperties +} diff --git a/pkg/schemas/security_integration_property_gen.go b/pkg/schemas/security_integration_property_gen.go new file mode 100644 index 0000000000..972481dd33 --- /dev/null +++ b/pkg/schemas/security_integration_property_gen.go @@ -0,0 +1,41 @@ +// Code generated by sdk-to-schema generator; DO NOT EDIT. + +package schemas + +import ( + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// ShowSecurityIntegrationPropertySchema represents output of SHOW query for the single SecurityIntegrationProperty. +var ShowSecurityIntegrationPropertySchema = map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Computed: true, + }, + "type": { + Type: schema.TypeString, + Computed: true, + }, + "value": { + Type: schema.TypeString, + Computed: true, + }, + "default": { + Type: schema.TypeString, + Computed: true, + }, +} + +var _ = ShowSecurityIntegrationPropertySchema + +func SecurityIntegrationPropertyToSchema(securityIntegrationProperty *sdk.SecurityIntegrationProperty) map[string]any { + securityIntegrationPropertySchema := make(map[string]any) + securityIntegrationPropertySchema["name"] = securityIntegrationProperty.Name + securityIntegrationPropertySchema["type"] = securityIntegrationProperty.Type + securityIntegrationPropertySchema["value"] = securityIntegrationProperty.Value + securityIntegrationPropertySchema["default"] = securityIntegrationProperty.Default + return securityIntegrationPropertySchema +} + +var _ = SecurityIntegrationPropertyToSchema diff --git a/pkg/sdk/security_integrations_gen.go b/pkg/sdk/security_integrations_gen.go index 3d64607a73..c02f1979eb 100644 --- a/pkg/sdk/security_integrations_gen.go +++ b/pkg/sdk/security_integrations_gen.go @@ -560,6 +560,10 @@ type SecurityIntegration struct { CreatedOn time.Time } +func (s *SecurityIntegration) ID() AccountObjectIdentifier { + return NewAccountObjectIdentifier(s.Name) +} + func (s *SecurityIntegration) SubType() (string, error) { typeParts := strings.Split(s.IntegrationType, "-") if len(typeParts) < 2 {