diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md index 04e76a9423..d968d13203 100644 --- a/MIGRATION_GUIDE.md +++ b/MIGRATION_GUIDE.md @@ -7,6 +7,13 @@ across different versions. > [!TIP] > We highly recommend upgrading the versions one by one instead of bulk upgrades. +## v1.0.4 ➞ v1.0.5 + +### Tracking external changes for oauth_redirect_uri in the snowflake_oauth_integration_for_partner_applications resource +From this version, the snowflake_oauth_integration_for_partner_applications resource is able to +detect changes on the Snowflake side and apply appropriate action from the provider level. This may produce +changes after running `terraform plan`, as before the configuration could contain different value than on the Snowflake side. + ## v1.0.3 ➞ v1.0.4 ### Fixed external_function VARCHAR return_type diff --git a/docs/resources/oauth_integration_for_partner_applications.md b/docs/resources/oauth_integration_for_partner_applications.md index c2c73167c0..d230227921 100644 --- a/docs/resources/oauth_integration_for_partner_applications.md +++ b/docs/resources/oauth_integration_for_partner_applications.md @@ -50,7 +50,7 @@ resource "snowflake_oauth_integration_for_partner_applications" "test" { - `comment` (String) Specifies a comment for the OAuth integration. - `enabled` (String) Specifies whether this OAuth integration is enabled or disabled. Available options are: "true" or "false". When the value is not set in the configuration the provider will put "default" there which means to use the Snowflake default for this value. - `oauth_issue_refresh_tokens` (String) Specifies whether to allow the client to exchange a refresh token for an access token when the current access token has expired. Available options are: "true" or "false". When the value is not set in the configuration the provider will put "default" there which means to use the Snowflake default for this value. -- `oauth_redirect_uri` (String) Specifies the client URI. After a user is authenticated, the web browser is redirected to this URI. The field should be only set when OAUTH_CLIENT = LOOKER. In any other case the field should be left out empty. 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". +- `oauth_redirect_uri` (String) Specifies the client URI. After a user is authenticated, the web browser is redirected to this URI. The field should be only set when OAUTH_CLIENT = LOOKER. In any other case the field should be left out empty. - `oauth_refresh_token_validity` (Number) Specifies how long refresh tokens should be valid (in seconds). OAUTH_ISSUE_REFRESH_TOKENS must be set to TRUE. - `oauth_use_secondary_roles` (String) Specifies whether default secondary roles set in the user properties are activated by default in the session being opened. Valid options are: `IMPLICIT` | `NONE`. diff --git a/pkg/acceptance/bettertestspoc/config/model/account_model_gen.go b/pkg/acceptance/bettertestspoc/config/model/account_model_gen.go index 88cac4abc3..258096de6b 100644 --- a/pkg/acceptance/bettertestspoc/config/model/account_model_gen.go +++ b/pkg/acceptance/bettertestspoc/config/model/account_model_gen.go @@ -104,7 +104,7 @@ func (a *AccountModel) WithAdminPassword(adminPassword string) *AccountModel { } func (a *AccountModel) WithAdminRsaPublicKey(adminRsaPublicKey string) *AccountModel { - a.AdminRsaPublicKey = tfconfig.StringVariable(adminRsaPublicKey) + a.AdminRsaPublicKey = config.MultilineWrapperVariable(adminRsaPublicKey) return a } diff --git a/pkg/acceptance/bettertestspoc/config/model/gen/multiline_attributes_overrides.go b/pkg/acceptance/bettertestspoc/config/model/gen/multiline_attributes_overrides.go index 1faa489bba..a3f188f6ee 100644 --- a/pkg/acceptance/bettertestspoc/config/model/gen/multiline_attributes_overrides.go +++ b/pkg/acceptance/bettertestspoc/config/model/gen/multiline_attributes_overrides.go @@ -14,4 +14,5 @@ var multilineAttributesOverrides = map[string][]string{ "ProcedurePython": {"procedure_definition"}, "ProcedureScala": {"procedure_definition"}, "ProcedureSql": {"procedure_definition"}, + "Account": {"admin_rsa_public_key"}, } diff --git a/pkg/resources/oauth_integration_for_partner_applications.go b/pkg/resources/oauth_integration_for_partner_applications.go index 0074b3a3f5..edf5f4eff6 100644 --- a/pkg/resources/oauth_integration_for_partner_applications.go +++ b/pkg/resources/oauth_integration_for_partner_applications.go @@ -41,7 +41,7 @@ var oauthIntegrationForPartnerApplicationsSchema = map[string]*schema.Schema{ "oauth_redirect_uri": { Type: schema.TypeString, Optional: true, - Description: externalChangesNotDetectedFieldDescription("Specifies the client URI. After a user is authenticated, the web browser is redirected to this URI. The field should be only set when OAUTH_CLIENT = LOOKER. In any other case the field should be left out empty."), + Description: "Specifies the client URI. After a user is authenticated, the web browser is redirected to this URI. The field should be only set when OAUTH_CLIENT = LOOKER. In any other case the field should be left out empty.", }, "enabled": { Type: schema.TypeString, @@ -143,6 +143,7 @@ func OauthIntegrationForPartnerApplications() *schema.Resource { oauthIntegrationForPartnerApplicationsSchema, DescribeOutputAttributeName, "oauth_client", + "oauth_redirect_uri", "enabled", "oauth_issue_refresh_tokens", "oauth_refresh_token_validity", @@ -192,6 +193,14 @@ func ImportOauthForPartnerApplicationIntegration(ctx context.Context, d *schema. } } + if oauthRedirectUri, err := collections.FindFirst(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "OAUTH_REDIRECT_URI" + }); err == nil { + if err = d.Set("oauth_redirect_uri", oauthRedirectUri.Value); err != nil { + return nil, err + } + } + if refreshTokenValidity, err := collections.FindFirst(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { return property.Name == "OAUTH_REFRESH_TOKEN_VALIDITY" }); err == nil { @@ -381,11 +390,20 @@ func ReadContextOauthIntegrationForPartnerApplications(withExternalChangesMarkin return diag.FromErr(err) } + // This has to be handled differently as OAUTH_REDIRECT_URI is only visible for a given OAUTH_CLIENT type. + var oauthRedirectUri string + if oauthRedirectUriProp, err := collections.FindFirst(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { + return property.Name == "OAUTH_REDIRECT_URI" + }); err == nil { + oauthRedirectUri = oauthRedirectUriProp.Value + } + if err = handleExternalChangesToObjectInDescribe(d, describeMapping{"oauth_issue_refresh_tokens", "oauth_issue_refresh_tokens", oauthIssueRefreshTokens.Value, oauthIssueRefreshTokens.Value, nil}, describeMapping{"oauth_refresh_token_validity", "oauth_refresh_token_validity", oauthRefreshTokenValidity.Value, oauthRefreshTokenValidityValue, nil}, describeMapping{"oauth_use_secondary_roles", "oauth_use_secondary_roles", oauthUseSecondaryRoles.Value, oauthUseSecondaryRoles.Value, nil}, describeMapping{"blocked_roles_list", "blocked_roles_list", blockedRolesList.Value, sdk.ParseCommaSeparatedStringArray(blockedRolesList.Value, false), nil}, + describeMapping{"oauth_redirect_uri", "oauth_redirect_uri", oauthRedirectUri, oauthRedirectUri, nil}, ); err != nil { return diag.FromErr(err) } @@ -397,6 +415,7 @@ func ReadContextOauthIntegrationForPartnerApplications(withExternalChangesMarkin "oauth_refresh_token_validity", "oauth_use_secondary_roles", "blocked_roles_list", + "oauth_redirect_uri", }); err != nil { return diag.FromErr(err) } diff --git a/pkg/resources/oauth_integration_for_partner_applications_acceptance_test.go b/pkg/resources/oauth_integration_for_partner_applications_acceptance_test.go index cc251e8f86..f0e69f85f1 100644 --- a/pkg/resources/oauth_integration_for_partner_applications_acceptance_test.go +++ b/pkg/resources/oauth_integration_for_partner_applications_acceptance_test.go @@ -6,6 +6,9 @@ import ( "strings" "testing" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceassert" + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" accconfig "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config" resourcehelpers "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers" @@ -79,7 +82,7 @@ func TestAcc_OauthIntegrationForPartnerApplications_Basic(t *testing.T) { resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "describe_output.#", "1"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "describe_output.0.oauth_client_type.0.value", string(sdk.OauthSecurityIntegrationClientTypeConfidential)), - resource.TestCheckNoResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "describe_output.0.oauth_redirect_uri.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "describe_output.0.oauth_redirect_uri.0.value"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "describe_output.0.enabled.0.value", "false"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "describe_output.0.oauth_use_secondary_roles.0.value", "NONE"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "describe_output.0.blocked_roles_list.0.value", "ACCOUNTADMIN,SECURITYADMIN"), @@ -140,7 +143,7 @@ func TestAcc_OauthIntegrationForPartnerApplications_Basic(t *testing.T) { resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "describe_output.#", "1"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "describe_output.0.oauth_client_type.0.value", string(sdk.OauthSecurityIntegrationClientTypeConfidential)), - resource.TestCheckNoResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "describe_output.0.oauth_redirect_uri.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "describe_output.0.oauth_redirect_uri.0.value"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "describe_output.0.enabled.0.value", "true"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "describe_output.0.oauth_use_secondary_roles.0.value", "IMPLICIT"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "describe_output.0.blocked_roles_list.0.value", "ACCOUNTADMIN,SECURITYADMIN"), @@ -227,7 +230,7 @@ func TestAcc_OauthIntegrationForPartnerApplications_Basic(t *testing.T) { resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "describe_output.#", "1"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "describe_output.0.oauth_client_type.0.value", string(sdk.OauthSecurityIntegrationClientTypeConfidential)), - resource.TestCheckNoResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "describe_output.0.oauth_redirect_uri.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "describe_output.0.oauth_redirect_uri.0.value"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "describe_output.0.enabled.0.value", "true"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "describe_output.0.oauth_use_secondary_roles.0.value", "IMPLICIT"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "describe_output.0.blocked_roles_list.0.value", "ACCOUNTADMIN,SECURITYADMIN"), @@ -271,7 +274,7 @@ func TestAcc_OauthIntegrationForPartnerApplications_Basic(t *testing.T) { resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "describe_output.#", "1"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "describe_output.0.oauth_client_type.0.value", string(sdk.OauthSecurityIntegrationClientTypeConfidential)), - resource.TestCheckNoResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "describe_output.0.oauth_redirect_uri.0.value"), + resource.TestCheckResourceAttrSet("snowflake_oauth_integration_for_partner_applications.test", "describe_output.0.oauth_redirect_uri.0.value"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "describe_output.0.enabled.0.value", "false"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "describe_output.0.oauth_use_secondary_roles.0.value", "NONE"), resource.TestCheckResourceAttr("snowflake_oauth_integration_for_partner_applications.test", "describe_output.0.blocked_roles_list.0.value", "ACCOUNTADMIN,SECURITYADMIN"), @@ -839,3 +842,49 @@ func TestAcc_OauthIntegrationForPartnerApplications_WithPrivilegedRolesBlockedLi }, }) } + +func TestAcc_OauthIntegrationForPartnerApplications_DetectExternalChangesForOauthRedirectUri(t *testing.T) { + id := acc.TestClient().Ids.RandomAccountObjectIdentifier() + oauthRedirectUri := "https://example.com" + otherOauthRedirectUri := "https://example2.com" + configModel := model.OauthIntegrationForPartnerApplications( + "test", + id.Name(), + string(sdk.OauthSecurityIntegrationClientLooker), + ).WithOauthRedirectUri(oauthRedirectUri) + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories, + PreCheck: func() { acc.TestAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(tfversion.Version1_5_0), + }, + CheckDestroy: acc.CheckDestroy(t, resources.OauthIntegrationForPartnerApplications), + Steps: []resource.TestStep{ + { + Config: accconfig.FromModels(t, configModel), + Check: assert.AssertThat(t, + resourceassert.OauthIntegrationForPartnerApplicationsResource(t, configModel.ResourceReference()). + HasOauthRedirectUriString(oauthRedirectUri), + ), + }, + { + PreConfig: func() { + acc.TestClient().SecurityIntegration.UpdateOauthForPartnerApplications(t, sdk.NewAlterOauthForPartnerApplicationsSecurityIntegrationRequest(id).WithSet( + *sdk.NewOauthForPartnerApplicationsIntegrationSetRequest().WithOauthRedirectUri(otherOauthRedirectUri), + )) + }, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectResourceAction(configModel.ResourceReference(), plancheck.ResourceActionUpdate), + }, + }, + Config: accconfig.FromModels(t, configModel), + Check: assert.AssertThat(t, + resourceassert.OauthIntegrationForPartnerApplicationsResource(t, configModel.ResourceReference()). + HasOauthRedirectUriString(oauthRedirectUri), + ), + }, + }, + }) +} diff --git a/pkg/sdk/testint/authentication_policies_gen_integration_test.go b/pkg/sdk/testint/authentication_policies_gen_integration_test.go index 6de2607031..cbaeeca28a 100644 --- a/pkg/sdk/testint/authentication_policies_gen_integration_test.go +++ b/pkg/sdk/testint/authentication_policies_gen_integration_test.go @@ -2,6 +2,7 @@ package testint import ( "fmt" + "strings" "testing" assertions "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" @@ -223,13 +224,12 @@ func TestInt_AuthenticationPolicies(t *testing.T) { assert.Len(t, authenticationPolicies, 1) }) - // TODO(SNOW-1663343): starts_with doesn't work (returns all) t.Run("starts_with", func(t *testing.T) { authenticationPolicies, err := client.AuthenticationPolicies.Show(ctx, sdk.NewShowAuthenticationPolicyRequest(). WithStartsWith("test_auth_policy_"). WithIn(sdk.In{Schema: id.SchemaId()})) require.NoError(t, err) - assert.Len(t, authenticationPolicies, 3) + assert.Len(t, authenticationPolicies, 2) }) t.Run("in_account", func(t *testing.T) { @@ -258,13 +258,13 @@ func TestInt_AuthenticationPolicies(t *testing.T) { assert.Len(t, authenticationPolicies, 1) }) - // TODO(SNOW-1663343): limit from doesn't work (should return 0 elements because alphabetically test_auth_policyzzz is last in the output) t.Run("limit from", func(t *testing.T) { authenticationPolicies, err := client.AuthenticationPolicies.Show(ctx, sdk.NewShowAuthenticationPolicyRequest(). - WithLimit(sdk.LimitFrom{Rows: sdk.Int(2), From: sdk.String(id.Name())}). + WithLimit(sdk.LimitFrom{Rows: sdk.Int(1), From: sdk.String("test_auth_policy_")}). WithIn(sdk.In{Schema: id.SchemaId()})) require.NoError(t, err) - assert.Len(t, authenticationPolicies, 2) + require.Len(t, authenticationPolicies, 1) + require.True(t, strings.HasPrefix(authenticationPolicies[0].Name, "test_auth_policy_2")) }) })