diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md
index 122d97ec8c..5b9f3c9171 100644
--- a/MIGRATION_GUIDE.md
+++ b/MIGRATION_GUIDE.md
@@ -9,6 +9,10 @@ across different versions.
Following the [announcement](https://github.com/Snowflake-Labs/terraform-provider-snowflake/discussions/2736) we have removed the old grant resources. The two resources [snowflake_role_ownership_grant](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/role_ownership_grant) and [snowflake_user_ownership_grant](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/resources/user_ownership_grant) were not listed in the announcement, but they were also marked as deprecated ones. We are removing them too to conclude the grants redesign saga.
### snowflake_scim_integration resource changes
+#### *(behavior change)* Changed behavior of `sync_password`
+
+Now, the `sync_password` field will set the state value to `unknown` whenever the value is not set in the config. This indicates that the value on the Snowflake side is set to the Snowflake default.
+
#### *(behavior change)* Renamed fields
Renamed field `provisioner_role` to `run_as_role` to align with Snowflake docs. Please rename this field in your configuration files. State will be migrated automatically.
diff --git a/docs/resources/scim_integration.md b/docs/resources/scim_integration.md
index f9d68cbb94..819e1a2cda 100644
--- a/docs/resources/scim_integration.md
+++ b/docs/resources/scim_integration.md
@@ -45,12 +45,92 @@ resource "snowflake_scim_integration" "test" {
- `comment` (String) Specifies a comment for the integration.
- `network_policy` (String) Specifies an existing network policy that controls SCIM network traffic.
-- `sync_password` (Boolean) Specifies whether to enable or disable the synchronization of a user password from an Okta SCIM client as part of the API request to Snowflake.
+- `sync_password` (String) Specifies whether to enable or disable the synchronization of a user password from an Okta SCIM client as part of the API request to Snowflake. Available options are: `true` or `false`. When the value is not set in the configuration the provider will put `unknown` there which means to use the Snowflake default for this value.
### Read-Only
-- `created_on` (String) Date and time when the SCIM 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.
+- `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))
+- `network_policy` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--network_policy))
+- `run_as_role` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--run_as_role))
+- `sync_password` (List of Object) (see [below for nested schema](#nestedobjatt--describe_output--sync_password))
+
+
+### 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.network_policy`
+
+Read-Only:
+
+- `default` (String)
+- `name` (String)
+- `type` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `describe_output.run_as_role`
+
+Read-Only:
+
+- `default` (String)
+- `name` (String)
+- `type` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `describe_output.sync_password`
+
+Read-Only:
+
+- `default` (String)
+- `name` (String)
+- `type` (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
diff --git a/pkg/resources/custom_diffs.go b/pkg/resources/custom_diffs.go
index c3e417e523..14382da0af 100644
--- a/pkg/resources/custom_diffs.go
+++ b/pkg/resources/custom_diffs.go
@@ -9,8 +9,6 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
- "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers"
- "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider"
"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
@@ -61,24 +59,6 @@ func ParameterValueComputedIf(key string, parameters []*sdk.Parameter, objectPar
}
}
-func BoolComputedIf(key string, getDefault func(client *sdk.Client, id sdk.AccountObjectIdentifier) (string, error)) schema.CustomizeDiffFunc {
- return customdiff.ComputedIf(key, func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) bool {
- configValue := d.GetRawConfig().AsValueMap()[key]
- if !configValue.IsNull() {
- return false
- }
-
- client := meta.(*provider.Context).Client
-
- def, err := getDefault(client, helpers.DecodeSnowflakeID(d.Id()).(sdk.AccountObjectIdentifier))
- if err != nil {
- return false
- }
- stateValue := d.Get(key).(bool)
- return def != strconv.FormatBool(stateValue)
- })
-}
-
// TODO [follow-up PR]: test
func ComputedIfAnyAttributeChanged(key string, changedAttributeKeys ...string) schema.CustomizeDiffFunc {
return customdiff.ComputedIf(key, func(ctx context.Context, diff *schema.ResourceDiff, meta interface{}) bool {
diff --git a/pkg/resources/diff_suppressions.go b/pkg/resources/diff_suppressions.go
index d2de195517..59edce5c83 100644
--- a/pkg/resources/diff_suppressions.go
+++ b/pkg/resources/diff_suppressions.go
@@ -27,17 +27,17 @@ func IgnoreAfterCreation(_, _, _ string, d *schema.ResourceData) bool {
return d.Id() != ""
}
-func IgnoreChangeToCurrentSnowflakeValue(keyInShowOutput string) schema.SchemaDiffSuppressFunc {
+func IgnoreChangeToCurrentSnowflakeValueInShow(keyInShowOutput string) schema.SchemaDiffSuppressFunc {
return func(_, _, new string, d *schema.ResourceData) bool {
if d.Id() == "" {
return false
}
- if showOutput, ok := d.GetOk(showOutputAttributeName); ok {
- showOutputList := showOutput.([]any)
- if len(showOutputList) == 1 {
- result := showOutputList[0].(map[string]any)
- log.Printf("[DEBUG] IgnoreChangeToCurrentSnowflakeValue: value for key %s is %v, new value is %s, comparison result is: %t", keyInShowOutput, result[keyInShowOutput], new, new == fmt.Sprintf("%v", result[keyInShowOutput]))
+ if queryOutput, ok := d.GetOk(showOutputAttributeName); ok {
+ queryOutputList := queryOutput.([]any)
+ if len(queryOutputList) == 1 {
+ result := queryOutputList[0].(map[string]any)
+ log.Printf("[DEBUG] IgnoreChangeToCurrentSnowflakeValueInShow: value for key %s is %v, new value is %s, comparison result is: %t", keyInShowOutput, result[keyInShowOutput], new, new == fmt.Sprintf("%v", result[keyInShowOutput]))
if new == fmt.Sprintf("%v", result[keyInShowOutput]) {
return true
}
@@ -47,6 +47,30 @@ func IgnoreChangeToCurrentSnowflakeValue(keyInShowOutput string) schema.SchemaDi
}
}
+func IgnoreChangeToCurrentSnowflakeValueInDescribe(keyInDescribeOutput string) schema.SchemaDiffSuppressFunc {
+ return func(_, _, new string, d *schema.ResourceData) bool {
+ if d.Id() == "" {
+ return false
+ }
+
+ if queryOutput, ok := d.GetOk(describeOutputAttributeName); ok {
+ queryOutputList := queryOutput.([]any)
+ if len(queryOutputList) == 1 {
+ result := queryOutputList[0].(map[string]any)
+ newValueInDescribeList := result[keyInDescribeOutput].([]any)
+ if len(newValueInDescribeList) == 1 {
+ newValueInDescribe := newValueInDescribeList[0].(map[string]any)["value"]
+ log.Printf("[DEBUG] IgnoreChangeToCurrentSnowflakeValueInDescribe: value for key %s is %v, new value is %s, comparison result is: %t", keyInDescribeOutput, newValueInDescribe, new, new == fmt.Sprintf("%v", newValueInDescribe))
+ if new == fmt.Sprintf("%v", newValueInDescribe) {
+ return true
+ }
+ }
+ }
+ }
+ return false
+ }
+}
+
func SuppressIfAny(diffSuppressFunctions ...schema.SchemaDiffSuppressFunc) schema.SchemaDiffSuppressFunc {
return func(k, old, new string, d *schema.ResourceData) bool {
var suppress bool
diff --git a/pkg/resources/scim_integration.go b/pkg/resources/scim_integration.go
index b6d83b3ece..4bfacb3d1f 100644
--- a/pkg/resources/scim_integration.go
+++ b/pkg/resources/scim_integration.go
@@ -4,16 +4,20 @@ import (
"context"
"errors"
"fmt"
- "log"
+ "strconv"
"strings"
+ "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/schemas"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
+
"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers"
"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider"
"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
"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"
)
@@ -26,12 +30,10 @@ var scimIntegrationSchema = map[string]*schema.Schema{
Description: "String that specifies the identifier (i.e. name) for the integration; must be unique in your account.",
},
"enabled": {
- Type: schema.TypeBool,
- Required: true,
- Description: "Specify whether the security integration is enabled. ",
- DiffSuppressFunc: func(k, oldValue, newValue string, d *schema.ResourceData) bool {
- return d.GetRawConfig().AsValueMap()["enabled"].IsNull()
- },
+ Type: schema.TypeBool,
+ Required: true,
+ Description: "Specify whether the security integration is enabled.",
+ DiffSuppressFunc: IgnoreChangeToCurrentSnowflakeValueInDescribe("enabled"),
},
"scim_client": {
Type: schema.TypeString,
@@ -60,25 +62,36 @@ var scimIntegrationSchema = map[string]*schema.Schema{
ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](),
Optional: true,
Description: "Specifies an existing network policy that controls SCIM network traffic.",
- DiffSuppressFunc: func(_, old, new string, d *schema.ResourceData) bool {
- return sdk.NewAccountObjectIdentifierFromFullyQualifiedName(old) == sdk.NewAccountObjectIdentifierFromFullyQualifiedName(new)
- },
+ DiffSuppressFunc: SuppressIfAny(suppressIdentifierQuoting, IgnoreChangeToCurrentSnowflakeValueInDescribe("network_policy")),
},
"sync_password": {
- Type: schema.TypeBool,
- Optional: true,
- Computed: true,
- Description: "Specifies whether to enable or disable the synchronization of a user password from an Okta SCIM client as part of the API request to Snowflake.",
+ Type: schema.TypeString,
+ Optional: true,
+ Default: "unknown",
+ ValidateFunc: validation.StringInSlice([]string{"true", "false"}, true),
+ DiffSuppressFunc: IgnoreChangeToCurrentSnowflakeValueInDescribe("sync_password"),
+ Description: "Specifies whether to enable or disable the synchronization of a user password from an Okta SCIM client as part of the API request to Snowflake. Available options are: `true` or `false`. When the value is not set in the configuration the provider will put `unknown` there which means to use the Snowflake default for this value.",
},
"comment": {
Type: schema.TypeString,
Optional: true,
Description: "Specifies a comment for the integration.",
},
- "created_on": {
- Type: schema.TypeString,
+ showOutputAttributeName: {
+ Type: schema.TypeList,
Computed: true,
- Description: "Date and time when the SCIM 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.DescribeScimSecurityIntegrationSchema,
+ },
},
}
@@ -87,27 +100,20 @@ func SCIMIntegration() *schema.Resource {
SchemaVersion: 1,
CreateContext: CreateContextSCIMIntegration,
- ReadContext: ReadContextSCIMIntegration,
+ ReadContext: ReadContextSCIMIntegration(true),
UpdateContext: UpdateContextSCIMIntegration,
DeleteContext: DeleteContextSCIMIntegration,
- CustomizeDiff: customdiff.All(
- BoolComputedIf("sync_password", func(client *sdk.Client, id sdk.AccountObjectIdentifier) (string, error) {
- props, err := client.SecurityIntegrations.Describe(context.Background(), id)
- if err != nil {
- return "", err
- }
- for _, prop := range props {
- if prop.GetName() == "SYNC_PASSWORD" {
- return prop.GetDefault(), nil
- }
- }
- return "", fmt.Errorf("")
- }),
- ),
+
Schema: scimIntegrationSchema,
Importer: &schema.ResourceImporter{
- StateContext: schema.ImportStatePassthroughContext,
+ StateContext: ImportScimIntegration,
},
+
+ CustomizeDiff: customdiff.All(
+ ComputedIfAnyAttributeChanged(showOutputAttributeName, "enabled", "scim_client", "comment"),
+ ComputedIfAnyAttributeChanged(describeOutputAttributeName, "enabled", "comment", "network_policy", "run_as_role", "sync_password"),
+ ),
+
StateUpgraders: []schema.StateUpgrader{
{
Version: 0,
@@ -119,152 +125,266 @@ func SCIMIntegration() *schema.Resource {
}
}
+func ImportScimIntegration(ctx context.Context, d *schema.ResourceData, meta any) ([]*schema.ResourceData, error) {
+ logging.DebugLogger.Printf("[DEBUG] Starting scim integration import")
+ client := meta.(*provider.Context).Client
+ id := helpers.DecodeSnowflakeID(d.Id()).(sdk.AccountObjectIdentifier)
+
+ integration, err := client.SecurityIntegrations.ShowByID(ctx, id)
+ if err != nil {
+ return nil, err
+ }
+
+ integrationProperties, err := client.SecurityIntegrations.Describe(ctx, id)
+ if err != nil {
+ return nil, err
+ }
+
+ if err = d.Set("name", integration.Name); err != nil {
+ return nil, err
+ }
+ if err = d.Set("enabled", integration.Enabled); err != nil {
+ return nil, err
+ }
+ if scimClient, err := integration.SubType(); err == nil {
+ if err = d.Set("scim_client", scimClient); err != nil {
+ return nil, err
+ }
+ }
+ if runAsRoleProperty, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { return property.Name == "RUN_AS_ROLE" }); err == nil {
+ if err = d.Set("run_as_role", runAsRoleProperty.Value); err != nil {
+ return nil, err
+ }
+ }
+ if networkPolicyProperty, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { return property.Name == "NETWORK_POLICY" }); err == nil {
+ if err = d.Set("network_policy", networkPolicyProperty.Value); err != nil {
+ return nil, err
+ }
+ }
+ if syncPasswordProperty, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { return property.Name == "SYNC_PASSWORD" }); err == nil {
+ if err = d.Set("sync_password", syncPasswordProperty.Value); err != nil {
+ return nil, err
+ }
+ }
+ if err = d.Set("comment", integration.Comment); err != nil {
+ return nil, err
+ }
+
+ return []*schema.ResourceData{d}, nil
+}
+
func CreateContextSCIMIntegration(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
client := meta.(*provider.Context).Client
- name := d.Get("name").(string)
- id := sdk.NewAccountObjectIdentifier(name)
- scimClientRaw := d.Get("scim_client").(string)
- runAsRoleRaw := d.Get("run_as_role").(string)
- scimClient, err := sdk.ToScimSecurityIntegrationScimClientOption(scimClientRaw)
+
+ id := sdk.NewAccountObjectIdentifier(d.Get("name").(string))
+
+ scimClient, err := sdk.ToScimSecurityIntegrationScimClientOption(d.Get("scim_client").(string))
if err != nil {
return diag.FromErr(err)
}
- runAsRole, err := sdk.ToScimSecurityIntegrationRunAsRoleOption(runAsRoleRaw)
+
+ runAsRole, err := sdk.ToScimSecurityIntegrationRunAsRoleOption(d.Get("run_as_role").(string))
if err != nil {
return diag.FromErr(err)
}
+
req := sdk.NewCreateScimSecurityIntegrationRequest(id, scimClient, runAsRole).WithEnabled(d.Get("enabled").(bool))
if v, ok := d.GetOk("network_policy"); ok {
req.WithNetworkPolicy(sdk.NewAccountObjectIdentifier(v.(string)))
}
- if !d.GetRawConfig().AsValueMap()["sync_password"].IsNull() {
- req.WithSyncPassword(d.Get("sync_password").(bool))
+
+ if v := d.Get("sync_password").(string); v != "unknown" {
+ parsed, err := strconv.ParseBool(v)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ req.WithSyncPassword(parsed)
}
+
if v, ok := d.GetOk("comment"); ok {
req.WithComment(v.(string))
}
+
if err := client.SecurityIntegrations.CreateScim(ctx, req); err != nil {
return diag.FromErr(err)
}
d.SetId(helpers.EncodeSnowflakeID(id))
- return ReadContextSCIMIntegration(ctx, d, meta)
+ return ReadContextSCIMIntegration(false)(ctx, d, meta)
}
-func ReadContextSCIMIntegration(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
- client := meta.(*provider.Context).Client
- id := helpers.DecodeSnowflakeID(d.Id()).(sdk.AccountObjectIdentifier)
+func ReadContextSCIMIntegration(withExternalChangesMarking bool) schema.ReadContextFunc {
+ return func(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
+ client := meta.(*provider.Context).Client
+ id := helpers.DecodeSnowflakeID(d.Id()).(sdk.AccountObjectIdentifier)
- 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),
- },
+ 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)
}
- return diag.FromErr(err)
- }
- if c := integration.Category; c != sdk.SecurityIntegrationCategory {
- return diag.FromErr(fmt.Errorf("expected %v to be a SECURITY integration, got %v", id, c))
- }
+ integrationProperties, err := client.SecurityIntegrations.Describe(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 properties. Marking the resource as removed.",
+ Detail: fmt.Sprintf("Security integration name: %s, Err: %s", id.FullyQualifiedName(), err),
+ },
+ }
+ }
+ return diag.FromErr(err)
+ }
- if err := d.Set("name", integration.Name); err != nil {
- return diag.FromErr(err)
- }
+ if c := integration.Category; c != sdk.SecurityIntegrationCategory {
+ return diag.FromErr(fmt.Errorf("expected %v to be a SECURITY integration, got %v", id, c))
+ }
- if err := d.Set("comment", integration.Comment); err != nil {
- return diag.FromErr(err)
- }
+ if err := d.Set("name", integration.Name); err != nil {
+ return diag.FromErr(err)
+ }
- if err := d.Set("created_on", integration.CreatedOn.String()); err != nil {
- return diag.FromErr(err)
- }
+ if err := d.Set("enabled", integration.Enabled); err != nil {
+ return diag.FromErr(err)
+ }
- if err := d.Set("enabled", integration.Enabled); err != nil {
- return diag.FromErr(err)
- }
+ scimClient, err := integration.SubType()
+ if err != nil {
+ return diag.FromErr(err)
+ }
- scimClient, err := integration.SubType()
- if err != nil {
- return diag.FromErr(err)
- }
- if err := d.Set("scim_client", scimClient); err != nil {
- return diag.FromErr(err)
- }
- integrationProperties, err := client.SecurityIntegrations.Describe(ctx, id)
- if err != nil {
- return diag.FromErr(err)
- }
- for _, property := range integrationProperties {
- name := property.Name
- value := property.Value
- switch name {
- case "ENABLED", "COMMENT":
- // We set this using the SHOW INTEGRATION call so let's ignore it here
- case "NETWORK_POLICY":
- networkPolicyID := sdk.NewAccountObjectIdentifier(value)
- if err := d.Set("network_policy", networkPolicyID.FullyQualifiedName()); err != nil {
+ if err := d.Set("scim_client", scimClient); err != nil {
+ return diag.FromErr(err)
+ }
+
+ runAsRoleProperty, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { return property.Name == "RUN_AS_ROLE" })
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ if err := d.Set("run_as_role", runAsRoleProperty.Value); err != nil {
+ return diag.FromErr(err)
+ }
+
+ if withExternalChangesMarking {
+ if err = handleExternalChangesToObjectInShow(d,
+ showMapping{"comment", "comment", integration.Comment, integration.Comment, nil},
+ ); err != nil {
return diag.FromErr(err)
}
- case "SYNC_PASSWORD":
- if err := d.Set("sync_password", helpers.StringToBool(value)); err != nil {
+
+ networkPolicyProperty, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { return property.Name == "NETWORK_POLICY" })
+ if err != nil {
return diag.FromErr(err)
}
- case "RUN_AS_ROLE":
- if err := d.Set("run_as_role", value); err != nil {
+
+ syncPasswordProperty, err := collections.FindOne(integrationProperties, func(property sdk.SecurityIntegrationProperty) bool { return property.Name == "SYNC_PASSWORD" })
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ if err = handleExternalChangesToObjectInDescribe(d,
+ describeMapping{"network_policy", "network_policy", networkPolicyProperty.Value, networkPolicyProperty.Value, nil},
+ describeMapping{"sync_password", "sync_password", syncPasswordProperty.Value, syncPasswordProperty.Value, nil},
+ ); err != nil {
return diag.FromErr(err)
}
- default:
- log.Printf("[WARN] unexpected property %v returned from Snowflake", name)
}
- }
- return nil
+ // These are all identity sets, needed for the case where:
+ // - previous config was empty (therefore Snowflake defaults had been used)
+ // - new config have the same values that are already in SF
+ if !d.GetRawConfig().IsNull() {
+ if v := d.GetRawConfig().AsValueMap()["network_policy"]; !v.IsNull() {
+ if err = d.Set("network_policy", v.AsString()); err != nil {
+ return diag.FromErr(err)
+ }
+ }
+ if v := d.GetRawConfig().AsValueMap()["sync_password"]; !v.IsNull() {
+ if err = d.Set("sync_password", v.AsString()); err != nil {
+ return diag.FromErr(err)
+ }
+ }
+ if v := d.GetRawConfig().AsValueMap()["comment"]; !v.IsNull() {
+ if err = d.Set("comment", v.AsString()); err != nil {
+ return diag.FromErr(err)
+ }
+ }
+ }
+
+ if err = d.Set(showOutputAttributeName, []map[string]any{schemas.SecurityIntegrationToSchema(integration)}); err != nil {
+ return diag.FromErr(err)
+ }
+
+ if err = d.Set(describeOutputAttributeName, []map[string]any{schemas.ScimSecurityIntegrationPropertiesToSchema(integrationProperties)}); err != nil {
+ return diag.FromErr(err)
+ }
+
+ return nil
+ }
}
func UpdateContextSCIMIntegration(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.NewScimIntegrationSetRequest(), sdk.NewScimIntegrationUnsetRequest()
+
if d.HasChange("enabled") {
set.WithEnabled(d.Get("enabled").(bool))
}
+
if d.HasChange("network_policy") {
- networkPolicyID := sdk.NewAccountObjectIdentifier(d.Get("network_policy").(string))
- if networkPolicyID.Name() != "" {
- set.WithNetworkPolicy(networkPolicyID)
+ if v := d.Get("network_policy").(string); v != "" {
+ set.WithNetworkPolicy(sdk.NewAccountObjectIdentifier(v))
} else {
unset.WithNetworkPolicy(true)
}
}
- if !d.GetRawConfig().AsValueMap()["sync_password"].IsNull() {
- set.WithSyncPassword(d.Get("sync_password").(bool))
- } else {
- unset.WithSyncPassword(true)
+
+ if d.HasChange("sync_password") {
+ if v := d.Get("sync_password").(string); v != "unknown" {
+ parsed, err := strconv.ParseBool(v)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+ set.WithSyncPassword(parsed)
+ } else {
+ unset.WithSyncPassword(true)
+ }
}
if d.HasChange("comment") {
set.WithComment(sdk.StringAllowEmpty{Value: d.Get("comment").(string)})
}
+
if (*set != sdk.ScimIntegrationSetRequest{}) {
if err := client.SecurityIntegrations.AlterScim(ctx, sdk.NewAlterScimSecurityIntegrationRequest(id).WithSet(*set)); err != nil {
return diag.FromErr(err)
}
}
+
if (*unset != sdk.ScimIntegrationUnsetRequest{}) {
if err := client.SecurityIntegrations.AlterScim(ctx, sdk.NewAlterScimSecurityIntegrationRequest(id).WithUnset(*unset)); err != nil {
return diag.FromErr(err)
}
}
- return ReadContextSCIMIntegration(ctx, d, meta)
+
+ return ReadContextSCIMIntegration(false)(ctx, d, meta)
}
func DeleteContextSCIMIntegration(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
diff --git a/pkg/resources/scim_integration_acceptance_test.go b/pkg/resources/scim_integration_acceptance_test.go
index f175a453cb..d7df385d87 100644
--- a/pkg/resources/scim_integration_acceptance_test.go
+++ b/pkg/resources/scim_integration_acceptance_test.go
@@ -2,9 +2,13 @@ package resources_test
import (
"fmt"
- "strings"
"testing"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/importchecks"
+
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/planchecks"
+ tfjson "github.com/hashicorp/terraform-json"
+
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/internal/snowflakeroles"
@@ -20,8 +24,10 @@ import (
func TestAcc_ScimIntegration_basic(t *testing.T) {
networkPolicy, networkPolicyCleanup := acc.TestClient().NetworkPolicy.CreateNetworkPolicy(t)
t.Cleanup(networkPolicyCleanup)
+
role, role2 := snowflakeroles.GenericScimProvisioner, snowflakeroles.OktaProvisioner
id := acc.TestClient().Ids.RandomAccountObjectIdentifier()
+
m := func(enabled bool, scimClient sdk.ScimSecurityIntegrationScimClientOption, runAsRole sdk.AccountObjectIdentifier, complete bool) map[string]config.Variable {
c := map[string]config.Variable{
"name": config.StringVariable(id.Name()),
@@ -36,6 +42,7 @@ func TestAcc_ScimIntegration_basic(t *testing.T) {
}
return c
}
+
resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
PreCheck: func() { acc.TestAccPreCheck(t) },
@@ -44,6 +51,7 @@ func TestAcc_ScimIntegration_basic(t *testing.T) {
},
CheckDestroy: acc.CheckDestroy(t, resources.ScimSecurityIntegration),
Steps: []resource.TestStep{
+ // create with empty optionals
{
ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ScimIntegration/basic"),
ConfigVariables: m(false, sdk.ScimSecurityIntegrationScimClientGeneric, role, false),
@@ -52,10 +60,43 @@ func TestAcc_ScimIntegration_basic(t *testing.T) {
resource.TestCheckResourceAttr("snowflake_scim_integration.test", "enabled", "false"),
resource.TestCheckResourceAttr("snowflake_scim_integration.test", "scim_client", "GENERIC"),
resource.TestCheckResourceAttr("snowflake_scim_integration.test", "run_as_role", role.Name()),
- resource.TestCheckResourceAttrSet("snowflake_scim_integration.test", "sync_password"),
- resource.TestCheckResourceAttrSet("snowflake_scim_integration.test", "created_on"),
+ resource.TestCheckNoResourceAttr("snowflake_scim_integration.test", "network_policy"),
+ resource.TestCheckResourceAttr("snowflake_scim_integration.test", "sync_password", "unknown"),
+ resource.TestCheckNoResourceAttr("snowflake_scim_integration.test", "comment"),
+
+ resource.TestCheckResourceAttr("snowflake_scim_integration.test", "show_output.#", "1"),
+ resource.TestCheckResourceAttr("snowflake_scim_integration.test", "show_output.0.name", id.Name()),
+ resource.TestCheckResourceAttr("snowflake_scim_integration.test", "show_output.0.integration_type", "SCIM - GENERIC"),
+ resource.TestCheckResourceAttr("snowflake_scim_integration.test", "show_output.0.category", "SECURITY"),
+ resource.TestCheckResourceAttr("snowflake_scim_integration.test", "show_output.0.enabled", "false"),
+ resource.TestCheckResourceAttr("snowflake_scim_integration.test", "show_output.0.comment", ""),
+ resource.TestCheckResourceAttrSet("snowflake_scim_integration.test", "show_output.0.created_on"),
+
+ resource.TestCheckResourceAttr("snowflake_scim_integration.test", "describe_output.#", "1"),
+ resource.TestCheckResourceAttr("snowflake_scim_integration.test", "describe_output.0.enabled.0.value", "false"),
+ resource.TestCheckResourceAttr("snowflake_scim_integration.test", "describe_output.0.network_policy.0.value", ""),
+ resource.TestCheckResourceAttr("snowflake_scim_integration.test", "describe_output.0.run_as_role.0.value", role.Name()),
+ resource.TestCheckResourceAttr("snowflake_scim_integration.test", "describe_output.0.sync_password.0.value", "true"),
+ resource.TestCheckResourceAttr("snowflake_scim_integration.test", "describe_output.0.comment.0.value", ""),
),
},
+ // import - without optionals
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ScimIntegration/basic"),
+ ConfigVariables: m(false, sdk.ScimSecurityIntegrationScimClientGeneric, role, false),
+ ResourceName: "snowflake_scim_integration.test",
+ ImportState: true,
+ ImportStateCheck: importchecks.ComposeImportStateCheck(
+ importchecks.TestCheckResourceAttrInstanceState(id.Name(), "name", id.Name()),
+ importchecks.TestCheckResourceAttrInstanceState(id.Name(), "enabled", "false"),
+ importchecks.TestCheckResourceAttrInstanceState(id.Name(), "scim_client", "GENERIC"),
+ importchecks.TestCheckResourceAttrInstanceState(id.Name(), "run_as_role", role.Name()),
+ importchecks.TestCheckResourceAttrInstanceState(id.Name(), "network_policy", ""),
+ importchecks.TestCheckResourceAttrInstanceState(id.Name(), "sync_password", "true"),
+ importchecks.TestCheckResourceAttrInstanceState(id.Name(), "comment", ""),
+ ),
+ },
+ // set optionals
{
ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ScimIntegration/complete"),
ConfigVariables: m(true, sdk.ScimSecurityIntegrationScimClientOkta, role2, true),
@@ -64,18 +105,41 @@ func TestAcc_ScimIntegration_basic(t *testing.T) {
resource.TestCheckResourceAttr("snowflake_scim_integration.test", "enabled", "true"),
resource.TestCheckResourceAttr("snowflake_scim_integration.test", "scim_client", "OKTA"),
resource.TestCheckResourceAttr("snowflake_scim_integration.test", "run_as_role", role2.Name()),
- resource.TestCheckResourceAttr("snowflake_scim_integration.test", "network_policy", sdk.NewAccountObjectIdentifier(networkPolicy.Name).FullyQualifiedName()),
+ resource.TestCheckResourceAttr("snowflake_scim_integration.test", "network_policy", sdk.NewAccountObjectIdentifier(networkPolicy.Name).Name()), // TODO(SNOW-999049): Fix during identifiers rework
resource.TestCheckResourceAttr("snowflake_scim_integration.test", "sync_password", "false"),
resource.TestCheckResourceAttr("snowflake_scim_integration.test", "comment", "foo"),
- resource.TestCheckResourceAttrSet("snowflake_scim_integration.test", "created_on"),
+
+ resource.TestCheckResourceAttr("snowflake_scim_integration.test", "show_output.#", "1"),
+ resource.TestCheckResourceAttr("snowflake_scim_integration.test", "show_output.0.name", id.Name()),
+ resource.TestCheckResourceAttr("snowflake_scim_integration.test", "show_output.0.integration_type", "SCIM - OKTA"),
+ resource.TestCheckResourceAttr("snowflake_scim_integration.test", "show_output.0.category", "SECURITY"),
+ resource.TestCheckResourceAttr("snowflake_scim_integration.test", "show_output.0.enabled", "true"),
+ resource.TestCheckResourceAttr("snowflake_scim_integration.test", "show_output.0.comment", "foo"),
+ resource.TestCheckResourceAttrSet("snowflake_scim_integration.test", "show_output.0.created_on"),
+
+ resource.TestCheckResourceAttr("snowflake_scim_integration.test", "describe_output.#", "1"),
+ resource.TestCheckResourceAttr("snowflake_scim_integration.test", "describe_output.0.enabled.0.value", "true"),
+ resource.TestCheckResourceAttr("snowflake_scim_integration.test", "describe_output.0.network_policy.0.value", sdk.NewAccountObjectIdentifier(networkPolicy.Name).Name()), // TODO(SNOW-999049): Fix during identifiers rework
+ resource.TestCheckResourceAttr("snowflake_scim_integration.test", "describe_output.0.run_as_role.0.value", role2.Name()),
+ resource.TestCheckResourceAttr("snowflake_scim_integration.test", "describe_output.0.sync_password.0.value", "false"),
+ resource.TestCheckResourceAttr("snowflake_scim_integration.test", "describe_output.0.comment.0.value", "foo"),
),
},
+ // import - complete
{
- ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ScimIntegration/basic"),
- ConfigVariables: m(true, sdk.ScimSecurityIntegrationScimClientOkta, role2, true),
- ResourceName: "snowflake_scim_integration.test",
- ImportState: true,
- ImportStateVerify: true,
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_ScimIntegration/complete"),
+ ConfigVariables: m(true, sdk.ScimSecurityIntegrationScimClientOkta, role2, true),
+ ResourceName: "snowflake_scim_integration.test",
+ ImportState: true,
+ ImportStateCheck: importchecks.ComposeImportStateCheck(
+ importchecks.TestCheckResourceAttrInstanceState(id.Name(), "name", id.Name()),
+ importchecks.TestCheckResourceAttrInstanceState(id.Name(), "enabled", "true"),
+ importchecks.TestCheckResourceAttrInstanceState(id.Name(), "scim_client", "OKTA"),
+ importchecks.TestCheckResourceAttrInstanceState(id.Name(), "run_as_role", role2.Name()),
+ importchecks.TestCheckResourceAttrInstanceState(id.Name(), "network_policy", sdk.NewAccountObjectIdentifier(networkPolicy.Name).Name()), // TODO(SNOW-999049): Fix during identifiers rework
+ importchecks.TestCheckResourceAttrInstanceState(id.Name(), "sync_password", "false"),
+ importchecks.TestCheckResourceAttrInstanceState(id.Name(), "comment", "foo"),
+ ),
},
// unset
{
@@ -87,9 +151,8 @@ func TestAcc_ScimIntegration_basic(t *testing.T) {
resource.TestCheckResourceAttr("snowflake_scim_integration.test", "scim_client", "OKTA"),
resource.TestCheckResourceAttr("snowflake_scim_integration.test", "run_as_role", role2.Name()),
resource.TestCheckResourceAttr("snowflake_scim_integration.test", "network_policy", ""),
- resource.TestCheckResourceAttr("snowflake_scim_integration.test", "sync_password", "true"),
+ resource.TestCheckResourceAttr("snowflake_scim_integration.test", "sync_password", "unknown"),
resource.TestCheckResourceAttr("snowflake_scim_integration.test", "comment", ""),
- resource.TestCheckResourceAttrSet("snowflake_scim_integration.test", "created_on"),
),
},
},
@@ -105,7 +168,7 @@ func TestAcc_ScimIntegration_complete(t *testing.T) {
return map[string]config.Variable{
"name": config.StringVariable(id.Name()),
"enabled": config.BoolVariable(false),
- "scim_client": config.StringVariable(strings.ToLower(string(sdk.ScimSecurityIntegrationScimClientGeneric))),
+ "scim_client": config.StringVariable(string(sdk.ScimSecurityIntegrationScimClientGeneric)),
"sync_password": config.BoolVariable(false),
"network_policy_name": config.StringVariable(networkPolicy.Name),
"run_as_role": config.StringVariable(role.Name()),
@@ -128,10 +191,9 @@ func TestAcc_ScimIntegration_complete(t *testing.T) {
resource.TestCheckResourceAttr("snowflake_scim_integration.test", "enabled", "false"),
resource.TestCheckResourceAttr("snowflake_scim_integration.test", "scim_client", "GENERIC"),
resource.TestCheckResourceAttr("snowflake_scim_integration.test", "run_as_role", role.Name()),
- resource.TestCheckResourceAttr("snowflake_scim_integration.test", "network_policy", sdk.NewAccountObjectIdentifier(networkPolicy.Name).FullyQualifiedName()),
+ resource.TestCheckResourceAttr("snowflake_scim_integration.test", "network_policy", sdk.NewAccountObjectIdentifier(networkPolicy.Name).Name()), // TODO(SNOW-999049): Fix during identifiers rework
resource.TestCheckResourceAttr("snowflake_scim_integration.test", "sync_password", "false"),
resource.TestCheckResourceAttr("snowflake_scim_integration.test", "comment", "foo"),
- resource.TestCheckResourceAttrSet("snowflake_scim_integration.test", "created_on"),
),
},
{
@@ -231,7 +293,15 @@ func TestAcc_ScimIntegration_migrateFromVersion091(t *testing.T) {
ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
Config: scimIntegrationv092(id.Name(), role.Name()),
ConfigPlanChecks: resource.ConfigPlanChecks{
- PreApply: []plancheck.PlanCheck{plancheck.ExpectEmptyPlan()},
+ PreApply: []plancheck.PlanCheck{
+ planchecks.ExpectChange("snowflake_scim_integration.test", "name", tfjson.ActionUpdate, sdk.String(id.Name()), sdk.String(id.Name())),
+ planchecks.ExpectChange("snowflake_scim_integration.test", "enabled", tfjson.ActionUpdate, sdk.String("true"), sdk.String("true")),
+ planchecks.ExpectChange("snowflake_scim_integration.test", "scim_client", tfjson.ActionUpdate, sdk.String("GENERIC"), sdk.String("GENERIC")),
+ planchecks.ExpectChange("snowflake_scim_integration.test", "run_as_role", tfjson.ActionUpdate, sdk.String(role.Name()), sdk.String(role.Name())),
+ planchecks.ExpectChange("snowflake_scim_integration.test", "network_policy", tfjson.ActionUpdate, sdk.String(""), sdk.String("")),
+ planchecks.ExpectChange("snowflake_scim_integration.test", "sync_password", tfjson.ActionUpdate, nil, sdk.String("unknown")),
+ planchecks.ExpectChange("snowflake_scim_integration.test", "comment", tfjson.ActionUpdate, nil, nil),
+ },
},
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "name", id.Name()),
diff --git a/pkg/resources/scim_integration_state_upgraders.go b/pkg/resources/scim_integration_state_upgraders.go
index 130ae41076..4c8f60f8c1 100644
--- a/pkg/resources/scim_integration_state_upgraders.go
+++ b/pkg/resources/scim_integration_state_upgraders.go
@@ -1,13 +1,21 @@
package resources
-import "context"
+import (
+ "context"
+ "strconv"
+)
-func v091ScimIntegrationStateUpgrader(ctx context.Context, rawState map[string]interface{}, meta interface{}) (map[string]interface{}, error) {
+func v091ScimIntegrationStateUpgrader(ctx context.Context, rawState map[string]any, meta any) (map[string]any, error) {
if rawState == nil {
return rawState, nil
}
rawState["run_as_role"] = rawState["provisioner_role"]
delete(rawState, "provisioner_role")
+
+ if v, ok := rawState["enabled"]; ok {
+ rawState["enabled"] = strconv.FormatBool(v.(bool))
+ }
+
return rawState, nil
}
diff --git a/pkg/resources/warehouse.go b/pkg/resources/warehouse.go
index 71ab1d0ba1..b44b843e8f 100644
--- a/pkg/resources/warehouse.go
+++ b/pkg/resources/warehouse.go
@@ -30,42 +30,42 @@ var warehouseSchema = map[string]*schema.Schema{
Type: schema.TypeString,
Optional: true,
ValidateDiagFunc: sdkValidation(sdk.ToWarehouseType),
- DiffSuppressFunc: SuppressIfAny(NormalizeAndCompare(sdk.ToWarehouseType), IgnoreChangeToCurrentSnowflakeValue("type")),
+ DiffSuppressFunc: SuppressIfAny(NormalizeAndCompare(sdk.ToWarehouseType), IgnoreChangeToCurrentSnowflakeValueInShow("type")),
Description: fmt.Sprintf("Specifies warehouse type. Valid values are (case-insensitive): %s. Warehouse needs to be suspended to change its type. Provider will handle automatic suspension and resumption if needed.", possibleValuesListed(sdk.ValidWarehouseTypesString)),
},
"warehouse_size": {
Type: schema.TypeString,
Optional: true,
ValidateDiagFunc: sdkValidation(sdk.ToWarehouseSize),
- DiffSuppressFunc: SuppressIfAny(NormalizeAndCompare(sdk.ToWarehouseSize), IgnoreChangeToCurrentSnowflakeValue("size")),
+ DiffSuppressFunc: SuppressIfAny(NormalizeAndCompare(sdk.ToWarehouseSize), IgnoreChangeToCurrentSnowflakeValueInShow("size")),
Description: fmt.Sprintf("Specifies the size of the virtual warehouse. Valid values are (case-insensitive): %s. Consult [warehouse documentation](https://docs.snowflake.com/en/sql-reference/sql/create-warehouse#optional-properties-objectproperties) for the details. Note: removing the size from config will result in the resource recreation.", possibleValuesListed(sdk.ValidWarehouseSizesString)),
},
"max_cluster_count": {
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntBetween(1, 10),
- DiffSuppressFunc: IgnoreChangeToCurrentSnowflakeValue("max_cluster_count"),
+ DiffSuppressFunc: IgnoreChangeToCurrentSnowflakeValueInShow("max_cluster_count"),
Description: "Specifies the maximum number of server clusters for the warehouse.",
},
"min_cluster_count": {
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntBetween(1, 10),
- DiffSuppressFunc: IgnoreChangeToCurrentSnowflakeValue("min_cluster_count"),
+ DiffSuppressFunc: IgnoreChangeToCurrentSnowflakeValueInShow("min_cluster_count"),
Description: "Specifies the minimum number of server clusters for the warehouse (only applies to multi-cluster warehouses).",
},
"scaling_policy": {
Type: schema.TypeString,
Optional: true,
ValidateDiagFunc: sdkValidation(sdk.ToScalingPolicy),
- DiffSuppressFunc: SuppressIfAny(NormalizeAndCompare(sdk.ToWarehouseType), IgnoreChangeToCurrentSnowflakeValue("scaling_policy")),
+ DiffSuppressFunc: SuppressIfAny(NormalizeAndCompare(sdk.ToWarehouseType), IgnoreChangeToCurrentSnowflakeValueInShow("scaling_policy")),
Description: fmt.Sprintf("Specifies the policy for automatically starting and shutting down clusters in a multi-cluster warehouse running in Auto-scale mode. Valid values are (case-insensitive): %s.", possibleValuesListed(sdk.ValidWarehouseScalingPoliciesString)),
},
"auto_suspend": {
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntAtLeast(0),
- DiffSuppressFunc: IgnoreChangeToCurrentSnowflakeValue("auto_suspend"),
+ DiffSuppressFunc: IgnoreChangeToCurrentSnowflakeValueInShow("auto_suspend"),
Description: "Specifies the number of seconds of inactivity after which a warehouse is automatically suspended.",
Default: -1,
},
@@ -73,7 +73,7 @@ var warehouseSchema = map[string]*schema.Schema{
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{"true", "false"}, true),
- DiffSuppressFunc: IgnoreChangeToCurrentSnowflakeValue("auto_resume"),
+ DiffSuppressFunc: IgnoreChangeToCurrentSnowflakeValueInShow("auto_resume"),
Description: "Specifies whether to automatically resume a warehouse when a SQL statement (e.g. query) is submitted to it.",
Default: "unknown",
},
@@ -87,7 +87,7 @@ var warehouseSchema = map[string]*schema.Schema{
Type: schema.TypeString,
Optional: true,
ValidateDiagFunc: IsValidIdentifier[sdk.AccountObjectIdentifier](),
- DiffSuppressFunc: SuppressIfAny(suppressIdentifierQuoting, IgnoreChangeToCurrentSnowflakeValue("resource_monitor")),
+ DiffSuppressFunc: SuppressIfAny(suppressIdentifierQuoting, IgnoreChangeToCurrentSnowflakeValueInShow("resource_monitor")),
Description: "Specifies the name of a resource monitor that is explicitly assigned to the warehouse.",
},
"comment": {
@@ -99,7 +99,7 @@ var warehouseSchema = map[string]*schema.Schema{
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringInSlice([]string{"true", "false"}, true),
- DiffSuppressFunc: IgnoreChangeToCurrentSnowflakeValue("enable_query_acceleration"),
+ DiffSuppressFunc: IgnoreChangeToCurrentSnowflakeValueInShow("enable_query_acceleration"),
Description: "Specifies whether to enable the query acceleration service for queries that rely on this warehouse for compute resources.",
Default: "unknown",
},
@@ -107,7 +107,7 @@ var warehouseSchema = map[string]*schema.Schema{
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntBetween(0, 100),
- DiffSuppressFunc: IgnoreChangeToCurrentSnowflakeValue("query_acceleration_max_scale_factor"),
+ DiffSuppressFunc: IgnoreChangeToCurrentSnowflakeValueInShow("query_acceleration_max_scale_factor"),
Description: "Specifies the maximum scale factor for leasing compute resources for query acceleration. The scale factor is used as a multiplier based on warehouse size.",
Default: -1,
},
@@ -348,7 +348,7 @@ func GetReadWarehouseFunc(withExternalChangesMarking bool) schema.ReadContextFun
}
if withExternalChangesMarking {
- if err = handleExternalChangesToObject(d,
+ if err = handleExternalChangesToObjectInShow(d,
showMapping{"type", "warehouse_type", string(w.Type), w.Type, nil},
showMapping{"size", "warehouse_size", string(w.Size), w.Size, nil},
showMapping{"max_cluster_count", "max_cluster_count", w.MaxClusterCount, w.MaxClusterCount, nil},
diff --git a/pkg/resources/warehouse_rework_show_output_proposal.go b/pkg/resources/warehouse_rework_show_output_proposal.go
index 8c9908953c..654fec57e7 100644
--- a/pkg/resources/warehouse_rework_show_output_proposal.go
+++ b/pkg/resources/warehouse_rework_show_output_proposal.go
@@ -4,10 +4,13 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
-const showOutputAttributeName = "show_output"
+const (
+ showOutputAttributeName = "show_output"
+ describeOutputAttributeName = "describe_output"
+)
-// handleExternalChangesToObject assumes that show output is kept in showOutputAttributeName attribute
-func handleExternalChangesToObject(d *schema.ResourceData, mappings ...showMapping) error {
+// handleExternalChangesToObjectInShow assumes that show output is kept in showOutputAttributeName attribute
+func handleExternalChangesToObjectInShow(d *schema.ResourceData, mappings ...showMapping) error {
if showOutput, ok := d.GetOk(showOutputAttributeName); ok {
showOutputList := showOutput.([]any)
if len(showOutputList) == 1 {
@@ -35,3 +38,43 @@ type showMapping struct {
valueToSet any
normalizeFunc func(any) any
}
+
+// handleExternalChangesToObjectInDescribe assumes that show output is kept in describeOutputAttributeName attribute
+func handleExternalChangesToObjectInDescribe(d *schema.ResourceData, mappings ...describeMapping) error {
+ if describeOutput, ok := d.GetOk(describeOutputAttributeName); ok {
+ describeOutputList := describeOutput.([]any)
+ if len(describeOutputList) == 1 {
+ result := describeOutputList[0].(map[string]any)
+
+ for _, mapping := range mappings {
+ if result[mapping.nameInDescribe] == nil {
+ continue
+ }
+
+ valueToCompareFromList := result[mapping.nameInDescribe].([]any)
+ if len(valueToCompareFromList) != 1 {
+ continue
+ }
+
+ valueToCompareFrom := valueToCompareFromList[0].(map[string]any)["value"]
+ if mapping.normalizeFunc != nil {
+ valueToCompareFrom = mapping.normalizeFunc(valueToCompareFrom)
+ }
+ if valueToCompareFrom != mapping.valueToCompare {
+ if err := d.Set(mapping.nameInConfig, mapping.valueToSet); err != nil {
+ return err
+ }
+ }
+ }
+ }
+ }
+ return nil
+}
+
+type describeMapping struct {
+ nameInDescribe string
+ nameInConfig string
+ valueToCompare any
+ valueToSet any
+ normalizeFunc func(any) any
+}
diff --git a/pkg/schemas/common_types.go b/pkg/schemas/common_types.go
new file mode 100644
index 0000000000..36db0c3692
--- /dev/null
+++ b/pkg/schemas/common_types.go
@@ -0,0 +1,42 @@
+package schemas
+
+import (
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+)
+
+// ParameterListSchema represents Snowflake parameter object.
+var ParameterListSchema = &schema.Schema{
+ Type: schema.TypeList,
+ Computed: true,
+ Elem: &schema.Resource{
+ Schema: ShowParameterSchema,
+ },
+}
+
+// DescribePropertyListSchema represents Snowflake property object returned by DESCRIBE query.
+var DescribePropertyListSchema = &schema.Schema{
+ Type: schema.TypeList,
+ Computed: true,
+ Elem: &schema.Resource{
+ Schema: DescribePropertySchema,
+ },
+}
+
+var DescribePropertySchema = map[string]*schema.Schema{
+ "name": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "type": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "value": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "default": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+}
diff --git a/pkg/schemas/parameters.go b/pkg/schemas/parameters.go
deleted file mode 100644
index b81d3b0786..0000000000
--- a/pkg/schemas/parameters.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package schemas
-
-import "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
-
-// ParameterListSchema represents Snowflake parameter object.
-var ParameterListSchema = &schema.Schema{
- Type: schema.TypeList,
- Computed: true,
- Elem: &schema.Resource{
- Schema: ShowParameterSchema,
- },
-}
diff --git a/pkg/schemas/scim_security_integration.go b/pkg/schemas/scim_security_integration.go
new file mode 100644
index 0000000000..a07f9ffb61
--- /dev/null
+++ b/pkg/schemas/scim_security_integration.go
@@ -0,0 +1,43 @@
+package schemas
+
+import (
+ "strings"
+
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+)
+
+// DescribeScimSecurityIntegrationSchema represents output of DESCRIBE query for the single SecurityIntegration.
+var DescribeScimSecurityIntegrationSchema = map[string]*schema.Schema{
+ "enabled": DescribePropertyListSchema,
+ "network_policy": DescribePropertyListSchema,
+ "run_as_role": DescribePropertyListSchema,
+ "sync_password": DescribePropertyListSchema,
+ "comment": DescribePropertyListSchema,
+}
+
+var _ = DescribeScimSecurityIntegrationSchema
+
+func ScimSecurityIntegrationPropertiesToSchema(securityIntegrationProperties []sdk.SecurityIntegrationProperty) map[string]any {
+ securityIntegrationSchema := make(map[string]any)
+ for _, securityIntegrationProperty := range securityIntegrationProperties {
+ switch securityIntegrationProperty.Name {
+ case "ENABLED",
+ "NETWORK_POLICY",
+ "RUN_AS_ROLE",
+ "SYNC_PASSWORD",
+ "COMMENT":
+ securityIntegrationSchema[strings.ToLower(securityIntegrationProperty.Name)] = []map[string]any{
+ {
+ "name": securityIntegrationProperty.Name,
+ "type": securityIntegrationProperty.Type,
+ "value": securityIntegrationProperty.Value,
+ "default": securityIntegrationProperty.Default,
+ },
+ }
+ }
+ }
+ return securityIntegrationSchema
+}
+
+var _ = ScimSecurityIntegrationPropertiesToSchema