diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index fb35776890..9764cd426d 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -46,6 +46,45 @@ Added a new datasource enabling querying and filtering all types of security int 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_external_oauth_integration resource changes + +#### *(behavior change)* Renamed fields +Renamed fields: +- `type` to `external_oauth_type` +- `issuer` to `external_oauth_issuer` +- `token_user_mapping_claims` to `external_oauth_token_user_mapping_claim` +- `snowflake_user_mapping_attribute` to `external_oauth_snowflake_user_mapping_attribute` +- `scope_mapping_attribute` to `external_oauth_scope_mapping_attribute` +- `jws_keys_urls` to `external_oauth_jws_keys_url` +- `rsa_public_key` to `external_oauth_rsa_public_key` +- `rsa_public_key_2` to `external_oauth_rsa_public_key_2` +- `blocked_roles` to `external_oauth_blocked_roles_list` +- `allowed_roles` to `external_oauth_allowed_roles_list` +- `audience_urls` to `external_oauth_audience_list` +- `any_role_mode` to `external_oauth_any_role_mode` +- `scope_delimiter` to `external_oauth_scope_delimiter` +to align with Snowflake docs. Please rename this field in your configuration files. State will be migrated automatically. + +#### *(behavior change)* Force new for multiple attributes after removing from config +Conditional force new was added for the following attributes when they are removed from config. There are no alter statements supporting UNSET on these fields. +- `external_oauth_rsa_public_key` +- `external_oauth_rsa_public_key_2` +- `external_oauth_scope_mapping_attribute` +- `external_oauth_jws_keys_url` +- `external_oauth_token_user_mapping_claim` + +#### *(behavior change)* Conflicting fields +Fields listed below can not be set at the same time in Snowflake. They are marked as conflicting fields. +- `external_oauth_jws_keys_url` <-> `external_oauth_rsa_public_key` +- `external_oauth_jws_keys_url` <-> `external_oauth_rsa_public_key_2` +- `external_oauth_allowed_roles_list` <-> `external_oauth_blocked_roles_list` + +#### *(behavior change)* Changed diff suppress for some fields +The fields listed below had diff suppress which removed '-' from strings. Now, this behavior is removed, so if you had '-' in these strings, please remove them. Note that '-' in these values is not allowed by Snowflake. +- `external_oauth_snowflake_user_mapping_attribute` +- `external_oauth_type` +- `external_oauth_any_role_mode` + ### snowflake_scim_integration resource changes #### *(behavior change)* Changed behavior of `sync_password` diff --git a/docs/resources/external_oauth_integration.md b/docs/resources/external_oauth_integration.md index db2edb22e0..bb4bf25f1f 100644 --- a/docs/resources/external_oauth_integration.md +++ b/docs/resources/external_oauth_integration.md @@ -2,25 +2,59 @@ page_title: "snowflake_external_oauth_integration Resource - terraform-provider-snowflake" subcategory: "" description: |- - An External OAuth security integration allows a client to use a third-party authorization server to obtain the access tokens needed to interact with Snowflake. + Resource used to manage external oauth security integrations. For more information, check documentation https://docs.snowflake.com/en/sql-reference/sql/create-security-integration-oauth-external. --- +!> **V1 release candidate** This resource was reworked and is a release candidate for the V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0920--v0930) to use it. + # snowflake_external_oauth_integration (Resource) -An External OAuth security integration allows a client to use a third-party authorization server to obtain the access tokens needed to interact with Snowflake. +Resource used to manage external oauth security integrations. For more information, check [documentation](https://docs.snowflake.com/en/sql-reference/sql/create-security-integration-oauth-external). ## Example Usage ```terraform -resource "snowflake_external_oauth_integration" "azure" { - name = "AZURE_POWERBI" - type = "AZURE" - enabled = true - issuer = "https://sts.windows.net/00000000-0000-0000-0000-000000000000" - snowflake_user_mapping_attribute = "LOGIN_NAME" - jws_keys_urls = ["https://login.windows.net/common/discovery/keys"] - audience_urls = ["https://analysis.windows.net/powerbi/connector/Snowflake"] - token_user_mapping_claims = ["upn"] +# basic resource +resource "snowflake_external_oauth_integration" "test" { + enabled = true + external_oauth_issuer = "issuer" + external_oauth_snowflake_user_mapping_attribute = "LOGIN_NAME" + external_oauth_token_user_mapping_claim = ["upn"] + name = "test" + external_oauth_type = "CUSTOM" +} +# resource with all fields set (jws keys url and allowed roles) +resource "snowflake_external_oauth_integration" "test" { + comment = "comment" + enabled = true + external_oauth_allowed_roles_list = ["user1"] + external_oauth_any_role_mode = "ENABLED" + external_oauth_audience_list = ["https://example.com"] + external_oauth_issuer = "issuer" + external_oauth_jws_keys_url = ["https://example.com"] + external_oauth_scope_delimiter = "," + external_oauth_scope_mapping_attribute = "scope" + external_oauth_snowflake_user_mapping_attribute = "LOGIN_NAME" + external_oauth_token_user_mapping_claim = ["upn"] + name = "test" + external_oauth_type = "CUSTOM" +} +# resource with all fields set (rsa public keys and blocked roles) +resource "snowflake_external_oauth_integration" "test" { + comment = "comment" + enabled = true + external_oauth_any_role_mode = "ENABLED" + external_oauth_audience_list = ["https://example.com"] + external_oauth_blocked_roles_list = ["user1"] + external_oauth_issuer = "issuer" + external_oauth_rsa_public_key = file("key.pem") + external_oauth_rsa_public_key_2 = file("key2.pem") + external_oauth_scope_delimiter = "," + external_oauth_scope_mapping_attribute = "scope" + external_oauth_snowflake_user_mapping_attribute = "LOGIN_NAME" + external_oauth_token_user_mapping_claim = ["upn"] + name = "test" + external_oauth_type = "CUSTOM" } ``` @@ -30,34 +64,231 @@ resource "snowflake_external_oauth_integration" "azure" { ### Required - `enabled` (Boolean) Specifies whether to initiate operation of the integration or suspend it. -- `issuer` (String) Specifies the URL to define the OAuth 2.0 authorization server. +- `external_oauth_issuer` (String) Specifies the URL to define the OAuth 2.0 authorization server. +- `external_oauth_snowflake_user_mapping_attribute` (String) Indicates which Snowflake user record attribute should be used to map the access token to a Snowflake user record. Valid values are (case-insensitive): `LOGIN_NAME` | `EMAIL_ADDRESS`. +- `external_oauth_token_user_mapping_claim` (Set of String) Specifies the access token claim or claims that can be used to map the access token to a Snowflake user record. If removed from the config, the resource is recreated. +- `external_oauth_type` (String) Specifies the OAuth 2.0 authorization server to be Okta, Microsoft Azure AD, Ping Identity PingFederate, or a Custom OAuth 2.0 authorization server. Valid values are (case-insensitive): `OKTA` | `AZURE` | `PING_FEDERATE` | `CUSTOM`. - `name` (String) Specifies the name of the External Oath integration. This name follows the rules for Object Identifiers. The name should be unique among security integrations in your account. -- `snowflake_user_mapping_attribute` (String) Indicates which Snowflake user record attribute should be used to map the access token to a Snowflake user record. -- `token_user_mapping_claims` (Set of String) Specifies the access token claim or claims that can be used to map the access token to a Snowflake user record. -- `type` (String) Specifies the OAuth 2.0 authorization server to be Okta, Microsoft Azure AD, Ping Identity PingFederate, or a Custom OAuth 2.0 authorization server. ### Optional -- `allowed_roles` (Set of String) Specifies the list of roles that the client can set as the primary role. -- `any_role_mode` (String) Specifies whether the OAuth client or user can use a role that is not defined in the OAuth access token. -- `audience_urls` (Set of String) Specifies additional values that can be used for the access token's audience validation on top of using the Customer's Snowflake Account URL -- `blocked_roles` (Set of String) Specifies the list of roles that a client cannot set as the primary role. Do not include ACCOUNTADMIN, ORGADMIN or SECURITYADMIN as they are already implicitly enforced and will cause in-place updates. - `comment` (String) Specifies a comment for the OAuth integration. -- `jws_keys_urls` (Set of String) Specifies the endpoint or a list of endpoints from which to download public keys or certificates to validate an External OAuth access token. The maximum number of URLs that can be specified in the list is 3. -- `rsa_public_key` (String) Specifies a Base64-encoded RSA public key, without the -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY----- headers. -- `rsa_public_key_2` (String) Specifies a second RSA public key, without the -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY----- headers. Used for key rotation. -- `scope_delimiter` (String) Specifies the scope delimiter in the authorization token. -- `scope_mapping_attribute` (String) Specifies the access token claim to map the access token to an account role. +- `external_oauth_allowed_roles_list` (Set of String) Specifies the list of roles that the client can set as the primary role. +- `external_oauth_any_role_mode` (String) Specifies whether the OAuth client or user can use a role that is not defined in the OAuth access token. Valid values are (case-insensitive): `DISABLE` | `ENABLE` | `ENABLE_FOR_PRIVILEGE`. +- `external_oauth_audience_list` (Set of String) Specifies additional values that can be used for the access token's audience validation on top of using the Customer's Snowflake Account URL +- `external_oauth_blocked_roles_list` (Set of String) Specifies the list of roles that a client cannot set as the primary role. By default, this list includes the ACCOUNTADMIN, ORGADMIN and SECURITYADMIN roles. To remove these privileged roles from the list, use the ALTER ACCOUNT command to set the EXTERNAL_OAUTH_ADD_PRIVILEGED_ROLES_TO_BLOCKED_LIST account parameter to FALSE. +- `external_oauth_jws_keys_url` (Set of String) Specifies the endpoint or a list of endpoints from which to download public keys or certificates to validate an External OAuth access token. The maximum number of URLs that can be specified in the list is 3. If removed from the config, the resource is recreated. +- `external_oauth_rsa_public_key` (String) Specifies a Base64-encoded RSA public key, without the -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY----- headers. If removed from the config, the resource is recreated. +- `external_oauth_rsa_public_key_2` (String) Specifies a second RSA public key, without the -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY----- headers. Used for key rotation. If removed from the config, the resource is recreated. +- `external_oauth_scope_delimiter` (String) Specifies the scope delimiter in the authorization token. +- `external_oauth_scope_mapping_attribute` (String) Specifies the access token claim to map the access token to an account role. If removed from the config, the resource is recreated. ### Read-Only -- `created_on` (String) Date and time when the External OAUTH integration was created. +- `describe_output` (List of Object) Outputs the result of `DESCRIBE SECURITY INTEGRATIONS` for the given security integration. (see [below for nested schema](#nestedatt--describe_output)) - `id` (String) The ID of this resource. +- `related_parameters` (List of Object) Paramteres related to this security integration. (see [below for nested schema](#nestedatt--related_parameters)) +- `show_output` (List of Object) Outputs the result of `SHOW SECURITY INTEGRATIONS` for the given security integration. (see [below for nested schema](#nestedatt--show_output)) + + +### Nested Schema for `describe_output` + +Read-Only: + +- `comment` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--comment)) +- `enabled` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--enabled)) +- `external_oauth_allowed_roles_list` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--external_oauth_allowed_roles_list)) +- `external_oauth_any_role_mode` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--external_oauth_any_role_mode)) +- `external_oauth_audience_list` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--external_oauth_audience_list)) +- `external_oauth_blocked_roles_list` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--external_oauth_blocked_roles_list)) +- `external_oauth_issuer` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--external_oauth_issuer)) +- `external_oauth_jws_keys_url` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--external_oauth_jws_keys_url)) +- `external_oauth_rsa_public_key` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--external_oauth_rsa_public_key)) +- `external_oauth_rsa_public_key_2` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--external_oauth_rsa_public_key_2)) +- `external_oauth_scope_delimiter` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--external_oauth_scope_delimiter)) +- `external_oauth_snowflake_user_mapping_attribute` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--external_oauth_snowflake_user_mapping_attribute)) +- `external_oauth_token_user_mapping_claim` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--external_oauth_token_user_mapping_claim)) + + +### Nested Schema for `describe_output.comment` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.enabled` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.external_oauth_allowed_roles_list` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.external_oauth_any_role_mode` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.external_oauth_audience_list` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.external_oauth_blocked_roles_list` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.external_oauth_issuer` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.external_oauth_jws_keys_url` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.external_oauth_rsa_public_key` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.external_oauth_rsa_public_key_2` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.external_oauth_scope_delimiter` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.external_oauth_snowflake_user_mapping_attribute` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + +### Nested Schema for `describe_output.external_oauth_token_user_mapping_claim` + +Read-Only: + +- `default` (String) +- `name` (String) +- `type` (String) +- `value` (String) + + + + +### Nested Schema for `related_parameters` + +Read-Only: + +- `external_oauth_add_privileged_roles_to_blocked_list` (List of Object) (see [below for nested schema](#nestedobjatt--related_parameters--external_oauth_add_privileged_roles_to_blocked_list)) + + +### Nested Schema for `related_parameters.external_oauth_add_privileged_roles_to_blocked_list` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `value` (String) + + + + +### Nested Schema for `show_output` + +Read-Only: + +- `category` (String) +- `comment` (String) +- `created_on` (String) +- `enabled` (Boolean) +- `integration_type` (String) +- `name` (String) ## Import Import is supported using the following syntax: ```shell -terraform import snowflake_external_oauth_integration.example name +terraform import snowflake_external_oauth_integration.example "name" ``` diff --git a/examples/resources/snowflake_external_oauth_integration/import.sh b/examples/resources/snowflake_external_oauth_integration/import.sh index feb48d066e..8029ac973e 100644 --- a/examples/resources/snowflake_external_oauth_integration/import.sh +++ b/examples/resources/snowflake_external_oauth_integration/import.sh @@ -1 +1 @@ -terraform import snowflake_external_oauth_integration.example name +terraform import snowflake_external_oauth_integration.example "name" diff --git a/examples/resources/snowflake_external_oauth_integration/resource.tf b/examples/resources/snowflake_external_oauth_integration/resource.tf index 269e02f8f1..1e99d38bf3 100644 --- a/examples/resources/snowflake_external_oauth_integration/resource.tf +++ b/examples/resources/snowflake_external_oauth_integration/resource.tf @@ -1,10 +1,42 @@ -resource "snowflake_external_oauth_integration" "azure" { - name = "AZURE_POWERBI" - type = "AZURE" - enabled = true - issuer = "https://sts.windows.net/00000000-0000-0000-0000-000000000000" - snowflake_user_mapping_attribute = "LOGIN_NAME" - jws_keys_urls = ["https://login.windows.net/common/discovery/keys"] - audience_urls = ["https://analysis.windows.net/powerbi/connector/Snowflake"] - token_user_mapping_claims = ["upn"] -} \ No newline at end of file +# basic resource +resource "snowflake_external_oauth_integration" "test" { + enabled = true + external_oauth_issuer = "issuer" + external_oauth_snowflake_user_mapping_attribute = "LOGIN_NAME" + external_oauth_token_user_mapping_claim = ["upn"] + name = "test" + external_oauth_type = "CUSTOM" +} +# resource with all fields set (jws keys url and allowed roles) +resource "snowflake_external_oauth_integration" "test" { + comment = "comment" + enabled = true + external_oauth_allowed_roles_list = ["user1"] + external_oauth_any_role_mode = "ENABLED" + external_oauth_audience_list = ["https://example.com"] + external_oauth_issuer = "issuer" + external_oauth_jws_keys_url = ["https://example.com"] + external_oauth_scope_delimiter = "," + external_oauth_scope_mapping_attribute = "scope" + external_oauth_snowflake_user_mapping_attribute = "LOGIN_NAME" + external_oauth_token_user_mapping_claim = ["upn"] + name = "test" + external_oauth_type = "CUSTOM" +} +# resource with all fields set (rsa public keys and blocked roles) +resource "snowflake_external_oauth_integration" "test" { + comment = "comment" + enabled = true + external_oauth_any_role_mode = "ENABLED" + external_oauth_audience_list = ["https://example.com"] + external_oauth_blocked_roles_list = ["user1"] + external_oauth_issuer = "issuer" + external_oauth_rsa_public_key = file("key.pem") + external_oauth_rsa_public_key_2 = file("key2.pem") + external_oauth_scope_delimiter = "," + external_oauth_scope_mapping_attribute = "scope" + external_oauth_snowflake_user_mapping_attribute = "LOGIN_NAME" + external_oauth_token_user_mapping_claim = ["upn"] + name = "test" + external_oauth_type = "CUSTOM" +} diff --git a/go.mod b/go.mod index b5f3159a0b..1ff88672d3 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,6 @@ require ( github.com/avast/retry-go v3.0.0+incompatible github.com/brianvoe/gofakeit/v6 v6.28.0 github.com/buger/jsonparser v1.1.1 - github.com/google/uuid v1.6.0 github.com/gookit/color v1.5.4 github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 github.com/hashicorp/go-uuid v1.0.3 diff --git a/pkg/acceptance/check_destroy.go b/pkg/acceptance/check_destroy.go index ef663fdf20..5483146d8b 100644 --- a/pkg/acceptance/check_destroy.go +++ b/pkg/acceptance/check_destroy.go @@ -91,6 +91,9 @@ var showByIdFunctions = map[resources.Resource]showByIdFunc{ resources.ExternalFunction: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { return runShowById(ctx, id, client.ExternalFunctions.ShowByID) }, + resources.ExternalOauthSecurityIntegration: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { + return runShowById(ctx, id, client.SecurityIntegrations.ShowByID) + }, resources.ExternalTable: func(ctx context.Context, client *sdk.Client, id sdk.ObjectIdentifier) error { return runShowById(ctx, id, client.ExternalTables.ShowByID) }, diff --git a/pkg/acceptance/helpers/security_integration_client.go b/pkg/acceptance/helpers/security_integration_client.go index 1e400b4f3f..23b84c500f 100644 --- a/pkg/acceptance/helpers/security_integration_client.go +++ b/pkg/acceptance/helpers/security_integration_client.go @@ -25,6 +25,14 @@ func (c *SecurityIntegrationClient) client() sdk.SecurityIntegrations { return c.context.client.SecurityIntegrations } +func (c *SecurityIntegrationClient) UpdateExternalOauth(t *testing.T, request *sdk.AlterExternalOauthSecurityIntegrationRequest) { + t.Helper() + ctx := context.Background() + + err := c.client().AlterExternalOauth(ctx, request) + require.NoError(t, err) +} + func (c *SecurityIntegrationClient) CreateSaml2(t *testing.T, id sdk.AccountObjectIdentifier) (*sdk.SecurityIntegration, func()) { t.Helper() return c.CreateSaml2WithRequest(t, sdk.NewCreateSaml2SecurityIntegrationRequest(id, c.ids.Alpha(), "https://example.com", "Custom", random.GenerateX509(t))) diff --git a/pkg/provider/resources/resources.go b/pkg/provider/resources/resources.go index db621745c8..07825cdad0 100644 --- a/pkg/provider/resources/resources.go +++ b/pkg/provider/resources/resources.go @@ -14,6 +14,7 @@ const ( EmailNotificationIntegration resource = "snowflake_email_notification_integration" ExternalFunction resource = "snowflake_external_function" ExternalTable resource = "snowflake_external_table" + ExternalOauthSecurityIntegration resource = "snowflake_external_oauth_security_integration" FailoverGroup resource = "snowflake_failover_group" FileFormat resource = "snowflake_file_format" Function resource = "snowflake_function" diff --git a/pkg/resources/custom_diffs.go b/pkg/resources/custom_diffs.go index 060c43df5c..191f1f1081 100644 --- a/pkg/resources/custom_diffs.go +++ b/pkg/resources/custom_diffs.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/collections" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" diff --git a/pkg/resources/diff_suppressions.go b/pkg/resources/diff_suppressions.go index 3e17034d46..f990094c0d 100644 --- a/pkg/resources/diff_suppressions.go +++ b/pkg/resources/diff_suppressions.go @@ -3,7 +3,10 @@ package resources import ( "fmt" "log" + "slices" + "strings" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" ) @@ -80,3 +83,36 @@ func SuppressIfAny(diffSuppressFunctions ...schema.SchemaDiffSuppressFunc) schem return suppress } } + +func IgnoreValuesFromSetIfParamSet(key, param string, values []string) schema.SchemaDiffSuppressFunc { + return func(k, old, new string, d *schema.ResourceData) bool { + params := d.Get(RelatedParametersAttributeName).([]any) + if len(params) == 0 { + return false + } + result := params[0].(map[string]any) + param := result[strings.ToLower(param)].([]any) + value := param[0].(map[string]any)["value"] + if !helpers.StringToBool(value.(string)) { + return false + } + if k == key+".#" { + old, new := d.GetChange(key) + var numOld, numNew int + oldList := expandStringList(old.(*schema.Set).List()) + newList := expandStringList(new.(*schema.Set).List()) + for _, v := range oldList { + if !slices.Contains(values, v) { + numOld++ + } + } + for _, v := range newList { + if !slices.Contains(values, v) { + numNew++ + } + } + return numOld == numNew + } + return slices.Contains(values, old) + } +} diff --git a/pkg/resources/external_oauth_integration.go b/pkg/resources/external_oauth_integration.go index 0cea6460c0..8e9804acdb 100644 --- a/pkg/resources/external_oauth_integration.go +++ b/pkg/resources/external_oauth_integration.go @@ -1,17 +1,26 @@ package resources import ( - "database/sql" + "context" + "errors" "fmt" - "strings" + "reflect" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/collections" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/logging" "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/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake" + "github.com/hashicorp/go-cty/cty" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) +var privilegedRoles = []string{"ACCOUNTADMIN", "ORGADMIN", "SECURITYADMIN"} + var oauthExternalIntegrationSchema = map[string]*schema.Schema{ "name": { Type: schema.TypeString, @@ -19,570 +28,698 @@ var oauthExternalIntegrationSchema = map[string]*schema.Schema{ ForceNew: true, Description: "Specifies the name of the External Oath integration. This name follows the rules for Object Identifiers. The name should be unique among security integrations in your account.", }, - "type": { - Type: schema.TypeString, - Required: true, - Description: "Specifies the OAuth 2.0 authorization server to be Okta, Microsoft Azure AD, Ping Identity PingFederate, or a Custom OAuth 2.0 authorization server.", - ValidateFunc: validation.StringInSlice([]string{ - string(snowflake.Okta), - string(snowflake.Azure), - string(snowflake.PingFederate), - string(snowflake.Custom), - }, true), - DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - normalize := func(s string) string { - return strings.ToUpper(strings.ReplaceAll(s, "-", "")) - } - return normalize(old) == normalize(new) - }, + "external_oauth_type": { + Type: schema.TypeString, + Required: true, + Description: fmt.Sprintf("Specifies the OAuth 2.0 authorization server to be Okta, Microsoft Azure AD, Ping Identity PingFederate, or a Custom OAuth 2.0 authorization server. Valid values are (case-insensitive): %s.", possibleValuesListed(sdk.AsStringList(sdk.AllExternalOauthSecurityIntegrationTypes))), + ValidateDiagFunc: sdkValidation(sdk.ToExternalOauthSecurityIntegrationTypeOption), + DiffSuppressFunc: NormalizeAndCompare(sdk.ToExternalOauthSecurityIntegrationTypeOption), }, "enabled": { Type: schema.TypeBool, Required: true, Description: "Specifies whether to initiate operation of the integration or suspend it.", }, - "issuer": { + "external_oauth_issuer": { Type: schema.TypeString, Required: true, Description: "Specifies the URL to define the OAuth 2.0 authorization server.", }, - "token_user_mapping_claims": { + "external_oauth_token_user_mapping_claim": { Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, Required: true, - Description: "Specifies the access token claim or claims that can be used to map the access token to a Snowflake user record.", + Description: "Specifies the access token claim or claims that can be used to map the access token to a Snowflake user record. If removed from the config, the resource is recreated.", }, - "scope_mapping_attribute": { - Type: schema.TypeString, - Optional: true, - Description: "Specifies the access token claim to map the access token to an account role.", + "external_oauth_snowflake_user_mapping_attribute": { + Type: schema.TypeString, + Required: true, + Description: fmt.Sprintf("Indicates which Snowflake user record attribute should be used to map the access token to a Snowflake user record. Valid values are (case-insensitive): %s.", possibleValuesListed(sdk.AsStringList(sdk.AllExternalOauthSecurityIntegrationSnowflakeUserMappingAttributes))), + ValidateDiagFunc: sdkValidation(sdk.ToExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeOption), + DiffSuppressFunc: NormalizeAndCompare(sdk.ToExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeOption), }, - "snowflake_user_mapping_attribute": { - Type: schema.TypeString, - Required: true, - Description: "Indicates which Snowflake user record attribute should be used to map the access token to a Snowflake user record.", - ValidateFunc: validation.StringInSlice([]string{ - string(snowflake.LoginName), - string(snowflake.EmailAddress), - }, true), - DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - normalize := func(s string) string { - return strings.ToUpper(strings.ReplaceAll(s, "-", "")) - } - return normalize(old) == normalize(new) - }, + "external_oauth_jws_keys_url": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + MaxItems: 3, + Optional: true, + ConflictsWith: []string{"external_oauth_rsa_public_key", "external_oauth_rsa_public_key_2"}, + Description: "Specifies the endpoint or a list of endpoints from which to download public keys or certificates to validate an External OAuth access token. The maximum number of URLs that can be specified in the list is 3. If removed from the config, the resource is recreated.", }, - "jws_keys_urls": { - Type: schema.TypeSet, - Elem: &schema.Schema{Type: schema.TypeString}, - MaxItems: 3, - Optional: true, - Description: "Specifies the endpoint or a list of endpoints from which to download public keys or certificates to validate an External OAuth access token. The maximum number of URLs that can be specified in the list is 3.", + "external_oauth_rsa_public_key": { + Type: schema.TypeString, + Optional: true, + Description: "Specifies a Base64-encoded RSA public key, without the -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY----- headers. If removed from the config, the resource is recreated.", + DiffSuppressFunc: ignoreTrimSpaceSuppressFunc, + ConflictsWith: []string{"external_oauth_jws_keys_url"}, }, - "rsa_public_key": { - Type: schema.TypeString, - Optional: true, - Description: "Specifies a Base64-encoded RSA public key, without the -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY----- headers.", + "external_oauth_rsa_public_key_2": { + Type: schema.TypeString, + Optional: true, + Description: "Specifies a second RSA public key, without the -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY----- headers. Used for key rotation. If removed from the config, the resource is recreated.", + DiffSuppressFunc: ignoreTrimSpaceSuppressFunc, + ConflictsWith: []string{"external_oauth_jws_keys_url"}, }, - "rsa_public_key_2": { - Type: schema.TypeString, - Optional: true, - Description: "Specifies a second RSA public key, without the -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY----- headers. Used for key rotation.", + "external_oauth_blocked_roles_list": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + Description: withPrivilegedRolesDescription("Specifies the list of roles that a client cannot set as the primary role.", string(sdk.AccountParameterExternalOAuthAddPrivilegedRolesToBlockedList)), + DiffSuppressFunc: IgnoreValuesFromSetIfParamSet("external_oauth_blocked_roles_list", string(sdk.AccountParameterExternalOAuthAddPrivilegedRolesToBlockedList), privilegedRoles), + ConflictsWith: []string{"external_oauth_allowed_roles_list"}, }, - "blocked_roles": { - Type: schema.TypeSet, - Elem: &schema.Schema{Type: schema.TypeString}, - Optional: true, - Description: "Specifies the list of roles that a client cannot set as the primary role. Do not include ACCOUNTADMIN, ORGADMIN or SECURITYADMIN as they are already implicitly enforced and will cause in-place updates.", + "external_oauth_allowed_roles_list": { + Type: schema.TypeSet, + Elem: &schema.Schema{Type: schema.TypeString}, + Optional: true, + Description: "Specifies the list of roles that the client can set as the primary role.", + ConflictsWith: []string{"external_oauth_blocked_roles_list"}, }, - "allowed_roles": { - Type: schema.TypeSet, - Elem: &schema.Schema{Type: schema.TypeString}, - Optional: true, - Description: "Specifies the list of roles that the client can set as the primary role.", - }, - "audience_urls": { + "external_oauth_audience_list": { Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, Description: "Specifies additional values that can be used for the access token's audience validation on top of using the Customer's Snowflake Account URL ", }, - "any_role_mode": { + "external_oauth_any_role_mode": { + Type: schema.TypeString, + Optional: true, + Description: fmt.Sprintf("Specifies whether the OAuth client or user can use a role that is not defined in the OAuth access token. Valid values are (case-insensitive): %s.", possibleValuesListed(sdk.AsStringList(sdk.AsStringList(sdk.AllExternalOauthSecurityIntegrationAnyRoleModes)))), + ValidateDiagFunc: sdkValidation(sdk.ToExternalOauthSecurityIntegrationAnyRoleModeOption), + DiffSuppressFunc: NormalizeAndCompare(sdk.ToExternalOauthSecurityIntegrationAnyRoleModeOption), + }, + "external_oauth_scope_delimiter": { Type: schema.TypeString, Optional: true, - Default: string(snowflake.Disable), - Description: "Specifies whether the OAuth client or user can use a role that is not defined in the OAuth access token.", - ValidateFunc: validation.StringInSlice([]string{ - string(snowflake.Disable), - string(snowflake.Enable), - string(snowflake.EnableForPrivilege), - }, true), - DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - normalize := func(s string) string { - return strings.ToUpper(strings.ReplaceAll(s, "-", "")) - } - return normalize(old) == normalize(new) - }, + Description: "Specifies the scope delimiter in the authorization token.", }, - "scope_delimiter": { + "external_oauth_scope_mapping_attribute": { Type: schema.TypeString, Optional: true, - Description: "Specifies the scope delimiter in the authorization token.", + Description: "Specifies the access token claim to map the access token to an account role. If removed from the config, the resource is recreated.", }, "comment": { Type: schema.TypeString, Optional: true, Description: "Specifies a comment for the OAuth integration.", }, - "created_on": { - Type: schema.TypeString, + ShowOutputAttributeName: { + Type: schema.TypeList, Computed: true, - Description: "Date and time when the External OAUTH integration was created.", + Description: "Outputs the result of `SHOW SECURITY INTEGRATIONS` for the given security integration.", + Elem: &schema.Resource{ + Schema: schemas.ShowSecurityIntegrationSchema, + }, + }, + DescribeOutputAttributeName: { + Type: schema.TypeList, + Computed: true, + Description: "Outputs the result of `DESCRIBE SECURITY INTEGRATIONS` for the given security integration.", + Elem: &schema.Resource{ + Schema: schemas.DescribeExternalOauthSecurityIntegrationSchema, + }, + }, + RelatedParametersAttributeName: { + Type: schema.TypeList, + Computed: true, + Description: "Paramteres related to this security integration.", + Elem: &schema.Resource{ + Schema: schemas.ShowExternalOauthParametersSchema, + }, }, } -// ExternalOauthIntegration returns a pointer to the resource representing a network policy. func ExternalOauthIntegration() *schema.Resource { return &schema.Resource{ - Description: "An External OAuth security integration allows a client to use a third-party authorization server to obtain the access tokens needed to interact with Snowflake.", - Create: CreateExternalOauthIntegration, - Read: ReadExternalOauthIntegration, - Update: UpdateExternalOauthIntegration, - Delete: DeleteExternalOauthIntegration, - - Schema: oauthExternalIntegrationSchema, + SchemaVersion: 1, + + CreateContext: CreateContextExternalOauthIntegration, + ReadContext: ReadContextExternalOauthIntegration(true), + UpdateContext: UpdateContextExternalOauthIntegration, + DeleteContext: DeleteContextExternalOauthIntegration, + Schema: oauthExternalIntegrationSchema, + CustomizeDiff: customdiff.All( + ForceNewIfChangeToEmptyString("external_oauth_rsa_public_key"), + ForceNewIfChangeToEmptyString("external_oauth_rsa_public_key_2"), + ForceNewIfChangeToEmptyString("external_oauth_scope_mapping_attribute"), + ForceNewIfChangeToEmptySet("external_oauth_jws_keys_url"), + ForceNewIfChangeToEmptySet("external_oauth_token_user_mapping_claim"), + ComputedIfAnyAttributeChanged(ShowOutputAttributeName, "enabled", "external_oauth_type", "comment"), + ComputedIfAnyAttributeChanged(DescribeOutputAttributeName, "enabled", "external_oauth_issuer", "external_oauth_jws_keys_url", "external_oauth_any_role_mode", + "external_oauth_rsa_public_key", "external_oauth_rsa_public_key_2", "external_oauth_blocked_roles_list", "external_oauth_allowed_roles_list", + "external_oauth_audience_list", "external_oauth_token_user_mapping_claim", "external_oauth_snowflake_user_mapping_attribute", "external_oauth_scope_delimiter", + "comment"), + ), Importer: &schema.ResourceImporter{ - StateContext: schema.ImportStatePassthroughContext, + StateContext: ImportExternalOauthIntegration, }, - } -} - -// CreateExternalOauthIntegration implements schema.CreateFunc. -func CreateExternalOauthIntegration(d *schema.ResourceData, meta interface{}) error { - manager, err := snowflake.NewExternalOauthIntegration3Manager() - if err != nil { - return fmt.Errorf("couldn't create external oauth integration manager: %w", err) - } - - input := &snowflake.ExternalOauthIntegration3CreateInput{ - ExternalOauthIntegration3: snowflake.ExternalOauthIntegration3{ - TopLevelIdentifier: snowflake.TopLevelIdentifier{ - Name: d.Get("name").(string), + Description: "Resource used to manage external oauth security integrations. For more information, check [documentation](https://docs.snowflake.com/en/sql-reference/sql/create-security-integration-oauth-external).", + + StateUpgraders: []schema.StateUpgrader{ + { + Version: 0, + // setting type to cty.EmptyObject is a bit hacky here but following https://developer.hashicorp.com/terraform/plugin/framework/migrating/resources/state-upgrade#sdkv2-1 would require lots of repetitive code; this should work with cty.EmptyObject + Type: cty.EmptyObject, + Upgrade: v092ExternalOauthIntegrationStateUpgrader, }, - - Type: "EXTERNAL_OAUTH", - TypeOk: true, - Enabled: d.Get("enabled").(bool), - EnabledOk: isOk(d.GetOk("enabled")), - ExternalOauthType: snowflake.ExternalOauthType(d.Get("type").(string)), - ExternalOauthTypeOk: isOk(d.GetOk("type")), - ExternalOauthIssuer: d.Get("issuer").(string), - ExternalOauthIssuerOk: isOk(d.GetOk("issuer")), - ExternalOauthTokenUserMappingClaim: expandStringList(d.Get("token_user_mapping_claims").(*schema.Set).List()), - ExternalOauthTokenUserMappingClaimOk: isOk(d.GetOk("token_user_mapping_claims")), - ExternalOauthSnowflakeUserMappingAttribute: snowflake.SFUserMappingAttribute(d.Get("snowflake_user_mapping_attribute").(string)), - ExternalOauthSnowflakeUserMappingAttributeOk: isOk(d.GetOk("snowflake_user_mapping_attribute")), - ExternalOauthJwsKeysURL: expandStringList(d.Get("jws_keys_urls").(*schema.Set).List()), - ExternalOauthJwsKeysURLOk: isOk(d.GetOk("jws_keys_urls")), - ExternalOauthBlockedRolesList: expandStringList(d.Get("blocked_roles").(*schema.Set).List()), - ExternalOauthBlockedRolesListOk: isOk(d.GetOk("blocked_roles")), - ExternalOauthAllowedRolesList: expandStringList(d.Get("allowed_roles").(*schema.Set).List()), - ExternalOauthAllowedRolesListOk: isOk(d.GetOk("allowed_roles")), - ExternalOauthRsaPublicKey: d.Get("rsa_public_key").(string), - ExternalOauthRsaPublicKeyOk: isOk(d.GetOk("rsa_public_key")), - ExternalOauthRsaPublicKey2: d.Get("rsa_public_key_2").(string), - ExternalOauthRsaPublicKey2Ok: isOk(d.GetOk("rsa_public_key_2")), - ExternalOauthAudienceList: expandStringList(d.Get("audience_urls").(*schema.Set).List()), - ExternalOauthAudienceListOk: isOk(d.GetOk("audience_urls")), - ExternalOauthAnyRoleMode: snowflake.AnyRoleMode(d.Get("any_role_mode").(string)), - ExternalOauthAnyRoleModeOk: isOk(d.GetOk("any_role_mode")), - ExternalOauthScopeDelimiter: d.Get("scope_delimiter").(string), - ExternalOauthScopeDelimiterOk: isOk(d.GetOk("scope_delimiter")), - ExternalOauthScopeMappingAttribute: d.Get("scope_mapping_attribute").(string), - ExternalOauthScopeMappingAttributeOk: isOk(d.GetOk("scope_mapping_attribute")), - - Comment: sql.NullString{String: d.Get("comment").(string)}, - CommentOk: isOk(d.GetOk("comment")), }, } - - stmt, err := manager.Create(input) - if err != nil { - return fmt.Errorf("couldn't generate create statement: %w", err) - } - - client := meta.(*provider.Context).Client - db := client.GetConn().DB - err = snowflake.Exec(db, stmt) - if err != nil { - return fmt.Errorf("error executing create statement: %w", err) - } - - d.SetId(ExternalOauthIntegrationID(&input.ExternalOauthIntegration3)) - - return ReadExternalOauthIntegration(d, meta) } -// ReadExternalOauthIntegration implements schema.ReadFunc. -func ReadExternalOauthIntegration(d *schema.ResourceData, meta interface{}) error { - manager, err := snowflake.NewExternalOauthIntegration3Manager() - if err != nil { - return fmt.Errorf("couldn't create external oauth integration builder: %w", err) - } - - input := ExternalOauthIntegrationIdentifier(d.Id()) - +func ImportExternalOauthIntegration(ctx context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) { + logging.DebugLogger.Printf("[DEBUG] Starting external oauth integration import") client := meta.(*provider.Context).Client - db := client.GetConn().DB - - // This resource needs a SHOW and a DESCRIBE + id := helpers.DecodeSnowflakeID(d.Id()).(sdk.AccountObjectIdentifier) - // SHOW - stmt, err := manager.ReadShow(input) + integration, err := client.SecurityIntegrations.ShowByID(ctx, id) if err != nil { - return fmt.Errorf("couldn't generate show statement: %w", err) + return nil, err } - row := snowflake.QueryRow(db, stmt) + integrationProperties, err := client.SecurityIntegrations.Describe(ctx, id) if err != nil { - return fmt.Errorf("error querying external oauth integration: %w", err) + return nil, err } - showOutput, err := manager.ParseShow(row) - if err != nil { - return fmt.Errorf("error parsing show result: %w", err) + if err = d.Set("name", integration.Name); err != nil { + return nil, err } - - if err := d.Set("type", strings.TrimPrefix(showOutput.Type, "EXTERNAL_OAUTH - ")); err != nil { - return fmt.Errorf("error setting type: %w", err) + if err = d.Set("enabled", integration.Enabled); err != nil { + return nil, err } - if err := d.Set("name", showOutput.Name); err != nil { - return fmt.Errorf("error setting name: %w", err) + if oauthType, err := integration.SubType(); err == nil { + if err = d.Set("external_oauth_type", oauthType); err != nil { + return nil, err + } } - if err := d.Set("enabled", showOutput.Enabled); err != nil { - return fmt.Errorf("error setting enabled: %w", err) + if prop, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { return property.Name == "EXTERNAL_OAUTH_ISSUER" }); err == nil { + if err = d.Set("external_oauth_issuer", prop.Value); err != nil { + return nil, err + } } - if err := d.Set("comment", showOutput.Comment.String); err != nil { - return fmt.Errorf("error setting comment: %w", err) + if prop, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "EXTERNAL_OAUTH_JWS_KEYS_URL" + }); err == nil { + if err = d.Set("external_oauth_jws_keys_url", sdk.ParseCommaSeparatedStringArray(prop.Value, false)); err != nil { + return nil, err + } + } + if prop, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "EXTERNAL_OAUTH_ANY_ROLE_MODE" + }); err == nil { + if err = d.Set("external_oauth_any_role_mode", prop.Value); err != nil { + return nil, err + } + } + if prop, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "EXTERNAL_OAUTH_RSA_PUBLIC_KEY" + }); err == nil { + if err = d.Set("external_oauth_rsa_public_key", prop.Value); err != nil { + return nil, err + } + } + if prop, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "EXTERNAL_OAUTH_RSA_PUBLIC_KEY_2" + }); err == nil { + if err = d.Set("external_oauth_rsa_public_key_2", prop.Value); err != nil { + return nil, err + } } - // if err := d.Set("created_on", showOutput.CreatedOn.String); err != nil { - // return fmt.Errorf("error setting created_on: %w", err) - // } - // DESCRIBE - stmt, err = manager.ReadDescribe(input) - if err != nil { - return fmt.Errorf("couldn't generate describe statement: %w", err) + if prop, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "EXTERNAL_OAUTH_BLOCKED_ROLES_LIST" + }); err == nil { + roles := sdk.ParseCommaSeparatedStringArray(prop.Value, false) + if err = d.Set("external_oauth_blocked_roles_list", roles); err != nil { + return nil, err + } } + if prop, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "EXTERNAL_OAUTH_ALLOWED_ROLES_LIST" + }); err == nil { + if err = d.Set("external_oauth_allowed_roles_list", sdk.ParseCommaSeparatedStringArray(prop.Value, false)); err != nil { + return nil, err + } + } + if prop, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "EXTERNAL_OAUTH_AUDIENCE_LIST" + }); err == nil { + if err = d.Set("external_oauth_audience_list", sdk.ParseCommaSeparatedStringArray(prop.Value, false)); err != nil { + return nil, err + } + } + if prop, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "EXTERNAL_OAUTH_TOKEN_USER_MAPPING_CLAIM" + }); err == nil { + if err = d.Set("external_oauth_token_user_mapping_claim", sdk.ParseCommaSeparatedStringArray(prop.Value, true)); err != nil { + return nil, err + } + } + if prop, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "EXTERNAL_OAUTH_SNOWFLAKE_USER_MAPPING_ATTRIBUTE" + }); err == nil { + if err = d.Set("external_oauth_snowflake_user_mapping_attribute", prop.Value); err != nil { + return nil, err + } + } + if prop, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "EXTERNAL_OAUTH_SCOPE_DELIMITER" + }); err == nil { + if err = d.Set("external_oauth_scope_delimiter", prop.Value); err != nil { + return nil, err + } + } + if prop, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { return property.Name == "COMMENT" }); err == nil { + if err = d.Set("comment", prop.Value); err != nil { + return nil, err + } + } + return []*schema.ResourceData{d}, nil +} - rows, err := snowflake.Query(db, stmt) +func CreateContextExternalOauthIntegration(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + enabled := d.Get("enabled").(bool) + externalOauthIssuer := d.Get("external_oauth_issuer").(string) + externalOauthSnowflakeUserMappingAttributeRaw := d.Get("external_oauth_snowflake_user_mapping_attribute").(string) + externalOauthTokenUserMappingClaimRaw := expandStringList(d.Get("external_oauth_token_user_mapping_claim").(*schema.Set).List()) + name := d.Get("name").(string) + integrationTypeRaw := d.Get("external_oauth_type").(string) + integrationType, err := sdk.ToExternalOauthSecurityIntegrationTypeOption(integrationTypeRaw) if err != nil { - return fmt.Errorf("error querying external oauth integration: %w", err) + return diag.FromErr(err) } - - defer rows.Close() - describeOutput, err := manager.ParseDescribe(rows.Rows) + externalOauthSnowflakeUserMappingAttribute, err := sdk.ToExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeOption(externalOauthSnowflakeUserMappingAttributeRaw) if err != nil { - return fmt.Errorf("failed to parse result of describe: %w", err) + return diag.FromErr(err) } - - if err := d.Set("issuer", describeOutput.ExternalOauthIssuer); err != nil { - return fmt.Errorf("error setting issuer: %w", err) + externalOauthTokenUserMappingClaim := make([]sdk.TokenUserMappingClaim, 0, len(externalOauthTokenUserMappingClaimRaw)) + for _, v := range externalOauthTokenUserMappingClaimRaw { + externalOauthTokenUserMappingClaim = append(externalOauthTokenUserMappingClaim, sdk.TokenUserMappingClaim{Claim: v}) } - if err := d.Set("jws_keys_urls", describeOutput.ExternalOauthJwsKeysURL); err != nil { - return fmt.Errorf("error setting jws_keys_urls: %w", err) + id := sdk.NewAccountObjectIdentifier(name) + req := sdk.NewCreateExternalOauthSecurityIntegrationRequest(id, enabled, integrationType, externalOauthIssuer, externalOauthTokenUserMappingClaim, externalOauthSnowflakeUserMappingAttribute) + + if v, ok := d.GetOk("comment"); ok { + req.WithComment(v.(string)) } - if err := d.Set("any_role_mode", describeOutput.ExternalOauthAnyRoleMode); err != nil { - return fmt.Errorf("error setting any_role_mode: %w", err) + + if v, ok := d.GetOk("external_oauth_allowed_roles_list"); ok { + vList := expandStringList(v.(*schema.Set).List()) + allowedRoles := make([]sdk.AccountObjectIdentifier, len(vList)) + for i := range vList { + allowedRoles[i] = sdk.NewAccountObjectIdentifier(vList[i]) + } + req.WithExternalOauthAllowedRolesList(sdk.AllowedRolesListRequest{AllowedRolesList: allowedRoles}) } - if err := d.Set("rsa_public_key", describeOutput.ExternalOauthRsaPublicKey); err != nil { - return fmt.Errorf("error setting rsa_public_key: %w", err) + + if v, ok := d.GetOk("external_oauth_any_role_mode"); ok { + valueRaw := v.(string) + value, err := sdk.ToExternalOauthSecurityIntegrationAnyRoleModeOption(valueRaw) + if err != nil { + return diag.FromErr(err) + } + req.WithExternalOauthAnyRoleMode(value) } - if err := d.Set("rsa_public_key_2", describeOutput.ExternalOauthRsaPublicKey2); err != nil { - return fmt.Errorf("error setting rsa_public_key_2: %w", err) + + if v, ok := d.GetOk("external_oauth_audience_list"); ok { + elems := expandStringList(v.(*schema.Set).List()) + audienceUrls := make([]sdk.AudienceListItem, len(elems)) + for i := range elems { + audienceUrls[i] = sdk.AudienceListItem{Item: elems[i]} + } + req.WithExternalOauthAudienceList(sdk.AudienceListRequest{AudienceList: audienceUrls}) } - // Filter out default roles - blockedRoles := []string{} - for i := range describeOutput.ExternalOauthBlockedRolesList { - role := describeOutput.ExternalOauthBlockedRolesList[i] - if role != "ACCOUNTADMIN" && role != "SECURITYADMIN" { - blockedRoles = append(blockedRoles, role) + + if v, ok := d.GetOk("external_oauth_blocked_roles_list"); ok { + vList := expandStringList(v.(*schema.Set).List()) + blockedRoles := make([]sdk.AccountObjectIdentifier, len(vList)) + for i := range vList { + blockedRoles[i] = sdk.NewAccountObjectIdentifier(vList[i]) } + req.WithExternalOauthBlockedRolesList(sdk.BlockedRolesListRequest{BlockedRolesList: blockedRoles}) } - if err := d.Set("blocked_roles", blockedRoles); err != nil { - return fmt.Errorf("error setting blocked_roles: %w", err) + + if v, ok := d.GetOk("external_oauth_jws_keys_url"); ok { + elems := expandStringList(v.(*schema.Set).List()) + urls := make([]sdk.JwsKeysUrl, len(elems)) + for i := range elems { + urls[i] = sdk.JwsKeysUrl{JwsKeyUrl: elems[i]} + } + req.WithExternalOauthJwsKeysUrl(urls) } - if err := d.Set("allowed_roles", describeOutput.ExternalOauthAllowedRolesList); err != nil { - return fmt.Errorf("error setting allowed_roles: %w", err) + + if v, ok := d.GetOk("external_oauth_rsa_public_key"); ok { + req.WithExternalOauthRsaPublicKey(v.(string)) } - if err := d.Set("audience_urls", describeOutput.ExternalOauthAudienceList); err != nil { - return fmt.Errorf("error setting audience_urls: %w", err) + + if v, ok := d.GetOk("external_oauth_rsa_public_key_2"); ok { + req.WithExternalOauthRsaPublicKey2(v.(string)) } - if err := d.Set("token_user_mapping_claims", describeOutput.ExternalOauthTokenUserMappingClaim); err != nil { - return fmt.Errorf("error setting token_user_mapping_claims: %w", err) + + if v, ok := d.GetOk("external_oauth_scope_delimiter"); ok { + req.WithExternalOauthScopeDelimiter(v.(string)) } - if err := d.Set("snowflake_user_mapping_attribute", describeOutput.ExternalOauthSnowflakeUserMappingAttribute); err != nil { - return fmt.Errorf("error setting snowflake_user_mapping_attribute: %w", err) + + if v, ok := d.GetOk("external_oauth_scope_mapping_attribute"); ok { + req.WithExternalOauthScopeMappingAttribute(v.(string)) } - if err := d.Set("scope_mapping_attribute", describeOutput.ExternalOauthScopeMappingAttribute); err != nil { - return fmt.Errorf("error setting scope_mapping_attribute: %w", err) + + if err := client.SecurityIntegrations.CreateExternalOauth(ctx, req); err != nil { + return diag.FromErr(err) } - return err + d.SetId(helpers.EncodeSnowflakeID(id)) + + return ReadContextExternalOauthIntegration(false)(ctx, d, meta) } -// UpdateExternalOauthIntegration implements schema.UpdateFunc. -func UpdateExternalOauthIntegration(d *schema.ResourceData, meta interface{}) error { - manager, err := snowflake.NewExternalOauthIntegration3Manager() - if err != nil { - return fmt.Errorf("couldn't create external oauth integration builder: %w", err) - } +func ReadContextExternalOauthIntegration(withExternalChangesMarking bool) schema.ReadContextFunc { + return func(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + id := helpers.DecodeSnowflakeID(d.Id()).(sdk.AccountObjectIdentifier) - runAlter := false - alterInput := &snowflake.ExternalOauthIntegration3UpdateInput{ - ExternalOauthIntegration3: snowflake.ExternalOauthIntegration3{ - TopLevelIdentifier: snowflake.TopLevelIdentifier{ - Name: d.Get("name").(string), - }, - }, - } - runUnset := false - unsetInput := &snowflake.ExternalOauthIntegration3UpdateInput{ - ExternalOauthIntegration3: snowflake.ExternalOauthIntegration3{ - TopLevelIdentifier: snowflake.TopLevelIdentifier{ - Name: d.Get("name").(string), - }, - }, - } + integration, err := client.SecurityIntegrations.ShowByID(ctx, id) + if err != nil { + if errors.Is(err, sdk.ErrObjectNotFound) { + d.SetId("") + return diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Warning, + Summary: "Failed to query security integration. Marking the resource as removed.", + Detail: fmt.Sprintf("Security integration name: %s, Err: %s", id.FullyQualifiedName(), err), + }, + } + } + return diag.FromErr(err) + } + integrationProperties, err := client.SecurityIntegrations.Describe(ctx, id) + if err != nil { + return diag.FromErr(err) + } - if d.HasChange("enabled") { - val, ok := d.GetOk("enabled") - if ok { - alterInput.Enabled = val.(bool) - alterInput.EnabledOk = true - runAlter = true - } else { - unsetInput.EnabledOk = true - runUnset = true + if c := integration.Category; c != sdk.SecurityIntegrationCategory { + return diag.FromErr(fmt.Errorf("expected %v to be a %s integration, got %v", id, sdk.SecurityIntegrationCategory, c)) } - } - if d.HasChange("type") { - val, ok := d.GetOk("type") - if ok { - alterInput.Type = val.(string) - alterInput.TypeOk = true - runAlter = true - } else { - unsetInput.TypeOk = true - runUnset = true + if err := d.Set("name", integration.Name); err != nil { + return diag.FromErr(err) } - } - if d.HasChange("issuer") { - val, ok := d.GetOk("issuer") - if ok { - alterInput.ExternalOauthIssuer = val.(string) - alterInput.ExternalOauthIssuerOk = true - runAlter = true - } else { - unsetInput.ExternalOauthIssuerOk = true - runUnset = true + if err := d.Set("comment", integration.Comment); err != nil { + return diag.FromErr(err) } - } - if d.HasChange("token_user_mapping_claims") { - val, ok := d.GetOk("token_user_mapping_claims") - if ok { - alterInput.ExternalOauthTokenUserMappingClaim = expandStringList(val.(*schema.Set).List()) - alterInput.ExternalOauthTokenUserMappingClaimOk = true - runAlter = true - } else { - unsetInput.ExternalOauthTokenUserMappingClaimOk = true - runUnset = true + + if err := d.Set("enabled", integration.Enabled); err != nil { + return diag.FromErr(err) } - } - if d.HasChange("snowflake_user_mapping_attribute") { - val, ok := d.GetOk("snowflake_user_mapping_attribute") - if ok { - alterInput.ExternalOauthSnowflakeUserMappingAttribute = snowflake.SFUserMappingAttribute(val.(string)) - alterInput.ExternalOauthSnowflakeUserMappingAttributeOk = true - runAlter = true - } else { - unsetInput.ExternalOauthSnowflakeUserMappingAttributeOk = true - runUnset = true + subType, err := integration.SubType() + if err != nil { + return diag.FromErr(err) } - } - if d.HasChange("jws_keys_urls") { - val, ok := d.GetOk("jws_keys_urls") - if ok { - alterInput.ExternalOauthJwsKeysURL = expandStringList(val.(*schema.Set).List()) - alterInput.ExternalOauthJwsKeysURLOk = true - runAlter = true - } else { - unsetInput.ExternalOauthJwsKeysURLOk = true - runUnset = true + if err := d.Set("external_oauth_type", subType); err != nil { + return diag.FromErr(err) } - } - if d.HasChange("rsa_public_key") { - val, ok := d.GetOk("rsa_public_key") - if ok { - alterInput.ExternalOauthRsaPublicKey = val.(string) - alterInput.ExternalOauthRsaPublicKeyOk = true - runAlter = true - } else { - unsetInput.ExternalOauthRsaPublicKeyOk = true - runUnset = true + if withExternalChangesMarking { + externalOauthIssuer, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { return property.Name == "EXTERNAL_OAUTH_ISSUER" }) + if err != nil { + return diag.FromErr(err) + } + externalOauthJwsKeysUrl, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "EXTERNAL_OAUTH_JWS_KEYS_URL" + }) + if err != nil { + return diag.FromErr(err) + } + externalOauthAnyRoleMode, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "EXTERNAL_OAUTH_ANY_ROLE_MODE" + }) + if err != nil { + return diag.FromErr(err) + } + externalOauthRsaPublicKey, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "EXTERNAL_OAUTH_RSA_PUBLIC_KEY" + }) + if err != nil { + return diag.FromErr(err) + } + externalOauthRsaPublicKey2, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "EXTERNAL_OAUTH_RSA_PUBLIC_KEY_2" + }) + if err != nil { + return diag.FromErr(err) + } + externalOauthBlockedRolesList, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "EXTERNAL_OAUTH_BLOCKED_ROLES_LIST" + }) + if err != nil { + return diag.FromErr(err) + } + externalOauthAllowedRolesList, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "EXTERNAL_OAUTH_ALLOWED_ROLES_LIST" + }) + if err != nil { + return diag.FromErr(err) + } + externalOauthAudienceList, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "EXTERNAL_OAUTH_AUDIENCE_LIST" + }) + if err != nil { + return diag.FromErr(err) + } + externalOauthTokenUserMappingClaim, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "EXTERNAL_OAUTH_TOKEN_USER_MAPPING_CLAIM" + }) + if err != nil { + return diag.FromErr(err) + } + externalOauthSnowflakeUserMappingAttribute, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "EXTERNAL_OAUTH_SNOWFLAKE_USER_MAPPING_ATTRIBUTE" + }) + if err != nil { + return diag.FromErr(err) + } + externalOauthScopeDelimiter, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "EXTERNAL_OAUTH_SCOPE_DELIMITER" + }) + if err != nil { + return diag.FromErr(err) + } + if err = handleExternalChangesToObjectInDescribe(d, + describeMapping{"external_oauth_issuer", "external_oauth_issuer", externalOauthIssuer.Value, externalOauthIssuer.Value, nil}, + describeMapping{"external_oauth_jws_keys_url", "external_oauth_jws_keys_url", externalOauthJwsKeysUrl.Value, sdk.ParseCommaSeparatedStringArray(externalOauthJwsKeysUrl.Value, false), nil}, + describeMapping{"external_oauth_any_role_mode", "external_oauth_any_role_mode", externalOauthAnyRoleMode.Value, externalOauthAnyRoleMode.Value, nil}, + describeMapping{"external_oauth_rsa_public_key", "external_oauth_rsa_public_key", externalOauthRsaPublicKey.Value, externalOauthRsaPublicKey.Value, nil}, + describeMapping{"external_oauth_rsa_public_key_2", "external_oauth_rsa_public_key_2", externalOauthRsaPublicKey2.Value, externalOauthRsaPublicKey2.Value, nil}, + describeMapping{"external_oauth_blocked_roles_list", "external_oauth_blocked_roles_list", externalOauthBlockedRolesList.Value, sdk.ParseCommaSeparatedStringArray(externalOauthBlockedRolesList.Value, false), nil}, + describeMapping{"external_oauth_allowed_roles_list", "external_oauth_allowed_roles_list", externalOauthAllowedRolesList.Value, sdk.ParseCommaSeparatedStringArray(externalOauthAllowedRolesList.Value, false), nil}, + describeMapping{"external_oauth_audience_list", "external_oauth_audience_list", externalOauthAudienceList.Value, sdk.ParseCommaSeparatedStringArray(externalOauthAudienceList.Value, false), nil}, + describeMapping{"external_oauth_token_user_mapping_claim", "external_oauth_token_user_mapping_claim", externalOauthTokenUserMappingClaim.Value, sdk.ParseCommaSeparatedStringArray(externalOauthTokenUserMappingClaim.Value, true), nil}, + describeMapping{"external_oauth_snowflake_user_mapping_attribute", "external_oauth_snowflake_user_mapping_attribute", externalOauthSnowflakeUserMappingAttribute.Value, externalOauthSnowflakeUserMappingAttribute.Value, nil}, + describeMapping{"external_oauth_scope_delimiter", "external_oauth_scope_delimiter", externalOauthScopeDelimiter.Value, externalOauthScopeDelimiter.Value, nil}, + ); err != nil { + return diag.FromErr(err) + } } - } - if d.HasChange("rsa_public_key_2") { - val, ok := d.GetOk("rsa_public_key_2") - if ok { - alterInput.ExternalOauthRsaPublicKey2 = val.(string) - alterInput.ExternalOauthRsaPublicKey2Ok = true - runAlter = true - } else { - unsetInput.ExternalOauthRsaPublicKey2Ok = true - runUnset = true + + if err = setStateToValuesFromConfig(d, warehouseSchema, []string{ + "external_oauth_jws_keys_url", + "external_oauth_rsa_public_key", + "external_oauth_rsa_public_key_2", + "external_oauth_blocked_roles_list", + "external_oauth_allowed_roles_list", + "external_oauth_audience_list", + "external_oauth_any_role_mode", + "external_oauth_scope_delimiter", + "external_oauth_scope_mapping_attribute", + "comment", + }); err != nil { + return diag.FromErr(err) } - } - if d.HasChange("blocked_roles") { - val, ok := d.GetOk("blocked_roles") - if ok { - alterInput.ExternalOauthBlockedRolesList = expandStringList(val.(*schema.Set).List()) - alterInput.ExternalOauthBlockedRolesListOk = true - runAlter = true - } else { - unsetInput.ExternalOauthBlockedRolesListOk = true - runUnset = true + if err = d.Set(ShowOutputAttributeName, []map[string]any{schemas.SecurityIntegrationToSchema(integration)}); err != nil { + return diag.FromErr(err) } - } - if d.HasChange("allowed_roles") { - val, ok := d.GetOk("allowed_roles") - if ok { - alterInput.ExternalOauthAllowedRolesList = expandStringList(val.(*schema.Set).List()) - alterInput.ExternalOauthAllowedRolesListOk = true - runAlter = true - } else { - unsetInput.ExternalOauthAllowedRolesListOk = true - runUnset = true + + if err = d.Set(DescribeOutputAttributeName, []map[string]any{schemas.ExternalOauthSecurityIntegrationPropertiesToSchema(integrationProperties)}); err != nil { + return diag.FromErr(err) } - } - if d.HasChange("audience_urls") { - val, ok := d.GetOk("audience_urls") - if ok { - alterInput.ExternalOauthAudienceList = expandStringList(val.(*schema.Set).List()) - alterInput.ExternalOauthAudienceListOk = true - runAlter = true - } else { - unsetInput.ExternalOauthAudienceListOk = true - runUnset = true + + param, err := client.Parameters.ShowAccountParameter(ctx, sdk.AccountParameterExternalOAuthAddPrivilegedRolesToBlockedList) + if err != nil { + return diag.FromErr(err) } + if err = d.Set(RelatedParametersAttributeName, []map[string]any{schemas.ExternalOauthParametersToSchema([]*sdk.Parameter{param})}); err != nil { + return diag.FromErr(err) + } + return nil } - if d.HasChange("any_role_mode") { - val, ok := d.GetOk("any_role_mode") - if ok { - alterInput.ExternalOauthAnyRoleMode = snowflake.AnyRoleMode(val.(string)) - alterInput.ExternalOauthAnyRoleModeOk = true - runAlter = true - } else { - unsetInput.ExternalOauthAnyRoleModeOk = true - runUnset = true +} + +func UpdateContextExternalOauthIntegration(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*provider.Context).Client + id := helpers.DecodeSnowflakeID(d.Id()).(sdk.AccountObjectIdentifier) + set, unset := sdk.NewExternalOauthIntegrationSetRequest(), sdk.NewExternalOauthIntegrationUnsetRequest() + + if d.HasChange("comment") { + set.WithComment(sdk.StringAllowEmpty{Value: d.Get("comment").(string)}) + } + + if d.HasChange("enabled") { + // this field is required + set.WithEnabled(d.Get("enabled").(bool)) + } + + if d.HasChange("external_oauth_allowed_roles_list") { + v := expandStringList(d.Get("external_oauth_allowed_roles_list").(*schema.Set).List()) + allowedRoles := make([]sdk.AccountObjectIdentifier, len(v)) + for i := range v { + allowedRoles[i] = sdk.NewAccountObjectIdentifier(v[i]) } + set.WithExternalOauthAllowedRolesList(sdk.AllowedRolesListRequest{AllowedRolesList: allowedRoles}) } - if d.HasChange("scope_delimiter") { - val, ok := d.GetOk("scope_delimiter") - if ok { - alterInput.ExternalOauthScopeDelimiter = val.(string) - alterInput.ExternalOauthScopeDelimiterOk = true - runAlter = true + + if d.HasChange("external_oauth_any_role_mode") { + v := d.Get("external_oauth_any_role_mode").(string) + if len(v) > 0 { + value, err := sdk.ToExternalOauthSecurityIntegrationAnyRoleModeOption(v) + if err != nil { + return diag.FromErr(err) + } + set.WithExternalOauthAnyRoleMode(value) } else { - unsetInput.ExternalOauthScopeDelimiterOk = true - runUnset = true + // TODO(SNOW-1515781): use UNSET + set.WithExternalOauthAnyRoleMode(sdk.ExternalOauthSecurityIntegrationAnyRoleModeDisable) } } - if d.HasChange("scope_mapping_attribute") { - val, ok := d.GetOk("scope_mapping_attribute") - if ok { - alterInput.ExternalOauthScopeMappingAttribute = val.(string) - alterInput.ExternalOauthScopeMappingAttributeOk = true - runAlter = true + + if d.HasChange("external_oauth_audience_list") { + v := expandStringList(d.Get("external_oauth_audience_list").(*schema.Set).List()) + if len(v) > 0 { + audienceList := make([]sdk.AudienceListItem, len(v)) + for i := range v { + audienceList[i] = sdk.AudienceListItem{Item: v[i]} + } + set.WithExternalOauthAudienceList(sdk.AudienceListRequest{AudienceList: audienceList}) } else { - unsetInput.ExternalOauthScopeMappingAttributeOk = true - runUnset = true + unset.WithExternalOauthAudienceList(true) } } - if d.HasChange("comment") { - val, ok := d.GetOk("comment") - if ok { - alterInput.Comment.String = val.(string) - alterInput.CommentOk = true - runAlter = true - } else { - unsetInput.CommentOk = true - runUnset = true + + if d.HasChange("external_oauth_blocked_roles_list") { + vRaw := d.Get("external_oauth_blocked_roles_list") + v := expandStringList(vRaw.(*schema.Set).List()) + blockedRoles := make([]sdk.AccountObjectIdentifier, len(v)) + for i := range v { + blockedRoles[i] = sdk.NewAccountObjectIdentifier(v[i]) } + set.WithExternalOauthBlockedRolesList(sdk.BlockedRolesListRequest{BlockedRolesList: blockedRoles}) } - client := meta.(*provider.Context).Client - db := client.GetConn().DB + if d.HasChange("external_oauth_issuer") { + // this field is required + set.WithExternalOauthIssuer(d.Get("external_oauth_issuer").(string)) + } - if runAlter { - stmt, err := manager.Update(alterInput) - if err != nil { - return fmt.Errorf("couldn't generate alter statement for external oauth integration: %w", err) + if d.HasChange("external_oauth_jws_keys_url") { + v := expandStringList(d.Get("external_oauth_jws_keys_url").(*schema.Set).List()) + if len(v) > 0 { + urls := make([]sdk.JwsKeysUrl, len(v)) + for i := range v { + urls[i] = sdk.JwsKeysUrl{JwsKeyUrl: v[i]} + } + set.WithExternalOauthJwsKeysUrl(urls) } + // else: force new + } - err = snowflake.Exec(db, stmt) - if err != nil { - return fmt.Errorf("error executing alter statement: %w", err) + if d.HasChange("external_oauth_rsa_public_key") { + set.WithExternalOauthRsaPublicKey(d.Get("external_oauth_rsa_public_key").(string)) + if v, ok := d.GetOk("external_oauth_rsa_public_key"); ok { + set.WithExternalOauthRsaPublicKey2(v.(string)) } + // else: force new } - if runUnset { - stmt, err := manager.Unset(unsetInput) - if err != nil { - return fmt.Errorf("couldn't generate unset statement for external oauth integration: %w", err) + if d.HasChange("external_oauth_rsa_public_key_2") { + if v, ok := d.GetOk("external_oauth_rsa_public_key_2"); ok { + set.WithExternalOauthRsaPublicKey2(v.(string)) } + // else: force new + } - err = snowflake.Exec(db, stmt) - if err != nil { - return fmt.Errorf("error executing unset statement: %w", err) + if d.HasChange("external_oauth_scope_delimiter") { + if v, ok := d.GetOk("external_oauth_scope_delimiter"); ok { + set.WithExternalOauthScopeDelimiter(v.(string)) + } else { + // TODO(SNOW-1515781): use UNSET + set.WithExternalOauthScopeDelimiter(",") } } - return ReadExternalOauthIntegration(d, meta) -} + if d.HasChange("external_oauth_scope_mapping_attribute") { + // this field is required + set.WithExternalOauthScopeMappingAttribute(d.Get("external_oauth_scope_mapping_attribute").(string)) + } -// DeleteExternalOauthIntegration implements schema.DeleteFunc. -func DeleteExternalOauthIntegration(d *schema.ResourceData, meta interface{}) error { - manager, err := snowflake.NewExternalOauthIntegration3Manager() - if err != nil { - return fmt.Errorf("couldn't create external oauth integration builder: %w", err) + if d.HasChange("external_oauth_snowflake_user_mapping_attribute") { + // this field is required + if v, ok := d.GetOk("external_oauth_snowflake_user_mapping_attribute"); ok { + value, err := sdk.ToExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeOption(v.(string)) + if err != nil { + return diag.FromErr(err) + } + set.WithExternalOauthSnowflakeUserMappingAttribute(value) + } } - input := &snowflake.ExternalOauthIntegration3DeleteInput{ - TopLevelIdentifier: snowflake.TopLevelIdentifier{ - Name: d.Get("name").(string), - }, + if d.HasChange("external_oauth_token_user_mapping_claim") { + v := expandStringList(d.Get("external_oauth_token_user_mapping_claim").(*schema.Set).List()) + claims := make([]sdk.TokenUserMappingClaim, len(v)) + for i := range v { + claims[i] = sdk.TokenUserMappingClaim{ + Claim: v[i], + } + } + set.WithExternalOauthTokenUserMappingClaim(claims) } - stmt, err := manager.Delete(input) - if err != nil { - return fmt.Errorf("couldn't generate drop statement: %w", err) + if d.HasChange("external_oauth_type") { + // this field is required + if v, ok := d.GetOk("external_oauth_type"); ok { + value, err := sdk.ToExternalOauthSecurityIntegrationTypeOption(v.(string)) + if err != nil { + return diag.FromErr(err) + } + set.WithExternalOauthType(value) + } } + if !reflect.DeepEqual(*set, sdk.ExternalOauthIntegrationSetRequest{}) { + if err := client.SecurityIntegrations.AlterExternalOauth(ctx, sdk.NewAlterExternalOauthSecurityIntegrationRequest(id).WithSet(*set)); err != nil { + return diag.FromErr(err) + } + } + if !reflect.DeepEqual(*unset, sdk.ExternalOauthIntegrationUnsetRequest{}) { + if err := client.SecurityIntegrations.AlterExternalOauth(ctx, sdk.NewAlterExternalOauthSecurityIntegrationRequest(id).WithUnset(*unset)); err != nil { + return diag.FromErr(err) + } + } + return ReadContextExternalOauthIntegration(false)(ctx, d, meta) +} + +func DeleteContextExternalOauthIntegration(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + id := helpers.DecodeSnowflakeID(d.Id()).(sdk.AccountObjectIdentifier) client := meta.(*provider.Context).Client - db := client.GetConn().DB - err = snowflake.Exec(db, stmt) + + err := client.SecurityIntegrations.Drop(ctx, sdk.NewDropSecurityIntegrationRequest(sdk.NewAccountObjectIdentifier(id.Name())).WithIfExists(true)) if err != nil { - return fmt.Errorf("error executing drop statement: %w", err) + return diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Error, + Summary: "Error deleting integration", + Detail: fmt.Sprintf("id %v err = %v", id.Name(), err), + }, + } } + d.SetId("") return nil } - -func ExternalOauthIntegrationID(eoi *snowflake.ExternalOauthIntegration3) string { - return eoi.QualifiedName() -} - -func ExternalOauthIntegrationIdentifier(id string) *snowflake.TopLevelIdentifier { - return snowflake.TopLevelIdentifierFromQualifiedName(id) -} diff --git a/pkg/resources/external_oauth_integration_acceptance_test.go b/pkg/resources/external_oauth_integration_acceptance_test.go index fc227bcab2..02b5f57b58 100644 --- a/pkg/resources/external_oauth_integration_acceptance_test.go +++ b/pkg/resources/external_oauth_integration_acceptance_test.go @@ -2,186 +2,974 @@ package resources_test import ( "fmt" + "regexp" + "sort" "strings" "testing" acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/importchecks" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/planchecks" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" - "github.com/google/uuid" + tfjson "github.com/hashicorp/terraform-json" + "github.com/hashicorp/terraform-plugin-testing/config" "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" "github.com/hashicorp/terraform-plugin-testing/tfversion" ) -func TestAcc_ExternalOauthIntegration(t *testing.T) { - oauthIntName := acc.TestClient().Ids.Alpha() - integrationType := "AZURE" - - issuer := fmt.Sprintf("https://sts.windows.net/%s", uuid.NewString()) - +func TestAcc_ExternalOauthIntegration_basic(t *testing.T) { + id := acc.TestClient().Ids.RandomAccountObjectIdentifier() + role, roleCleanup := acc.TestClient().Role.CreateRole(t) + issuer := random.String() + t.Cleanup(roleCleanup) + m := func(complete, unset bool) map[string]config.Variable { + c := map[string]config.Variable{ + "enabled": config.BoolVariable(true), + "name": config.StringVariable(id.Name()), + "external_oauth_type": config.StringVariable(string(sdk.ExternalOauthSecurityIntegrationTypeCustom)), + "external_oauth_snowflake_user_mapping_attribute": config.StringVariable(string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress)), + "external_oauth_token_user_mapping_claim": config.SetVariable(config.StringVariable("foo")), + "external_oauth_issuer": config.StringVariable(issuer), + "external_oauth_jws_keys_url": config.SetVariable(config.StringVariable("https://example.com")), + } + if complete { + c["external_oauth_allowed_roles_list"] = config.SetVariable(config.StringVariable(role.ID().Name())) + c["external_oauth_any_role_mode"] = config.StringVariable(string(sdk.ExternalOauthSecurityIntegrationAnyRoleModeDisable)) + c["external_oauth_audience_list"] = config.SetVariable(config.StringVariable("foo")) + c["external_oauth_scope_delimiter"] = config.StringVariable(".") + c["external_oauth_scope_mapping_attribute"] = config.StringVariable("foo") + c["comment"] = config.StringVariable("foo") + } + if unset { + c["external_oauth_scope_mapping_attribute"] = config.StringVariable("foo") + } + return c + } resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, PreCheck: func() { acc.TestAccPreCheck(t) }, TerraformVersionChecks: []tfversion.TerraformVersionCheck{ tfversion.RequireAbove(tfversion.Version1_5_0), }, - CheckDestroy: nil, + CheckDestroy: acc.CheckDestroy(t, resources.ExternalOauthSecurityIntegration), Steps: []resource.TestStep{ + // create with empty optionals { - Config: externalOauthIntegrationConfig(oauthIntName, integrationType, issuer, "test resource"), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "name", oauthIntName), - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "type", integrationType), + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalOauthIntegration/basic"), + ConfigVariables: m(false, false), + Check: resource.ComposeAggregateTestCheckFunc( resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "enabled", "true"), - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "issuer", issuer), - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "snowflake_user_mapping_attribute", "LOGIN_NAME"), - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "token_user_mapping_claims.#", "2"), - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "token_user_mapping_claims.0", "test"), - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "token_user_mapping_claims.1", "upn"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_issuer", issuer), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_jws_keys_url.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_jws_keys_url.0", "https://example.com"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_snowflake_user_mapping_attribute", string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress)), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_token_user_mapping_claim.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_token_user_mapping_claim.0", "foo"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "name", id.Name()), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_type", string(sdk.ExternalOauthSecurityIntegrationTypeCustom)), + + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.name", id.Name()), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.integration_type", "EXTERNAL_OAUTH - CUSTOM"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.category", "SECURITY"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.enabled", "true"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.comment", ""), + resource.TestCheckResourceAttrSet("snowflake_external_oauth_integration.test", "show_output.0.created_on"), + + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.enabled.0.value", "true"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_issuer.0.value", issuer), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_jws_keys_url.0.value", "https://example.com"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_any_role_mode.0.value", string(sdk.ExternalOauthSecurityIntegrationAnyRoleModeDisable)), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_allowed_roles_list.0.value", ""), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_audience_list.0.value", ""), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_token_user_mapping_claim.0.value", "['foo']"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_snowflake_user_mapping_attribute.0.value", string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress)), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_scope_delimiter.0.value", ","), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.comment.0.value", "")), + }, + // import - without optionals + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalOauthIntegration/basic"), + ConfigVariables: m(false, false), + ResourceName: "snowflake_external_oauth_integration.test", + ImportState: true, + ImportStateCheck: importchecks.ComposeImportStateCheck( + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "name", id.Name()), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "enabled", "true"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_issuer", issuer), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_type", string(sdk.ExternalOauthSecurityIntegrationTypeCustom)), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_snowflake_user_mapping_attribute", string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress)), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_token_user_mapping_claim.#", "1"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_token_user_mapping_claim.0", "foo"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_jws_keys_url.#", "1"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_jws_keys_url.0", "https://example.com"), ), }, + // set optionals + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalOauthIntegration/completeWithJwsKeysUrlAndAllowedRolesList"), + ConfigVariables: m(true, true), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "comment", "foo"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "enabled", "true"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_allowed_roles_list.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_allowed_roles_list.0", role.ID().Name()), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_any_role_mode", string(sdk.ExternalOauthSecurityIntegrationAnyRoleModeDisable)), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_audience_list.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_audience_list.0", "foo"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_issuer", issuer), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_jws_keys_url.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_jws_keys_url.0", "https://example.com"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_scope_delimiter", "."), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_scope_mapping_attribute", "foo"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_snowflake_user_mapping_attribute", string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress)), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_token_user_mapping_claim.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_token_user_mapping_claim.0", "foo"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "name", id.Name()), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_type", string(sdk.ExternalOauthSecurityIntegrationTypeCustom)), + + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.name", id.Name()), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.integration_type", "EXTERNAL_OAUTH - CUSTOM"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.category", "SECURITY"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.enabled", "true"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.comment", "foo"), + resource.TestCheckResourceAttrSet("snowflake_external_oauth_integration.test", "show_output.0.created_on"), + + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.enabled.0.value", "true"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_issuer.0.value", issuer), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_jws_keys_url.0.value", "https://example.com"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_any_role_mode.0.value", string(sdk.ExternalOauthSecurityIntegrationAnyRoleModeDisable)), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_allowed_roles_list.0.value", role.Name), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_audience_list.0.value", "foo"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_token_user_mapping_claim.0.value", "['foo']"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_snowflake_user_mapping_attribute.0.value", string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress)), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_scope_delimiter.0.value", "."), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.comment.0.value", "foo")), + }, + // import - complete + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalOauthIntegration/completeWithJwsKeysUrlAndAllowedRolesList"), + ConfigVariables: m(true, true), + ResourceName: "snowflake_external_oauth_integration.test", + ImportState: true, + ImportStateCheck: importchecks.ComposeImportStateCheck( + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "name", id.Name()), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "enabled", "true"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_issuer", issuer), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_type", string(sdk.ExternalOauthSecurityIntegrationTypeCustom)), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_snowflake_user_mapping_attribute", string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress)), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_token_user_mapping_claim.#", "1"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_token_user_mapping_claim.0", "foo"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_jws_keys_url.#", "1"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_jws_keys_url.0", "https://example.com"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_any_role_mode", string(sdk.ExternalOauthSecurityIntegrationAnyRoleModeDisable)), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_allowed_roles_list.#", "1"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_allowed_roles_list.0", role.Name), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_audience_list.#", "1"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_audience_list.0", "foo"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_scope_delimiter", "."), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "comment", "foo"), + ), + }, + // change values externally + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalOauthIntegration/completeWithJwsKeysUrlAndAllowedRolesList"), + ConfigVariables: m(true, true), + PreConfig: func() { + acc.TestClient().SecurityIntegration.UpdateExternalOauth(t, sdk.NewAlterExternalOauthSecurityIntegrationRequest(id). + WithSet(*sdk.NewExternalOauthIntegrationSetRequest(). + WithExternalOauthSnowflakeUserMappingAttribute(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeLoginName), + )) + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_external_oauth_integration.test", plancheck.ResourceActionUpdate), + planchecks.ExpectDrift("snowflake_external_oauth_integration.test", "external_oauth_snowflake_user_mapping_attribute", sdk.String(string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress)), sdk.String(string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeLoginName))), + planchecks.ExpectChange("snowflake_external_oauth_integration.test", "external_oauth_snowflake_user_mapping_attribute", tfjson.ActionUpdate, sdk.String(string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeLoginName)), sdk.String(string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress))), + }, + }, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "comment", "foo"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "enabled", "true"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_allowed_roles_list.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_allowed_roles_list.0", role.ID().Name()), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_any_role_mode", string(sdk.ExternalOauthSecurityIntegrationAnyRoleModeDisable)), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_audience_list.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_audience_list.0", "foo"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_issuer", issuer), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_jws_keys_url.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_jws_keys_url.0", "https://example.com"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_scope_delimiter", "."), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_scope_mapping_attribute", "foo"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_snowflake_user_mapping_attribute", string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress)), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_token_user_mapping_claim.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_token_user_mapping_claim.0", "foo"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "name", id.Name()), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_type", string(sdk.ExternalOauthSecurityIntegrationTypeCustom)), + + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.name", id.Name()), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.integration_type", "EXTERNAL_OAUTH - CUSTOM"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.category", "SECURITY"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.enabled", "true"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.comment", "foo"), + resource.TestCheckResourceAttrSet("snowflake_external_oauth_integration.test", "show_output.0.created_on"), + + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.enabled.0.value", "true"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_issuer.0.value", issuer), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_jws_keys_url.0.value", "https://example.com"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_any_role_mode.0.value", string(sdk.ExternalOauthSecurityIntegrationAnyRoleModeDisable)), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_allowed_roles_list.0.value", role.Name), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_audience_list.0.value", "foo"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_token_user_mapping_claim.0.value", "['foo']"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_snowflake_user_mapping_attribute.0.value", string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress)), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_scope_delimiter.0.value", "."), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.comment.0.value", "foo")), + }, + // unset without force new { - ResourceName: "snowflake_external_oauth_integration.test", - ImportState: true, - ImportStateVerify: true, + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalOauthIntegration/unset"), + ConfigVariables: m(false, true), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_external_oauth_integration.test", plancheck.ResourceActionUpdate), + }, + }, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "comment", ""), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "enabled", "true"), + resource.TestCheckNoResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_allowed_roles_list.#"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_any_role_mode", ""), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_issuer", issuer), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_jws_keys_url.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_jws_keys_url.0", "https://example.com"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_scope_delimiter", ""), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_scope_mapping_attribute", "foo"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_snowflake_user_mapping_attribute", string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress)), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_token_user_mapping_claim.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_token_user_mapping_claim.0", "foo"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "name", id.Name()), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_type", string(sdk.ExternalOauthSecurityIntegrationTypeCustom)), + + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.name", id.Name()), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.integration_type", "EXTERNAL_OAUTH - CUSTOM"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.category", "SECURITY"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.enabled", "true"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.comment", ""), + resource.TestCheckResourceAttrSet("snowflake_external_oauth_integration.test", "show_output.0.created_on"), + + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.enabled.0.value", "true"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_issuer.0.value", issuer), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_jws_keys_url.0.value", "https://example.com"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_any_role_mode.0.value", string(sdk.ExternalOauthSecurityIntegrationAnyRoleModeDisable)), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_allowed_roles_list.0.value", ""), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_audience_list.0.value", ""), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_token_user_mapping_claim.0.value", "['foo']"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_snowflake_user_mapping_attribute.0.value", string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress)), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_scope_delimiter.0.value", ","), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.comment.0.value", "")), + }, + // unset all + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalOauthIntegration/basic"), + ConfigVariables: m(false, true), + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction("snowflake_external_oauth_integration.test", plancheck.ResourceActionDestroyBeforeCreate), + }, + }, + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "comment", ""), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "enabled", "true"), + resource.TestCheckNoResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_allowed_roles_list.#"), + resource.TestCheckNoResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_any_role_mode"), + resource.TestCheckNoResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_audience_list.#"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_issuer", issuer), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_jws_keys_url.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_jws_keys_url.0", "https://example.com"), + resource.TestCheckNoResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_scope_delimiter"), + resource.TestCheckNoResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_scope_mapping_attribute"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_snowflake_user_mapping_attribute", string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress)), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_token_user_mapping_claim.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_token_user_mapping_claim.0", "foo"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "name", id.Name()), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_type", string(sdk.ExternalOauthSecurityIntegrationTypeCustom)), + + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.name", id.Name()), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.integration_type", "EXTERNAL_OAUTH - CUSTOM"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.category", "SECURITY"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.enabled", "true"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.comment", ""), + resource.TestCheckResourceAttrSet("snowflake_external_oauth_integration.test", "show_output.0.created_on"), + + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.enabled.0.value", "true"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_issuer.0.value", issuer), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_jws_keys_url.0.value", "https://example.com"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_any_role_mode.0.value", string(sdk.ExternalOauthSecurityIntegrationAnyRoleModeDisable)), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_allowed_roles_list.0.value", ""), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_audience_list.0.value", ""), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_token_user_mapping_claim.0.value", "['foo']"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_snowflake_user_mapping_attribute.0.value", string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress)), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_scope_delimiter.0.value", ","), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.comment.0.value", "")), }, }, }) } -func TestAcc_ExternalOauthIntegrationEmptyComment(t *testing.T) { - oauthIntName := strings.ToLower(acc.TestClient().Ids.Alpha()) - integrationType := "AZURE" - - issuer := fmt.Sprintf("https://sts.windows.net/%s", uuid.NewString()) +func TestAcc_ExternalOauthIntegration_completeWithJwsKeysUrlAndAllowedRolesList(t *testing.T) { + id := acc.TestClient().Ids.RandomAccountObjectIdentifier() + role, roleCleanup := acc.TestClient().Role.CreateRole(t) + issuer := random.String() + t.Cleanup(roleCleanup) + m := func() map[string]config.Variable { + return map[string]config.Variable{ + "comment": config.StringVariable("foo"), + "enabled": config.BoolVariable(true), + "external_oauth_allowed_roles_list": config.SetVariable(config.StringVariable(role.ID().Name())), + "external_oauth_any_role_mode": config.StringVariable(string(sdk.ExternalOauthSecurityIntegrationAnyRoleModeDisable)), + "external_oauth_audience_list": config.SetVariable(config.StringVariable("foo")), + "external_oauth_issuer": config.StringVariable(issuer), + "external_oauth_jws_keys_url": config.SetVariable(config.StringVariable("https://example.com")), + "external_oauth_scope_delimiter": config.StringVariable("."), + "external_oauth_scope_mapping_attribute": config.StringVariable("foo"), + "external_oauth_snowflake_user_mapping_attribute": config.StringVariable(string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress)), + "external_oauth_token_user_mapping_claim": config.SetVariable(config.StringVariable("foo")), + "name": config.StringVariable(id.Name()), + "external_oauth_type": config.StringVariable(string(sdk.ExternalOauthSecurityIntegrationTypeCustom)), + } + } resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, PreCheck: func() { acc.TestAccPreCheck(t) }, TerraformVersionChecks: []tfversion.TerraformVersionCheck{ tfversion.RequireAbove(tfversion.Version1_5_0), }, - CheckDestroy: nil, Steps: []resource.TestStep{ { - Config: externalOauthIntegrationConfig(oauthIntName, integrationType, issuer, ""), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "name", oauthIntName), - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "type", integrationType), + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalOauthIntegration/completeWithJwsKeysUrlAndAllowedRolesList"), + ConfigVariables: m(), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "comment", "foo"), resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "enabled", "true"), - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "issuer", issuer), - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "snowflake_user_mapping_attribute", "LOGIN_NAME"), - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "token_user_mapping_claims.#", "2"), - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "token_user_mapping_claims.0", "test"), - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "token_user_mapping_claims.1", "upn"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_allowed_roles_list.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_allowed_roles_list.0", role.ID().Name()), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_any_role_mode", string(sdk.ExternalOauthSecurityIntegrationAnyRoleModeDisable)), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_audience_list.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_audience_list.0", "foo"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_issuer", issuer), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_jws_keys_url.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_jws_keys_url.0", "https://example.com"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_scope_delimiter", "."), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_scope_mapping_attribute", "foo"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_snowflake_user_mapping_attribute", string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress)), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_token_user_mapping_claim.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_token_user_mapping_claim.0", "foo"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "name", id.Name()), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_type", string(sdk.ExternalOauthSecurityIntegrationTypeCustom)), + + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.name", id.Name()), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.integration_type", "EXTERNAL_OAUTH - CUSTOM"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.category", "SECURITY"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.enabled", "true"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.comment", "foo"), + resource.TestCheckResourceAttrSet("snowflake_external_oauth_integration.test", "show_output.0.created_on"), + + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.enabled.0.value", "true"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_issuer.0.value", issuer), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_jws_keys_url.0.value", "https://example.com"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_any_role_mode.0.value", string(sdk.ExternalOauthSecurityIntegrationAnyRoleModeDisable)), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_allowed_roles_list.0.value", role.Name), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_audience_list.0.value", "foo"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_token_user_mapping_claim.0.value", "['foo']"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_snowflake_user_mapping_attribute.0.value", string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress)), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_scope_delimiter.0.value", "."), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.comment.0.value", "foo"), ), }, { - ResourceName: "snowflake_external_oauth_integration.test", - ImportState: true, - ImportStateVerify: true, + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalOauthIntegration/completeWithJwsKeysUrlAndAllowedRolesList"), + ConfigVariables: m(), + ResourceName: "snowflake_external_oauth_integration.test", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"external_oauth_rsa_public_key", "external_oauth_rsa_public_key_2", "external_oauth_scope_mapping_attribute"}, }, }, }) } -func TestAcc_ExternalOauthIntegrationLowercaseName(t *testing.T) { - oauthIntName := strings.ToLower(acc.TestClient().Ids.Alpha()) - integrationType := "AZURE" +func TestAcc_ExternalOauthIntegration_completeWithRsaPublicKeysAndBlockedRolesList_paramSet(t *testing.T) { + id := acc.TestClient().Ids.RandomAccountObjectIdentifier() + role, roleCleanup := acc.TestClient().Role.CreateRole(t) + t.Cleanup(roleCleanup) + expectedRoles := []string{"ACCOUNTADMIN", "SECURITYADMIN", role.Name} + sort.Strings(expectedRoles) + issuer := random.String() + rsaKey, _ := random.GenerateRSAPublicKey(t) + paramCleanup := acc.TestClient().Parameter.UpdateAccountParameterTemporarily(t, sdk.AccountParameterExternalOAuthAddPrivilegedRolesToBlockedList, "true") + t.Cleanup(paramCleanup) + m := func() map[string]config.Variable { + return map[string]config.Variable{ + "comment": config.StringVariable("foo"), + "enabled": config.BoolVariable(true), + "external_oauth_blocked_roles_list": config.SetVariable(config.StringVariable(role.ID().Name())), + "external_oauth_any_role_mode": config.StringVariable(string(sdk.ExternalOauthSecurityIntegrationAnyRoleModeDisable)), + "external_oauth_audience_list": config.SetVariable(config.StringVariable("foo")), + "external_oauth_issuer": config.StringVariable(issuer), + "external_oauth_rsa_public_key": config.StringVariable(rsaKey), + "external_oauth_rsa_public_key_2": config.StringVariable(rsaKey), + "external_oauth_scope_delimiter": config.StringVariable("."), + "external_oauth_scope_mapping_attribute": config.StringVariable("foo"), + "external_oauth_snowflake_user_mapping_attribute": config.StringVariable(string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress)), + "external_oauth_token_user_mapping_claim": config.SetVariable(config.StringVariable("foo")), + "name": config.StringVariable(id.Name()), + "external_oauth_type": config.StringVariable(string(sdk.ExternalOauthSecurityIntegrationTypeCustom)), + } + } + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalOauthIntegration/completeWithRsaPublicKeysAndBlockedRolesList"), + ConfigVariables: m(), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "comment", "foo"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "enabled", "true"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_blocked_roles_list.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_blocked_roles_list.0", role.ID().Name()), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_any_role_mode", string(sdk.ExternalOauthSecurityIntegrationAnyRoleModeDisable)), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_audience_list.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_audience_list.0", "foo"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_issuer", issuer), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_rsa_public_key", rsaKey), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_scope_delimiter", "."), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_scope_mapping_attribute", "foo"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_snowflake_user_mapping_attribute", string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress)), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_token_user_mapping_claim.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_token_user_mapping_claim.0", "foo"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "name", id.Name()), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_type", string(sdk.ExternalOauthSecurityIntegrationTypeCustom)), + + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.name", id.Name()), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.integration_type", "EXTERNAL_OAUTH - CUSTOM"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.category", "SECURITY"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.enabled", "true"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.comment", "foo"), + resource.TestCheckResourceAttrSet("snowflake_external_oauth_integration.test", "show_output.0.created_on"), - issuer := fmt.Sprintf("https://sts.windows.net/%s", uuid.NewString()) + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.enabled.0.value", "true"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_issuer.0.value", issuer), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_jws_keys_url.0.value", ""), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_any_role_mode.0.value", string(sdk.ExternalOauthSecurityIntegrationAnyRoleModeDisable)), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_rsa_public_key.0.value", rsaKey), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_rsa_public_key_2.0.value", rsaKey), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_blocked_roles_list.0.value", strings.Join(expectedRoles, ",")), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_allowed_roles_list.0.value", ""), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_audience_list.0.value", "foo"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_token_user_mapping_claim.0.value", "['foo']"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_snowflake_user_mapping_attribute.0.value", string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress)), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_scope_delimiter.0.value", "."), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.comment.0.value", "foo"), + ), + }, + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalOauthIntegration/completeWithRsaPublicKeysAndBlockedRolesList"), + ConfigVariables: m(), + ResourceName: "snowflake_external_oauth_integration.test", + ImportState: true, + ImportStateCheck: importchecks.ComposeAggregateImportStateCheck( + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "comment", "foo"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "enabled", "true"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_blocked_roles_list.#", "3"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_blocked_roles_list.0", expectedRoles[0]), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_blocked_roles_list.1", expectedRoles[1]), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_blocked_roles_list.2", expectedRoles[2]), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_any_role_mode", string(sdk.ExternalOauthSecurityIntegrationAnyRoleModeDisable)), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_audience_list.#", "1"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_audience_list.0", "foo"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_issuer", issuer), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_rsa_public_key", rsaKey), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_scope_delimiter", "."), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_snowflake_user_mapping_attribute", string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress)), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_token_user_mapping_claim.#", "1"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_token_user_mapping_claim.0", "foo"), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "name", id.Name()), + importchecks.TestCheckResourceAttrInstanceState(id.Name(), "external_oauth_type", string(sdk.ExternalOauthSecurityIntegrationTypeCustom)), + ), + }, + }, + }) +} +func TestAcc_ExternalOauthIntegration_completeWithRsaPublicKeysAndBlockedRolesList_paramUnset(t *testing.T) { + id := acc.TestClient().Ids.RandomAccountObjectIdentifier() + role, roleCleanup := acc.TestClient().Role.CreateRole(t) + t.Cleanup(roleCleanup) + issuer := random.String() + rsaKey, _ := random.GenerateRSAPublicKey(t) + paramCleanup := acc.TestClient().Parameter.UpdateAccountParameterTemporarily(t, sdk.AccountParameterExternalOAuthAddPrivilegedRolesToBlockedList, "false") + t.Cleanup(paramCleanup) + m := func() map[string]config.Variable { + return map[string]config.Variable{ + "comment": config.StringVariable("foo"), + "enabled": config.BoolVariable(true), + "external_oauth_blocked_roles_list": config.SetVariable(config.StringVariable(role.ID().Name())), + "external_oauth_any_role_mode": config.StringVariable(string(sdk.ExternalOauthSecurityIntegrationAnyRoleModeDisable)), + "external_oauth_audience_list": config.SetVariable(config.StringVariable("foo")), + "external_oauth_issuer": config.StringVariable(issuer), + "external_oauth_rsa_public_key": config.StringVariable(rsaKey), + "external_oauth_rsa_public_key_2": config.StringVariable(rsaKey), + "external_oauth_scope_delimiter": config.StringVariable("."), + "external_oauth_scope_mapping_attribute": config.StringVariable("foo"), + "external_oauth_snowflake_user_mapping_attribute": config.StringVariable(string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress)), + "external_oauth_token_user_mapping_claim": config.SetVariable(config.StringVariable("foo")), + "name": config.StringVariable(id.Name()), + "external_oauth_type": config.StringVariable(string(sdk.ExternalOauthSecurityIntegrationTypeCustom)), + } + } resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, PreCheck: func() { acc.TestAccPreCheck(t) }, TerraformVersionChecks: []tfversion.TerraformVersionCheck{ tfversion.RequireAbove(tfversion.Version1_5_0), }, - CheckDestroy: nil, Steps: []resource.TestStep{ { - Config: externalOauthIntegrationConfig(oauthIntName, integrationType, issuer, "test resource"), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "name", oauthIntName), - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "type", integrationType), + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalOauthIntegration/completeWithRsaPublicKeysAndBlockedRolesList"), + ConfigVariables: m(), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "comment", "foo"), resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "enabled", "true"), - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "issuer", issuer), - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "snowflake_user_mapping_attribute", "LOGIN_NAME"), - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "token_user_mapping_claims.#", "2"), - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "token_user_mapping_claims.0", "test"), - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "token_user_mapping_claims.1", "upn"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_blocked_roles_list.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_blocked_roles_list.0", role.ID().Name()), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_any_role_mode", string(sdk.ExternalOauthSecurityIntegrationAnyRoleModeDisable)), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_audience_list.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_audience_list.0", "foo"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_issuer", issuer), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_rsa_public_key", rsaKey), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_scope_delimiter", "."), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_scope_mapping_attribute", "foo"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_snowflake_user_mapping_attribute", string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress)), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_token_user_mapping_claim.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_token_user_mapping_claim.0", "foo"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "name", id.Name()), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "external_oauth_type", string(sdk.ExternalOauthSecurityIntegrationTypeCustom)), + + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.name", id.Name()), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.integration_type", "EXTERNAL_OAUTH - CUSTOM"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.category", "SECURITY"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.enabled", "true"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "show_output.0.comment", "foo"), + resource.TestCheckResourceAttrSet("snowflake_external_oauth_integration.test", "show_output.0.created_on"), + + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.#", "1"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.enabled.0.value", "true"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_issuer.0.value", issuer), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_jws_keys_url.0.value", ""), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_any_role_mode.0.value", string(sdk.ExternalOauthSecurityIntegrationAnyRoleModeDisable)), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_rsa_public_key.0.value", rsaKey), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_rsa_public_key_2.0.value", rsaKey), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_blocked_roles_list.0.value", role.Name), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_allowed_roles_list.0.value", ""), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_audience_list.0.value", "foo"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_token_user_mapping_claim.0.value", "['foo']"), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_snowflake_user_mapping_attribute.0.value", string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress)), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.external_oauth_scope_delimiter.0.value", "."), + resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "describe_output.0.comment.0.value", "foo"), ), }, { - ResourceName: "snowflake_external_oauth_integration.test", - ImportState: true, - ImportStateVerify: true, + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalOauthIntegration/completeWithRsaPublicKeysAndBlockedRolesList"), + ConfigVariables: m(), + ResourceName: "snowflake_external_oauth_integration.test", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"external_oauth_rsa_public_key", "external_oauth_rsa_public_key_2", "external_oauth_scope_mapping_attribute"}, + }, + }, + }) +} + +func TestAcc_ExternalOauthIntegration_invalidAnyRoleMode(t *testing.T) { + m := func() map[string]config.Variable { + return map[string]config.Variable{ + "comment": config.StringVariable("foo"), + "enabled": config.BoolVariable(true), + "external_oauth_allowed_roles_list": config.SetVariable(config.StringVariable("foo")), + "external_oauth_any_role_mode": config.StringVariable("invalid"), + "external_oauth_audience_list": config.SetVariable(config.StringVariable("foo")), + "external_oauth_blocked_roles_list": config.SetVariable(config.StringVariable("foo")), + "external_oauth_issuer": config.StringVariable("foo"), + "external_oauth_jws_keys_url": config.SetVariable(config.StringVariable("foo")), + "external_oauth_rsa_public_key": config.StringVariable("foo"), + "external_oauth_rsa_public_key_2": config.StringVariable("foo"), + "external_oauth_scope_delimiter": config.StringVariable("foo"), + "external_oauth_scope_mapping_attribute": config.StringVariable("foo"), + "external_oauth_snowflake_user_mapping_attribute": config.StringVariable(string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress)), + "external_oauth_token_user_mapping_claim": config.SetVariable(config.StringVariable("foo")), + "name": config.StringVariable("foo"), + "external_oauth_type": config.StringVariable(string(sdk.ExternalOauthSecurityIntegrationTypeCustom)), + } + } + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalOauthIntegration/completeWithJwsKeysUrlAndAllowedRolesList"), + ConfigVariables: m(), + ExpectError: regexp.MustCompile("Error: invalid ExternalOauthSecurityIntegrationAnyRoleModeOption: INVALID"), }, }, }) } -func TestAcc_ExternalOauthIntegrationCustom(t *testing.T) { - oauthIntName := acc.TestClient().Ids.Alpha() - integrationType := "CUSTOM" +func TestAcc_ExternalOauthIntegration_invalidSnowflakeUserMappingAttribute(t *testing.T) { + m := func() map[string]config.Variable { + return map[string]config.Variable{ + "comment": config.StringVariable("foo"), + "enabled": config.BoolVariable(true), + "external_oauth_allowed_roles_list": config.SetVariable(config.StringVariable("foo")), + "external_oauth_any_role_mode": config.StringVariable(string(sdk.ExternalOauthSecurityIntegrationAnyRoleModeDisable)), + "external_oauth_audience_list": config.SetVariable(config.StringVariable("foo")), + "external_oauth_blocked_roles_list": config.SetVariable(config.StringVariable("foo")), + "external_oauth_issuer": config.StringVariable("foo"), + "external_oauth_jws_keys_url": config.SetVariable(config.StringVariable("foo")), + "external_oauth_rsa_public_key": config.StringVariable("foo"), + "external_oauth_rsa_public_key_2": config.StringVariable("foo"), + "external_oauth_scope_delimiter": config.StringVariable("foo"), + "external_oauth_scope_mapping_attribute": config.StringVariable("foo"), + "external_oauth_snowflake_user_mapping_attribute": config.StringVariable("invalid"), + "external_oauth_token_user_mapping_claim": config.SetVariable(config.StringVariable("foo")), + "name": config.StringVariable("foo"), + "external_oauth_type": config.StringVariable(string(sdk.ExternalOauthSecurityIntegrationTypeCustom)), + } + } + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalOauthIntegration/completeWithJwsKeysUrlAndAllowedRolesList"), + ConfigVariables: m(), + ExpectError: regexp.MustCompile("Error: invalid ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeOption: INVALID"), + }, + }, + }) +} - issuer := fmt.Sprintf("https://sts.windows.net/%s", uuid.NewString()) +func TestAcc_ExternalOauthIntegration_invalidOauthType(t *testing.T) { + m := func() map[string]config.Variable { + return map[string]config.Variable{ + "comment": config.StringVariable("foo"), + "enabled": config.BoolVariable(true), + "external_oauth_allowed_roles_list": config.SetVariable(config.StringVariable("foo")), + "external_oauth_any_role_mode": config.StringVariable(string(sdk.ExternalOauthSecurityIntegrationAnyRoleModeDisable)), + "external_oauth_audience_list": config.SetVariable(config.StringVariable("foo")), + "external_oauth_blocked_roles_list": config.SetVariable(config.StringVariable("foo")), + "external_oauth_issuer": config.StringVariable("foo"), + "external_oauth_jws_keys_url": config.SetVariable(config.StringVariable("foo")), + "external_oauth_rsa_public_key": config.StringVariable("foo"), + "external_oauth_rsa_public_key_2": config.StringVariable("foo"), + "external_oauth_scope_delimiter": config.StringVariable("foo"), + "external_oauth_scope_mapping_attribute": config.StringVariable("foo"), + "external_oauth_snowflake_user_mapping_attribute": config.StringVariable(string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress)), + "external_oauth_token_user_mapping_claim": config.SetVariable(config.StringVariable("foo")), + "name": config.StringVariable("foo"), + "external_oauth_type": config.StringVariable("invalid"), + } + } + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + Steps: []resource.TestStep{ + { + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalOauthIntegration/completeWithJwsKeysUrlAndAllowedRolesList"), + ConfigVariables: m(), + ExpectError: regexp.MustCompile("Error: invalid ExternalOauthSecurityIntegrationTypeOption: INVALID"), + }, + }, + }) +} +func TestAcc_ExternalOauthIntegration_InvalidIncomplete(t *testing.T) { + id := acc.TestClient().Ids.RandomAccountObjectIdentifier() + m := func() map[string]config.Variable { + return map[string]config.Variable{ + "name": config.StringVariable(id.Name()), + } + } resource.Test(t, resource.TestCase{ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, PreCheck: func() { acc.TestAccPreCheck(t) }, TerraformVersionChecks: []tfversion.TerraformVersionCheck{ tfversion.RequireAbove(tfversion.Version1_5_0), }, - CheckDestroy: nil, + ErrorCheck: helpers.AssertErrorContainsPartsFunc(t, []string{ + // Some strings are trimmed because of inconsistent '\n' placement from tf error messages. + `The argument "external_oauth_type" is required, but no definition was found.`, + `The argument "external_oauth_snowflake_user_mapping_attribute" is required,`, + `The argument "enabled" is required, but no definition was found.`, + `The argument "external_oauth_issuer" is required,`, + `The argument "external_oauth_token_user_mapping_claim" is required,`, + }), Steps: []resource.TestStep{ { - Config: fmt.Sprintf(` - resource "snowflake_external_oauth_integration" "test" { - name = "%s" - type = "%s" - enabled = true - issuer = "%s" - snowflake_user_mapping_attribute = "LOGIN_NAME" - jws_keys_urls = ["https://login.windows.net/common/discovery/keys"] - audience_urls = ["https://analysis.windows.net/powerbi/connector/Snowflake"] - token_user_mapping_claims = ["upn", "test"] - scope_mapping_attribute = "scp" - comment = "hey" - } - `, oauthIntName, integrationType, issuer), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "name", oauthIntName), - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "type", integrationType), - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "enabled", "true"), - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "issuer", issuer), - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "snowflake_user_mapping_attribute", "LOGIN_NAME"), - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "scope_mapping_attribute", "scp"), - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "token_user_mapping_claims.#", "2"), - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "token_user_mapping_claims.0", "test"), - resource.TestCheckResourceAttr("snowflake_external_oauth_integration.test", "token_user_mapping_claims.1", "upn"), + ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ExternalOauthIntegration/invalid"), + ConfigVariables: m(), + }, + }, + }) +} + +func TestAcc_ExternalOauthIntegration_migrateFromVersion092_withRsaPublicKeysAndBlockedRolesList(t *testing.T) { + id := acc.TestClient().Ids.RandomAccountObjectIdentifier() + role, roleCleanup := acc.TestClient().Role.CreateRole(t) + t.Cleanup(roleCleanup) + issuer := random.String() + rsaKey, _ := random.GenerateRSAPublicKey(t) + resourceName := "snowflake_external_oauth_integration.test" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + + Steps: []resource.TestStep{ + { + ExternalProviders: map[string]resource.ExternalProvider{ + "snowflake": { + VersionConstraint: "=0.92.0", + Source: "Snowflake-Labs/snowflake", + }, + }, + Config: externalOauthIntegrationWithRsaPublicKeysAndBlockedRolesListv092(id.Name(), issuer, rsaKey, role.Name), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", id.Name()), + resource.TestCheckResourceAttr(resourceName, "type", string(sdk.ExternalOauthSecurityIntegrationTypeCustom)), + resource.TestCheckResourceAttr(resourceName, "issuer", issuer), + resource.TestCheckResourceAttr(resourceName, "token_user_mapping_claims.#", "1"), + resource.TestCheckResourceAttr(resourceName, "token_user_mapping_claims.0", "foo"), + resource.TestCheckResourceAttr(resourceName, "snowflake_user_mapping_attribute", string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeLoginName)), + resource.TestCheckResourceAttr(resourceName, "scope_mapping_attribute", "foo"), + resource.TestCheckResourceAttr(resourceName, "rsa_public_key", rsaKey), + resource.TestCheckResourceAttr(resourceName, "rsa_public_key_2", rsaKey), + resource.TestCheckResourceAttr(resourceName, "blocked_roles.#", "1"), + resource.TestCheckResourceAttr(resourceName, "blocked_roles.0", role.Name), + resource.TestCheckResourceAttr(resourceName, "audience_urls.#", "1"), + resource.TestCheckResourceAttr(resourceName, "audience_urls.0", "foo"), + resource.TestCheckResourceAttr(resourceName, "any_role_mode", string(sdk.ExternalOauthSecurityIntegrationAnyRoleModeDisable)), + resource.TestCheckResourceAttr(resourceName, "scope_delimiter", ":"), ), }, { - ResourceName: "snowflake_external_oauth_integration.test", - ImportState: true, - ImportStateVerify: true, + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + Config: externalOauthIntegrationWithRsaPublicKeysAndBlockedRolesListv093(id.Name(), issuer, rsaKey, role.Name), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", id.Name()), + resource.TestCheckResourceAttr(resourceName, "external_oauth_type", string(sdk.ExternalOauthSecurityIntegrationTypeCustom)), + resource.TestCheckResourceAttr(resourceName, "external_oauth_issuer", issuer), + resource.TestCheckResourceAttr(resourceName, "external_oauth_token_user_mapping_claim.#", "1"), + resource.TestCheckResourceAttr(resourceName, "external_oauth_token_user_mapping_claim.0", "foo"), + resource.TestCheckResourceAttr(resourceName, "external_oauth_snowflake_user_mapping_attribute", string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeLoginName)), + resource.TestCheckResourceAttr(resourceName, "external_oauth_scope_mapping_attribute", "foo"), + resource.TestCheckResourceAttr(resourceName, "external_oauth_rsa_public_key", rsaKey), + resource.TestCheckResourceAttr(resourceName, "external_oauth_rsa_public_key_2", rsaKey), + resource.TestCheckResourceAttr(resourceName, "external_oauth_blocked_roles_list.#", "1"), + resource.TestCheckResourceAttr(resourceName, "external_oauth_blocked_roles_list.0", role.Name), + resource.TestCheckResourceAttr(resourceName, "external_oauth_audience_list.#", "1"), + resource.TestCheckResourceAttr(resourceName, "external_oauth_audience_list.0", "foo"), + resource.TestCheckResourceAttr(resourceName, "external_oauth_any_role_mode", string(sdk.ExternalOauthSecurityIntegrationAnyRoleModeDisable)), + resource.TestCheckResourceAttr(resourceName, "external_oauth_scope_delimiter", ":"), + ), }, }, }) } -func externalOauthIntegrationConfig(name, integrationType, issuer, comment string) string { - return fmt.Sprintf(` - resource "snowflake_external_oauth_integration" "test" { - name = "%s" - type = "%s" - enabled = true - issuer = "%s" - snowflake_user_mapping_attribute = "LOGIN_NAME" - jws_keys_urls = ["https://login.windows.net/common/discovery/keys"] - audience_urls = ["https://analysis.windows.net/powerbi/connector/Snowflake"] - token_user_mapping_claims = ["upn", "test"] - comment = "%s" - } - `, name, integrationType, issuer, comment) +func externalOauthIntegrationWithRsaPublicKeysAndBlockedRolesListv092(name, issuer, rsaKey, roleName string) string { + s := ` +locals { + key_raw = <<-EOT +%s + EOT + key = trimsuffix(local.key_raw, "\n") +} +resource "snowflake_external_oauth_integration" "test" { + name = "%s" + enabled = true + type = "CUSTOM" + issuer = "%s" + token_user_mapping_claims = ["foo"] + snowflake_user_mapping_attribute = "LOGIN_NAME" + scope_mapping_attribute = "foo" + rsa_public_key = local.key + rsa_public_key_2 = local.key + blocked_roles = ["%s"] + audience_urls = ["foo"] + any_role_mode = "DISABLE" + scope_delimiter = ":" +}` + return fmt.Sprintf(s, rsaKey, name, issuer, roleName) +} + +func externalOauthIntegrationWithRsaPublicKeysAndBlockedRolesListv093(name, issuer, rsaKey, roleName string) string { + s := ` +locals { + key_raw = <<-EOT +%s + EOT + key = trimsuffix(local.key_raw, "\n") +} +resource "snowflake_external_oauth_integration" "test" { + name = "%s" + enabled = true + external_oauth_type = "CUSTOM" + external_oauth_issuer = "%s" + external_oauth_token_user_mapping_claim = ["foo"] + external_oauth_snowflake_user_mapping_attribute = "LOGIN_NAME" + external_oauth_scope_mapping_attribute = "foo" + external_oauth_rsa_public_key = local.key + external_oauth_rsa_public_key_2 = local.key + external_oauth_blocked_roles_list = ["%s"] + external_oauth_audience_list = ["foo"] + external_oauth_any_role_mode = "DISABLE" + external_oauth_scope_delimiter = ":" +}` + return fmt.Sprintf(s, rsaKey, name, issuer, roleName) +} + +func TestAcc_ExternalOauthIntegration_migrateFromVersion092_withJwsKeysUrlAndAllowedRolesList(t *testing.T) { + id := acc.TestClient().Ids.RandomAccountObjectIdentifier() + role, roleCleanup := acc.TestClient().Role.CreateRole(t) + t.Cleanup(roleCleanup) + issuer := random.String() + resourceName := "snowflake_external_oauth_integration.test" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + + Steps: []resource.TestStep{ + { + ExternalProviders: map[string]resource.ExternalProvider{ + "snowflake": { + VersionConstraint: "=0.92.0", + Source: "Snowflake-Labs/snowflake", + }, + }, + Config: externalOauthIntegrationWithJwsKeysUrlAndAllowedRolesListv092(id.Name(), issuer, role.Name), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", id.Name()), + resource.TestCheckResourceAttr(resourceName, "type", string(sdk.ExternalOauthSecurityIntegrationTypeCustom)), + resource.TestCheckResourceAttr(resourceName, "issuer", issuer), + resource.TestCheckResourceAttr(resourceName, "token_user_mapping_claims.#", "1"), + resource.TestCheckResourceAttr(resourceName, "token_user_mapping_claims.0", "foo"), + resource.TestCheckResourceAttr(resourceName, "snowflake_user_mapping_attribute", string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeLoginName)), + resource.TestCheckResourceAttr(resourceName, "scope_mapping_attribute", "foo"), + resource.TestCheckResourceAttr(resourceName, "jws_keys_urls.#", "1"), + resource.TestCheckResourceAttr(resourceName, "jws_keys_urls.0", "https://example.com"), + resource.TestCheckResourceAttr(resourceName, "allowed_roles.#", "1"), + resource.TestCheckResourceAttr(resourceName, "allowed_roles.0", role.Name), + resource.TestCheckResourceAttr(resourceName, "audience_urls.#", "1"), + resource.TestCheckResourceAttr(resourceName, "audience_urls.0", "foo"), + resource.TestCheckResourceAttr(resourceName, "any_role_mode", string(sdk.ExternalOauthSecurityIntegrationAnyRoleModeDisable)), + resource.TestCheckResourceAttr(resourceName, "scope_delimiter", ":"), + ), + }, + { + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + Config: externalOauthIntegrationWithJwsKeysUrlAndAllowedRolesListv093(id.Name(), issuer, role.Name), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr(resourceName, "name", id.Name()), + resource.TestCheckResourceAttr(resourceName, "external_oauth_type", string(sdk.ExternalOauthSecurityIntegrationTypeCustom)), + resource.TestCheckResourceAttr(resourceName, "external_oauth_issuer", issuer), + resource.TestCheckResourceAttr(resourceName, "external_oauth_token_user_mapping_claim.#", "1"), + resource.TestCheckResourceAttr(resourceName, "external_oauth_token_user_mapping_claim.0", "foo"), + resource.TestCheckResourceAttr(resourceName, "external_oauth_snowflake_user_mapping_attribute", string(sdk.ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeLoginName)), + resource.TestCheckResourceAttr(resourceName, "external_oauth_scope_mapping_attribute", "foo"), + resource.TestCheckResourceAttr(resourceName, "external_oauth_jws_keys_url.#", "1"), + resource.TestCheckResourceAttr(resourceName, "external_oauth_jws_keys_url.0", "https://example.com"), + resource.TestCheckResourceAttr(resourceName, "external_oauth_allowed_roles_list.#", "1"), + resource.TestCheckResourceAttr(resourceName, "external_oauth_allowed_roles_list.0", role.Name), + resource.TestCheckResourceAttr(resourceName, "external_oauth_audience_list.#", "1"), + resource.TestCheckResourceAttr(resourceName, "external_oauth_audience_list.0", "foo"), + resource.TestCheckResourceAttr(resourceName, "external_oauth_any_role_mode", string(sdk.ExternalOauthSecurityIntegrationAnyRoleModeDisable)), + resource.TestCheckResourceAttr(resourceName, "external_oauth_scope_delimiter", ":"), + ), + }, + }, + }) +} + +func externalOauthIntegrationWithJwsKeysUrlAndAllowedRolesListv092(name, issuer, roleName string) string { + s := ` +resource "snowflake_external_oauth_integration" "test" { + name = "%s" + enabled = true + type = "CUSTOM" + issuer = "%s" + token_user_mapping_claims = ["foo"] + snowflake_user_mapping_attribute = "LOGIN_NAME" + scope_mapping_attribute = "foo" + jws_keys_urls = ["https://example.com"] + allowed_roles = ["%s"] + audience_urls = ["foo"] + any_role_mode = "DISABLE" + scope_delimiter = ":" +}` + return fmt.Sprintf(s, name, issuer, roleName) +} + +func externalOauthIntegrationWithJwsKeysUrlAndAllowedRolesListv093(name, issuer, roleName string) string { + s := ` +resource "snowflake_external_oauth_integration" "test" { + name = "%s" + enabled = true + external_oauth_type = "CUSTOM" + external_oauth_issuer = "%s" + external_oauth_token_user_mapping_claim = ["foo"] + external_oauth_snowflake_user_mapping_attribute = "LOGIN_NAME" + external_oauth_scope_mapping_attribute = "foo" + external_oauth_jws_keys_url = ["https://example.com"] + external_oauth_allowed_roles_list = ["%s"] + external_oauth_audience_list = ["foo"] + external_oauth_any_role_mode = "DISABLE" + external_oauth_scope_delimiter = ":" +}` + return fmt.Sprintf(s, name, issuer, roleName) } diff --git a/pkg/resources/external_oauth_integration_stage_upgraders.go b/pkg/resources/external_oauth_integration_stage_upgraders.go new file mode 100644 index 0000000000..88ba0dd65a --- /dev/null +++ b/pkg/resources/external_oauth_integration_stage_upgraders.go @@ -0,0 +1,38 @@ +package resources + +import ( + "context" +) + +func v092ExternalOauthIntegrationStateUpgrader(ctx context.Context, rawState map[string]any, meta any) (map[string]any, error) { + if rawState == nil { + return rawState, nil + } + + type renameField struct { + from string + to string + } + fieldsToRename := []renameField{ + {from: "type", to: "external_oauth_type"}, + {from: "issuer", to: "external_oauth_issuer"}, + {from: "token_user_mapping_claims", to: "external_oauth_token_user_mapping_claim"}, + {from: "snowflake_user_mapping_attribute", to: "external_oauth_snowflake_user_mapping_attribute"}, + {from: "scope_mapping_attribute", to: "external_oauth_scope_mapping_attribute"}, + {from: "jws_keys_urls", to: "external_oauth_jws_keys_url"}, + {from: "rsa_public_key", to: "external_oauth_rsa_public_key"}, + {from: "rsa_public_key_2", to: "external_oauth_rsa_public_key_2"}, + {from: "blocked_roles", to: "external_oauth_blocked_roles_list"}, + {from: "allowed_roles", to: "external_oauth_allowed_roles_list"}, + {from: "audience_urls", to: "external_oauth_audience_list"}, + {from: "any_role_mode", to: "external_oauth_any_role_mode"}, + {from: "scope_delimiter", to: "external_oauth_scope_delimiter"}, + } + + for _, field := range fieldsToRename { + rawState[field.to] = rawState[field.from] + delete(rawState, field.from) + } + + return rawState, nil +} diff --git a/pkg/resources/show_and_describe_handlers.go b/pkg/resources/show_and_describe_handlers.go index 0e90c9e169..fcf546ee69 100644 --- a/pkg/resources/show_and_describe_handlers.go +++ b/pkg/resources/show_and_describe_handlers.go @@ -7,9 +7,10 @@ import ( ) const ( - ShowOutputAttributeName = "show_output" - DescribeOutputAttributeName = "describe_output" - ParametersAttributeName = "parameters" + ShowOutputAttributeName = "show_output" + DescribeOutputAttributeName = "describe_output" + ParametersAttributeName = "parameters" + RelatedParametersAttributeName = "related_parameters" ) // handleExternalChangesToObjectInShow assumes that show output is kept in ShowOutputAttributeName attribute diff --git a/pkg/resources/special_values.go b/pkg/resources/special_values.go index 3fa44729f0..4a74508a8e 100644 --- a/pkg/resources/special_values.go +++ b/pkg/resources/special_values.go @@ -41,3 +41,7 @@ func booleanStringFieldDescription(description string) string { func externalChangesNotDetectedFieldDescription(description string) string { return fmt.Sprintf(`%s External changes for this field won't be detected. In case you want to apply external changes, you can re-create the resource manually using "terraform taint".`, description) } + +func withPrivilegedRolesDescription(description, paramName string) string { + return fmt.Sprintf(`%s By default, this list includes the ACCOUNTADMIN, ORGADMIN and SECURITYADMIN roles. To remove these privileged roles from the list, use the ALTER ACCOUNT command to set the %s account parameter to FALSE. `, description, paramName) +} diff --git a/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/basic/test.tf b/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/basic/test.tf new file mode 100644 index 0000000000..b0c709f4ca --- /dev/null +++ b/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/basic/test.tf @@ -0,0 +1,9 @@ +resource "snowflake_external_oauth_integration" "test" { + name = var.name + external_oauth_type = var.external_oauth_type + enabled = var.enabled + external_oauth_issuer = var.external_oauth_issuer + external_oauth_token_user_mapping_claim = var.external_oauth_token_user_mapping_claim + external_oauth_snowflake_user_mapping_attribute = var.external_oauth_snowflake_user_mapping_attribute + external_oauth_jws_keys_url = var.external_oauth_jws_keys_url +} diff --git a/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/basic/variables.tf b/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/basic/variables.tf new file mode 100644 index 0000000000..d1ddaa878a --- /dev/null +++ b/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/basic/variables.tf @@ -0,0 +1,21 @@ +variable "name" { + type = string +} +variable "external_oauth_type" { + type = string +} +variable "enabled" { + type = bool +} +variable "external_oauth_issuer" { + type = string +} +variable "external_oauth_snowflake_user_mapping_attribute" { + type = string +} +variable "external_oauth_token_user_mapping_claim" { + type = set(string) +} +variable "external_oauth_jws_keys_url" { + type = set(string) +} diff --git a/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/completeWithJwsKeysUrlAndAllowedRolesList/test.tf b/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/completeWithJwsKeysUrlAndAllowedRolesList/test.tf new file mode 100644 index 0000000000..955d08e6c3 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/completeWithJwsKeysUrlAndAllowedRolesList/test.tf @@ -0,0 +1,15 @@ +resource "snowflake_external_oauth_integration" "test" { + comment = var.comment + enabled = var.enabled + external_oauth_allowed_roles_list = var.external_oauth_allowed_roles_list + external_oauth_any_role_mode = var.external_oauth_any_role_mode + external_oauth_audience_list = var.external_oauth_audience_list + external_oauth_issuer = var.external_oauth_issuer + external_oauth_jws_keys_url = var.external_oauth_jws_keys_url + external_oauth_scope_delimiter = var.external_oauth_scope_delimiter + external_oauth_scope_mapping_attribute = var.external_oauth_scope_mapping_attribute + external_oauth_snowflake_user_mapping_attribute = var.external_oauth_snowflake_user_mapping_attribute + external_oauth_token_user_mapping_claim = var.external_oauth_token_user_mapping_claim + name = var.name + external_oauth_type = var.external_oauth_type +} diff --git a/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/completeWithJwsKeysUrlAndAllowedRolesList/variables.tf b/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/completeWithJwsKeysUrlAndAllowedRolesList/variables.tf new file mode 100644 index 0000000000..be2785222b --- /dev/null +++ b/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/completeWithJwsKeysUrlAndAllowedRolesList/variables.tf @@ -0,0 +1,40 @@ + +variable "comment" { + type = string +} +variable "enabled" { + type = bool +} +variable "external_oauth_allowed_roles_list" { + type = set(string) +} +variable "external_oauth_any_role_mode" { + type = string +} +variable "external_oauth_audience_list" { + type = set(string) +} +variable "external_oauth_issuer" { + type = string +} +variable "external_oauth_jws_keys_url" { + type = set(string) +} +variable "external_oauth_scope_delimiter" { + type = string +} +variable "external_oauth_scope_mapping_attribute" { + type = string +} +variable "external_oauth_snowflake_user_mapping_attribute" { + type = string +} +variable "external_oauth_token_user_mapping_claim" { + type = set(string) +} +variable "name" { + type = string +} +variable "external_oauth_type" { + type = string +} diff --git a/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/completeWithRsaPublicKeysAndBlockedRolesList/test.tf b/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/completeWithRsaPublicKeysAndBlockedRolesList/test.tf new file mode 100644 index 0000000000..20df3fe6dc --- /dev/null +++ b/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/completeWithRsaPublicKeysAndBlockedRolesList/test.tf @@ -0,0 +1,16 @@ +resource "snowflake_external_oauth_integration" "test" { + comment = var.comment + enabled = var.enabled + external_oauth_blocked_roles_list = var.external_oauth_blocked_roles_list + external_oauth_any_role_mode = var.external_oauth_any_role_mode + external_oauth_audience_list = var.external_oauth_audience_list + external_oauth_issuer = var.external_oauth_issuer + external_oauth_rsa_public_key = var.external_oauth_rsa_public_key + external_oauth_rsa_public_key_2 = var.external_oauth_rsa_public_key_2 + external_oauth_scope_delimiter = var.external_oauth_scope_delimiter + external_oauth_scope_mapping_attribute = var.external_oauth_scope_mapping_attribute + external_oauth_snowflake_user_mapping_attribute = var.external_oauth_snowflake_user_mapping_attribute + external_oauth_token_user_mapping_claim = var.external_oauth_token_user_mapping_claim + name = var.name + external_oauth_type = var.external_oauth_type +} diff --git a/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/completeWithRsaPublicKeysAndBlockedRolesList/variables.tf b/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/completeWithRsaPublicKeysAndBlockedRolesList/variables.tf new file mode 100644 index 0000000000..43a23ed6c7 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/completeWithRsaPublicKeysAndBlockedRolesList/variables.tf @@ -0,0 +1,42 @@ +variable "comment" { + type = string +} +variable "enabled" { + type = bool +} +variable "external_oauth_blocked_roles_list" { + type = set(string) +} +variable "external_oauth_any_role_mode" { + type = string +} +variable "external_oauth_audience_list" { + type = set(string) +} +variable "external_oauth_issuer" { + type = string +} +variable "external_oauth_scope_delimiter" { + type = string +} +variable "external_oauth_scope_mapping_attribute" { + type = string +} +variable "external_oauth_snowflake_user_mapping_attribute" { + type = string +} +variable "external_oauth_token_user_mapping_claim" { + type = set(string) +} +variable "name" { + type = string +} +variable "external_oauth_type" { + type = string +} +variable "external_oauth_rsa_public_key" { + type = string +} +variable "external_oauth_rsa_public_key_2" { + type = string +} diff --git a/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/invalid/test.tf b/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/invalid/test.tf new file mode 100644 index 0000000000..8cc64d16dd --- /dev/null +++ b/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/invalid/test.tf @@ -0,0 +1,3 @@ +resource "snowflake_external_oauth_integration" "test" { + name = var.name +} diff --git a/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/invalid/variables.tf b/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/invalid/variables.tf new file mode 100644 index 0000000000..77e5cc9698 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/invalid/variables.tf @@ -0,0 +1,3 @@ +variable "name" { + type = string +} diff --git a/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/unset/test.tf b/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/unset/test.tf new file mode 100644 index 0000000000..ba631d3d84 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/unset/test.tf @@ -0,0 +1,10 @@ +resource "snowflake_external_oauth_integration" "test" { + name = var.name + external_oauth_type = var.external_oauth_type + enabled = var.enabled + external_oauth_issuer = var.external_oauth_issuer + external_oauth_token_user_mapping_claim = var.external_oauth_token_user_mapping_claim + external_oauth_snowflake_user_mapping_attribute = var.external_oauth_snowflake_user_mapping_attribute + external_oauth_jws_keys_url = var.external_oauth_jws_keys_url + external_oauth_scope_mapping_attribute = var.external_oauth_scope_mapping_attribute +} diff --git a/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/unset/variables.tf b/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/unset/variables.tf new file mode 100644 index 0000000000..54333edfc4 --- /dev/null +++ b/pkg/resources/testdata/TestAcc_ExternalOauthIntegration/unset/variables.tf @@ -0,0 +1,24 @@ +variable "name" { + type = string +} +variable "external_oauth_type" { + type = string +} +variable "enabled" { + type = bool +} +variable "external_oauth_issuer" { + type = string +} +variable "external_oauth_snowflake_user_mapping_attribute" { + type = string +} +variable "external_oauth_token_user_mapping_claim" { + type = set(string) +} +variable "external_oauth_jws_keys_url" { + type = set(string) +} +variable "external_oauth_scope_mapping_attribute" { + type = string +} diff --git a/pkg/schemas/external_oauth_security_integration.go b/pkg/schemas/external_oauth_security_integration.go new file mode 100644 index 0000000000..544d057690 --- /dev/null +++ b/pkg/schemas/external_oauth_security_integration.go @@ -0,0 +1,68 @@ +package schemas + +import ( + "slices" + "strings" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +// DescribeExternalOauthSecurityIntegrationSchema represents output of DESCRIBE query for the single SecurityIntegration. +var DescribeExternalOauthSecurityIntegrationSchema = map[string]*schema.Schema{ + "enabled": DescribePropertyListSchema, + "external_oauth_issuer": DescribePropertyListSchema, + "external_oauth_jws_keys_url": DescribePropertyListSchema, + "external_oauth_any_role_mode": DescribePropertyListSchema, + "external_oauth_rsa_public_key": DescribePropertyListSchema, + "external_oauth_rsa_public_key_2": DescribePropertyListSchema, + "external_oauth_blocked_roles_list": DescribePropertyListSchema, + "external_oauth_allowed_roles_list": DescribePropertyListSchema, + "external_oauth_audience_list": DescribePropertyListSchema, + "external_oauth_token_user_mapping_claim": DescribePropertyListSchema, + "external_oauth_snowflake_user_mapping_attribute": DescribePropertyListSchema, + "external_oauth_scope_delimiter": DescribePropertyListSchema, + "comment": DescribePropertyListSchema, +} + +var _ = DescribeExternalOauthSecurityIntegrationSchema + +func ExternalOauthSecurityIntegrationPropertiesToSchema(securityIntegrationProperties []sdk.SecurityIntegrationProperty) map[string]any { + securityIntegrationSchema := make(map[string]any) + for _, securityIntegrationProperty := range securityIntegrationProperties { + securityIntegrationProperty := securityIntegrationProperty + switch securityIntegrationProperty.Name { + case "ENABLED", + "EXTERNAL_OAUTH_ISSUER", + "EXTERNAL_OAUTH_JWS_KEYS_URL", + "EXTERNAL_OAUTH_ANY_ROLE_MODE", + "EXTERNAL_OAUTH_RSA_PUBLIC_KEY", + "EXTERNAL_OAUTH_RSA_PUBLIC_KEY_2", + "EXTERNAL_OAUTH_BLOCKED_ROLES_LIST", + "EXTERNAL_OAUTH_ALLOWED_ROLES_LIST", + "EXTERNAL_OAUTH_AUDIENCE_LIST", + "EXTERNAL_OAUTH_TOKEN_USER_MAPPING_CLAIM", + "EXTERNAL_OAUTH_SNOWFLAKE_USER_MAPPING_ATTRIBUTE", + "EXTERNAL_OAUTH_SCOPE_DELIMITER", + "COMMENT": + securityIntegrationSchema[strings.ToLower(securityIntegrationProperty.Name)] = []map[string]any{SecurityIntegrationPropertyToSchema(&securityIntegrationProperty)} + } + } + return securityIntegrationSchema +} + +var _ = ExternalOauthSecurityIntegrationPropertiesToSchema + +var ShowExternalOauthParametersSchema = map[string]*schema.Schema{ + strings.ToLower(string(sdk.AccountParameterExternalOAuthAddPrivilegedRolesToBlockedList)): ParameterListSchema, +} + +func ExternalOauthParametersToSchema(parameters []*sdk.Parameter) map[string]any { + schemaMap := make(map[string]any) + for _, param := range parameters { + if slices.Contains([]sdk.AccountParameter{sdk.AccountParameterExternalOAuthAddPrivilegedRolesToBlockedList}, sdk.AccountParameter(param.Key)) { + schemaMap[strings.ToLower(param.Key)] = []map[string]any{ParameterToSchema(param)} + } + } + return schemaMap +} diff --git a/pkg/sdk/security_integrations_def.go b/pkg/sdk/security_integrations_def.go index f0f03ec6c3..1fb48d58cf 100644 --- a/pkg/sdk/security_integrations_def.go +++ b/pkg/sdk/security_integrations_def.go @@ -45,6 +45,29 @@ const ( ExternalOauthSecurityIntegrationTypeCustom ExternalOauthSecurityIntegrationTypeOption = "CUSTOM" ) +var AllExternalOauthSecurityIntegrationTypes = []ExternalOauthSecurityIntegrationTypeOption{ + ExternalOauthSecurityIntegrationTypeOkta, + ExternalOauthSecurityIntegrationTypeAzure, + ExternalOauthSecurityIntegrationTypePingFederate, + ExternalOauthSecurityIntegrationTypeCustom, +} + +func ToExternalOauthSecurityIntegrationTypeOption(s string) (ExternalOauthSecurityIntegrationTypeOption, error) { + s = strings.ToUpper(s) + switch s { + case string(ExternalOauthSecurityIntegrationTypeOkta): + return ExternalOauthSecurityIntegrationTypeOkta, nil + case string(ExternalOauthSecurityIntegrationTypeAzure): + return ExternalOauthSecurityIntegrationTypeAzure, nil + case string(ExternalOauthSecurityIntegrationTypePingFederate): + return ExternalOauthSecurityIntegrationTypePingFederate, nil + case string(ExternalOauthSecurityIntegrationTypeCustom): + return ExternalOauthSecurityIntegrationTypeCustom, nil + default: + return "", fmt.Errorf("invalid ExternalOauthSecurityIntegrationTypeOption: %s", s) + } +} + type ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeOption string const ( @@ -52,6 +75,23 @@ const ( ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeOption = "EMAIL_ADDRESS" ) +var AllExternalOauthSecurityIntegrationSnowflakeUserMappingAttributes = []ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeOption{ + ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeLoginName, + ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress, +} + +func ToExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeOption(s string) (ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeOption, error) { + s = strings.ToUpper(s) + switch s { + case string(ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeLoginName): + return ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeLoginName, nil + case string(ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress): + return ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeEmailAddress, nil + default: + return "", fmt.Errorf("invalid ExternalOauthSecurityIntegrationSnowflakeUserMappingAttributeOption: %s", s) + } +} + type ExternalOauthSecurityIntegrationAnyRoleModeOption string const ( @@ -60,6 +100,26 @@ const ( ExternalOauthSecurityIntegrationAnyRoleModeEnableForPrivilege ExternalOauthSecurityIntegrationAnyRoleModeOption = "ENABLE_FOR_PRIVILEGE" ) +var AllExternalOauthSecurityIntegrationAnyRoleModes = []ExternalOauthSecurityIntegrationAnyRoleModeOption{ + ExternalOauthSecurityIntegrationAnyRoleModeDisable, + ExternalOauthSecurityIntegrationAnyRoleModeEnable, + ExternalOauthSecurityIntegrationAnyRoleModeEnableForPrivilege, +} + +func ToExternalOauthSecurityIntegrationAnyRoleModeOption(s string) (ExternalOauthSecurityIntegrationAnyRoleModeOption, error) { + s = strings.ToUpper(s) + switch s { + case string(ExternalOauthSecurityIntegrationAnyRoleModeDisable): + return ExternalOauthSecurityIntegrationAnyRoleModeDisable, nil + case string(ExternalOauthSecurityIntegrationAnyRoleModeEnable): + return ExternalOauthSecurityIntegrationAnyRoleModeEnable, nil + case string(ExternalOauthSecurityIntegrationAnyRoleModeEnableForPrivilege): + return ExternalOauthSecurityIntegrationAnyRoleModeEnableForPrivilege, nil + default: + return "", fmt.Errorf("invalid ExternalOauthSecurityIntegrationAnyRoleModeOption: %s", s) + } +} + type OauthSecurityIntegrationUseSecondaryRolesOption string const ( @@ -403,13 +463,15 @@ var externalOauthIntegrationSetDef = g.NewQueryStruct("ExternalOauthIntegrationS g.ParameterOptions(), ). OptionalTextAssignment("EXTERNAL_OAUTH_SCOPE_DELIMITER", g.ParameterOptions().SingleQuotes()). - OptionalComment(). + OptionalTextAssignment("EXTERNAL_OAUTH_SCOPE_MAPPING_ATTRIBUTE", g.ParameterOptions().SingleQuotes()). + // TODO(SNOW-1461780): use COMMENT in unset and here use OptionalComment + OptionalAssignment("COMMENT", "StringAllowEmpty", g.ParameterOptions()). WithValidation(g.ConflictingFields, "ExternalOauthBlockedRolesList", "ExternalOauthAllowedRolesList"). WithValidation(g.ConflictingFields, "ExternalOauthJwsKeysUrl", "ExternalOauthRsaPublicKey"). WithValidation(g.ConflictingFields, "ExternalOauthJwsKeysUrl", "ExternalOauthRsaPublicKey2"). WithValidation(g.AtLeastOneValueSet, "Enabled", "ExternalOauthType", "ExternalOauthIssuer", "ExternalOauthTokenUserMappingClaim", "ExternalOauthSnowflakeUserMappingAttribute", "ExternalOauthJwsKeysUrl", "ExternalOauthBlockedRolesList", "ExternalOauthAllowedRolesList", "ExternalOauthRsaPublicKey", "ExternalOauthRsaPublicKey2", - "ExternalOauthAudienceList", "ExternalOauthAnyRoleMode", "ExternalOauthScopeDelimiter", "Comment") + "ExternalOauthAudienceList", "ExternalOauthAnyRoleMode", "ExternalOauthScopeDelimiter", "ExternalOauthScopeMappingAttribute", "Comment") var externalOauthIntegrationUnsetDef = g.NewQueryStruct("ExternalOauthIntegrationUnset"). OptionalSQL("ENABLED"). diff --git a/pkg/sdk/security_integrations_dto_builders_gen.go b/pkg/sdk/security_integrations_dto_builders_gen.go index 97efc1b065..9c8d7dade2 100644 --- a/pkg/sdk/security_integrations_dto_builders_gen.go +++ b/pkg/sdk/security_integrations_dto_builders_gen.go @@ -979,7 +979,12 @@ func (s *ExternalOauthIntegrationSetRequest) WithExternalOauthScopeDelimiter(Ext return s } -func (s *ExternalOauthIntegrationSetRequest) WithComment(Comment string) *ExternalOauthIntegrationSetRequest { +func (s *ExternalOauthIntegrationSetRequest) WithExternalOauthScopeMappingAttribute(ExternalOauthScopeMappingAttribute string) *ExternalOauthIntegrationSetRequest { + s.ExternalOauthScopeMappingAttribute = &ExternalOauthScopeMappingAttribute + return s +} + +func (s *ExternalOauthIntegrationSetRequest) WithComment(Comment StringAllowEmpty) *ExternalOauthIntegrationSetRequest { s.Comment = &Comment return s } diff --git a/pkg/sdk/security_integrations_dto_gen.go b/pkg/sdk/security_integrations_dto_gen.go index 7e427facb7..7726e8d8b8 100644 --- a/pkg/sdk/security_integrations_dto_gen.go +++ b/pkg/sdk/security_integrations_dto_gen.go @@ -301,7 +301,8 @@ type ExternalOauthIntegrationSetRequest struct { ExternalOauthAudienceList *AudienceListRequest ExternalOauthAnyRoleMode *ExternalOauthSecurityIntegrationAnyRoleModeOption ExternalOauthScopeDelimiter *string - Comment *string + ExternalOauthScopeMappingAttribute *string + Comment *StringAllowEmpty } type ExternalOauthIntegrationUnsetRequest struct { diff --git a/pkg/sdk/security_integrations_gen.go b/pkg/sdk/security_integrations_gen.go index 2e9ac0572c..71dda87a4d 100644 --- a/pkg/sdk/security_integrations_gen.go +++ b/pkg/sdk/security_integrations_gen.go @@ -347,7 +347,8 @@ type ExternalOauthIntegrationSet struct { ExternalOauthAudienceList *AudienceList `ddl:"parameter,parentheses" sql:"EXTERNAL_OAUTH_AUDIENCE_LIST"` ExternalOauthAnyRoleMode *ExternalOauthSecurityIntegrationAnyRoleModeOption `ddl:"parameter" sql:"EXTERNAL_OAUTH_ANY_ROLE_MODE"` ExternalOauthScopeDelimiter *string `ddl:"parameter,single_quotes" sql:"EXTERNAL_OAUTH_SCOPE_DELIMITER"` - Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` + ExternalOauthScopeMappingAttribute *string `ddl:"parameter,single_quotes" sql:"EXTERNAL_OAUTH_SCOPE_MAPPING_ATTRIBUTE"` + Comment *StringAllowEmpty `ddl:"parameter" sql:"COMMENT"` } type ExternalOauthIntegrationUnset struct { Enabled *bool `ddl:"keyword" sql:"ENABLED"` diff --git a/pkg/sdk/security_integrations_gen_test.go b/pkg/sdk/security_integrations_gen_test.go index 011076de3b..64de07e48d 100644 --- a/pkg/sdk/security_integrations_gen_test.go +++ b/pkg/sdk/security_integrations_gen_test.go @@ -777,7 +777,7 @@ func TestSecurityIntegrations_AlterExternalOauth(t *testing.T) { assertOptsInvalidJoinedErrors(t, opts, errAtLeastOneOf("AlterExternalOauthSecurityIntegrationOptions.Set", "Enabled", "ExternalOauthType", "ExternalOauthIssuer", "ExternalOauthTokenUserMappingClaim", "ExternalOauthSnowflakeUserMappingAttribute", "ExternalOauthJwsKeysUrl", "ExternalOauthBlockedRolesList", "ExternalOauthAllowedRolesList", "ExternalOauthRsaPublicKey", "ExternalOauthRsaPublicKey2", "ExternalOauthAudienceList", - "ExternalOauthAnyRoleMode", "ExternalOauthScopeDelimiter", "Comment")) + "ExternalOauthAnyRoleMode", "ExternalOauthScopeDelimiter", "ExternalOauthScopeMappingAttribute", "Comment")) }) t.Run("validation: at least one of the fields [opts.Unset.*] should be set", func(t *testing.T) { @@ -846,12 +846,13 @@ func TestSecurityIntegrations_AlterExternalOauth(t *testing.T) { ExternalOauthAudienceList: &AudienceList{AudienceList: []AudienceListItem{{Item: "foo"}}}, ExternalOauthAnyRoleMode: Pointer(ExternalOauthSecurityIntegrationAnyRoleModeDisable), ExternalOauthScopeDelimiter: Pointer(" "), - Comment: Pointer("foo"), + ExternalOauthScopeMappingAttribute: Pointer("foo"), + Comment: Pointer(StringAllowEmpty{Value: "foo"}), } assertOptsValidAndSQLEquals(t, opts, "ALTER SECURITY INTEGRATION %s SET ENABLED = true, EXTERNAL_OAUTH_TYPE = CUSTOM, EXTERNAL_OAUTH_ISSUER = 'foo',"+ " EXTERNAL_OAUTH_TOKEN_USER_MAPPING_CLAIM = ('foo'), EXTERNAL_OAUTH_SNOWFLAKE_USER_MAPPING_ATTRIBUTE = 'EMAIL_ADDRESS', EXTERNAL_OAUTH_ALLOWED_ROLES_LIST = (%s),"+ " EXTERNAL_OAUTH_RSA_PUBLIC_KEY = 'foo', EXTERNAL_OAUTH_RSA_PUBLIC_KEY_2 = 'foo', EXTERNAL_OAUTH_AUDIENCE_LIST = ('foo'), EXTERNAL_OAUTH_ANY_ROLE_MODE = DISABLE,"+ - " EXTERNAL_OAUTH_SCOPE_DELIMITER = ' ', COMMENT = 'foo'", id.FullyQualifiedName(), roleID.FullyQualifiedName()) + " EXTERNAL_OAUTH_SCOPE_DELIMITER = ' ', EXTERNAL_OAUTH_SCOPE_MAPPING_ATTRIBUTE = 'foo', COMMENT = 'foo'", id.FullyQualifiedName(), roleID.FullyQualifiedName()) opts.Set = &ExternalOauthIntegrationSet{ ExternalOauthBlockedRolesList: &BlockedRolesList{BlockedRolesList: []AccountObjectIdentifier{roleID}}, ExternalOauthJwsKeysUrl: []JwsKeysUrl{{JwsKeyUrl: "foo"}}, @@ -868,6 +869,14 @@ func TestSecurityIntegrations_AlterExternalOauth(t *testing.T) { assertOptsValidAndSQLEquals(t, opts, "ALTER SECURITY INTEGRATION %s UNSET ENABLED, EXTERNAL_OAUTH_AUDIENCE_LIST", id.FullyQualifiedName()) }) + t.Run("set empty comment", func(t *testing.T) { + opts := defaultOpts() + opts.Set = &ExternalOauthIntegrationSet{ + Comment: Pointer(StringAllowEmpty{Value: ""}), + } + assertOptsValidAndSQLEquals(t, opts, "ALTER SECURITY INTEGRATION %s SET COMMENT = ''", id.FullyQualifiedName()) + }) + t.Run("set tags", func(t *testing.T) { opts := defaultOpts() opts.SetTags = []TagAssociation{ diff --git a/pkg/sdk/security_integrations_impl_gen.go b/pkg/sdk/security_integrations_impl_gen.go index 45a3195d5e..089b8d1a5e 100644 --- a/pkg/sdk/security_integrations_impl_gen.go +++ b/pkg/sdk/security_integrations_impl_gen.go @@ -449,9 +449,10 @@ func (r *AlterExternalOauthSecurityIntegrationRequest) toOpts() *AlterExternalOa ExternalOauthRsaPublicKey: r.Set.ExternalOauthRsaPublicKey, ExternalOauthRsaPublicKey2: r.Set.ExternalOauthRsaPublicKey2, - ExternalOauthAnyRoleMode: r.Set.ExternalOauthAnyRoleMode, - ExternalOauthScopeDelimiter: r.Set.ExternalOauthScopeDelimiter, - Comment: r.Set.Comment, + ExternalOauthAnyRoleMode: r.Set.ExternalOauthAnyRoleMode, + ExternalOauthScopeDelimiter: r.Set.ExternalOauthScopeDelimiter, + ExternalOauthScopeMappingAttribute: r.Set.ExternalOauthScopeMappingAttribute, + Comment: r.Set.Comment, } if r.Set.ExternalOauthBlockedRolesList != nil { diff --git a/pkg/sdk/security_integrations_validations_gen.go b/pkg/sdk/security_integrations_validations_gen.go index 7c9b21606d..131ae527fb 100644 --- a/pkg/sdk/security_integrations_validations_gen.go +++ b/pkg/sdk/security_integrations_validations_gen.go @@ -239,8 +239,8 @@ func (opts *AlterExternalOauthSecurityIntegrationOptions) validate() error { if everyValueSet(opts.Set.ExternalOauthJwsKeysUrl, opts.Set.ExternalOauthRsaPublicKey2) { errs = append(errs, errOneOf("AlterExternalOauthSecurityIntegrationOptions.Set", "ExternalOauthJwsKeysUrl", "ExternalOauthRsaPublicKey2")) } - if !anyValueSet(opts.Set.Enabled, opts.Set.ExternalOauthType, opts.Set.ExternalOauthIssuer, opts.Set.ExternalOauthTokenUserMappingClaim, opts.Set.ExternalOauthSnowflakeUserMappingAttribute, opts.Set.ExternalOauthJwsKeysUrl, opts.Set.ExternalOauthBlockedRolesList, opts.Set.ExternalOauthAllowedRolesList, opts.Set.ExternalOauthRsaPublicKey, opts.Set.ExternalOauthRsaPublicKey2, opts.Set.ExternalOauthAudienceList, opts.Set.ExternalOauthAnyRoleMode, opts.Set.ExternalOauthScopeDelimiter, opts.Set.Comment) { - errs = append(errs, errAtLeastOneOf("AlterExternalOauthSecurityIntegrationOptions.Set", "Enabled", "ExternalOauthType", "ExternalOauthIssuer", "ExternalOauthTokenUserMappingClaim", "ExternalOauthSnowflakeUserMappingAttribute", "ExternalOauthJwsKeysUrl", "ExternalOauthBlockedRolesList", "ExternalOauthAllowedRolesList", "ExternalOauthRsaPublicKey", "ExternalOauthRsaPublicKey2", "ExternalOauthAudienceList", "ExternalOauthAnyRoleMode", "ExternalOauthScopeDelimiter", "Comment")) + if !anyValueSet(opts.Set.Enabled, opts.Set.ExternalOauthType, opts.Set.ExternalOauthIssuer, opts.Set.ExternalOauthTokenUserMappingClaim, opts.Set.ExternalOauthSnowflakeUserMappingAttribute, opts.Set.ExternalOauthJwsKeysUrl, opts.Set.ExternalOauthBlockedRolesList, opts.Set.ExternalOauthAllowedRolesList, opts.Set.ExternalOauthRsaPublicKey, opts.Set.ExternalOauthRsaPublicKey2, opts.Set.ExternalOauthAudienceList, opts.Set.ExternalOauthAnyRoleMode, opts.Set.ExternalOauthScopeDelimiter, opts.Set.ExternalOauthScopeMappingAttribute, opts.Set.Comment) { + errs = append(errs, errAtLeastOneOf("AlterExternalOauthSecurityIntegrationOptions.Set", "Enabled", "ExternalOauthType", "ExternalOauthIssuer", "ExternalOauthTokenUserMappingClaim", "ExternalOauthSnowflakeUserMappingAttribute", "ExternalOauthJwsKeysUrl", "ExternalOauthBlockedRolesList", "ExternalOauthAllowedRolesList", "ExternalOauthRsaPublicKey", "ExternalOauthRsaPublicKey2", "ExternalOauthAudienceList", "ExternalOauthAnyRoleMode", "ExternalOauthScopeDelimiter", "ExternalOauthScopeMappingAttribute", "Comment")) } } if valueSet(opts.Unset) { diff --git a/pkg/sdk/testint/security_integrations_gen_integration_test.go b/pkg/sdk/testint/security_integrations_gen_integration_test.go index f4edb6ba11..817729bad5 100644 --- a/pkg/sdk/testint/security_integrations_gen_integration_test.go +++ b/pkg/sdk/testint/security_integrations_gen_integration_test.go @@ -876,7 +876,7 @@ func TestInt_SecurityIntegrations(t *testing.T) { WithExternalOauthAudienceList(sdk.AudienceListRequest{AudienceList: []sdk.AudienceListItem{{Item: "foo"}}}). WithExternalOauthAnyRoleMode(sdk.ExternalOauthSecurityIntegrationAnyRoleModeDisable). WithExternalOauthScopeDelimiter(" "). - WithComment("foo"), + WithComment(sdk.StringAllowEmpty{Value: "foo"}), ) err := client.SecurityIntegrations.AlterExternalOauth(ctx, setRequest) require.NoError(t, err) diff --git a/templates/resources/external_oauth_integration.md.tmpl b/templates/resources/external_oauth_integration.md.tmpl new file mode 100644 index 0000000000..e7f66bbf91 --- /dev/null +++ b/templates/resources/external_oauth_integration.md.tmpl @@ -0,0 +1,32 @@ +--- +page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}" +subcategory: "" +description: |- +{{ if gt (len (split .Description "")) 1 -}} +{{ index (split .Description "") 1 | plainmarkdown | trimspace | prefixlines " " }} +{{- else -}} +{{ .Description | plainmarkdown | trimspace | prefixlines " " }} +{{- end }} +--- + +!> **V1 release candidate** This resource was reworked and is a release candidate for the V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the resource if needed. Any errors reported will be resolved with a higher priority. We encourage checking this resource out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0920--v0930) to use it. + +# {{.Name}} ({{.Type}}) + +{{ .Description | trimspace }} + +{{ if .HasExample -}} +## Example Usage + +{{ tffile (printf "examples/resources/%s/resource.tf" .Name)}} +{{- end }} + +{{ .SchemaMarkdown | trimspace }} +{{- if .HasImport }} + +## Import + +Import is supported using the following syntax: + +{{ codefile "shell" (printf "examples/resources/%s/import.sh" .Name)}} +{{- end }}