Skip to content

Commit

Permalink
feat: Add service user and legacy service user resources (#3119)
Browse files Browse the repository at this point in the history
Introduce `snowflake_service_user` and `snowflake_legacy_service_user`
resources:
- Add new resources (almost the same schema and the same limitations as
`snowflake_user` resource - read below)
- Adjust datasource and add `type` to the `describe_output`
- Adjust SDK: handling types
- Add SDK integration tests to validate and document the Snowflake
behavior of handling user types (create, alter set, alter unset, and
parameters)
- Fix `TestAcc_User_issue2970` test
- Add migration guide
- Generate models and assertions for the new resources
- Fix sweepers

References: #2951 

##### Implementation considerations
Users of different types differ in Snowflake just slightly - a few
fields are restricted from SERVICE and LEGACY_SERVICE type users. We
decided to split Snowflake resources into multiple resources on our
route to V1, to simplify the logic and usage (in this case, not to
handle the type changes which are followed by attributes being
hidden/revealed). That led to three choices for the implementation, to
reuse the existing resource:
- copy-paste `snowflake_user` resource
- parameterized handling methods of `snowflake_user` resource with the
user type
- enrich the helper methods with the knowledge of the schema (i.e. to
change the logic of setting a field to always check first if the
attribute exists in the given schema)

Ultimately, the second option was picked for the current implementation.
More details of pros and cons of each solution are presented in points
below.

###### copy-paste
Pros:
- Fastest implementation
- We plan to ultimately generate the resources, so at the end the
implementation would be close to duplicated resources

Cons:
- Need to alter the logic in three different places
- Separate implementation of resources would require separate complete
set of tests

###### parameterized handling methods
Pros:
- user object handled in "one" place
- implementation reused, so only smoke acceptance tests are justified

Cons:
- more complex logic in user resource, spread over multiple handling
methods

###### enrich the helper methods
Pros:
- user resource implementation could stay the same in helper methods
(because the checks would be delegated to helper methods)
- solution that can be reused in more resources
- only smoke tests would be justified

Cons:
- SDKv2 does not allow to fetch the schema in context of helper methods
without exposing unexported fields
- "unsafe" solution potentially used in the majority of resources
(because helper methods should be reused more and more)
  • Loading branch information
sfc-gh-asawicki authored Oct 10, 2024
1 parent 97fa9b4 commit 0e88e08
Show file tree
Hide file tree
Showing 40 changed files with 9,312 additions and 524 deletions.
61 changes: 61 additions & 0 deletions MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,67 @@ resource "snowflake_stream_on_table" "stream" {

Then, follow our [Resource migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/resource_migration.md).

### *(new feature)* new snowflake_service_user and snowflake_legacy_service_user resources

Release v0.95.0 introduced reworked `snowflake_user` resource. As [noted](#note-user-types), the new `SERVICE` and `LEGACY_SERVICE` user types were not supported.

This release introduces two new resources to handle these new user types: `snowflake_service_user` and `snowflake_legacy_service_user`.

Both resources have schemas almost identical to the `snowflake_user` resource with the following exceptions:
- `snowflake_service_user` does not contain the following fields (because they are not supported for the user of type `SERVICE` in Snowflake):
- `password`
- `first_name`
- `middle_name`
- `last_name`
- `must_change_password`
- `mins_to_bypass_mfa`
- `disable_mfa`
- `snowflake_legacy_service_user` does not contain the following fields (because they are not supported for the user of type `LEGACY_SERVICE` in Snowflake):
- `first_name`
- `middle_name`
- `last_name`
- `mins_to_bypass_mfa`
- `disable_mfa`

`snowflake_users` datasource was adjusted to handle different user types and `type` field was added to the `describe_output`.

If you used to manage service or legacy service users through `snowflake_user` resource (e.g. using `lifecycle.ignore_changes`) or `snowflake_unsafe_execute`, please migrate to the new resources following [our guidelines on resource migration](docs/technical-documentation/resource_migration.md).

E.g. change the old config from:

```terraform
resource "snowflake_user" "service_user" {
lifecycle {
ignore_changes = [user_type]
}
name = "Snowflake Service User"
login_name = "service_user"
email = "[email protected]"
rsa_public_key = "..."
rsa_public_key_2 = "..."
}
```

to

```
resource "snowflake_service_user" "service_user" {
name = "Snowflake Service User"
login_name = "service_user"
email = "[email protected]"
rsa_public_key = "..."
rsa_public_key_2 = "..."
}
```

Then, follow our [resource migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/docs/technical-documentation/resource_migration.md).

Connected issues: [#2951](https://github.com/Snowflake-Labs/terraform-provider-snowflake/issues/2951)

## v0.95.0 ➞ v0.96.0

### snowflake_masking_policies data source changes
Expand Down
1 change: 1 addition & 0 deletions docs/data-sources/users.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ Read-Only:
- `rsa_public_key_fp` (String)
- `snowflake_lock` (Boolean)
- `snowflake_support` (Boolean)
- `type` (String)


<a id="nestedobjatt--users--parameters"></a>
Expand Down
1,013 changes: 1,013 additions & 0 deletions docs/resources/legacy_service_user.md

Large diffs are not rendered by default.

1,008 changes: 1,008 additions & 0 deletions docs/resources/service_user.md

Large diffs are not rendered by default.

103 changes: 90 additions & 13 deletions docs/resources/user.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
page_title: "snowflake_user Resource - terraform-provider-snowflake"
subcategory: ""
description: |-
Resource used to manage user objects. For more information, check user documentation https://docs.snowflake.com/en/sql-reference/commands-user-role.
Resource used to manage user objects. For more information, check user documentation https://docs.snowflake.com/en/sql-reference/commands-user-role#user-management.
---

!> **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#v094x--v0950) to use it.
Expand All @@ -11,36 +11,113 @@ description: |-

-> **Note** Attaching user policies will be handled in the following versions of the provider which may still affect this resource.

-> **Note** `service` and `legacy_service` user types are currently not supported. They will be supported in the following versions as separate resources (namely `snowflake_service_user` and `snowflake_legacy_service_user`).
-> **Note** Other two user types are handled in separate resources: `snowflake_service_user` for user type `service` and `snowflake_legacy_service_user` for user type `legacy_service`.

-> **Note** External changes to `days_to_expiry`, `mins_to_unlock`, and `mins_to_bypass_mfa` are not currently handled by the provider (because the value changes continuously on Snowflake side after setting it).

# snowflake_user (Resource)

Resource used to manage user objects. For more information, check [user documentation](https://docs.snowflake.com/en/sql-reference/commands-user-role).
Resource used to manage user objects. For more information, check [user documentation](https://docs.snowflake.com/en/sql-reference/commands-user-role#user-management).

## Example Usage

```terraform
# minimal
resource "snowflake_user" "minimal" {
name = "Snowflake User - minimal"
}
# with all attributes set
resource "snowflake_user" "user" {
name = "Snowflake User"
login_name = "snowflake_user"
comment = "A user of snowflake."
password = "secret"
disabled = false
display_name = "Snowflake User"
email = "[email protected]"
first_name = "Snowflake"
middle_name = "Middle"
last_name = "User"
comment = "User of snowflake."
password = "secret"
disabled = "false"
display_name = "Snowflake User display name"
email = "[email protected]"
default_warehouse = "warehouse"
default_secondary_roles_option = "ALL"
default_role = "role1"
default_namespace = "some.namespace"
default_warehouse = "warehouse"
default_secondary_roles = "ALL"
default_role = "role1"
mins_to_unlock = 9
days_to_expiry = 8
mins_to_bypass_mfa = 10
rsa_public_key = "..."
rsa_public_key_2 = "..."
must_change_password = false
must_change_password = "true"
disable_mfa = "false"
}
# all parameters set on the resource level
resource "snowflake_user" "u" {
name = "Snowflake User with all parameters"
abort_detached_query = true
autocommit = false
binary_input_format = "UTF8"
binary_output_format = "BASE64"
client_memory_limit = 1024
client_metadata_request_use_connection_ctx = true
client_prefetch_threads = 2
client_result_chunk_size = 48
client_result_column_case_insensitive = true
client_session_keep_alive = true
client_session_keep_alive_heartbeat_frequency = 2400
client_timestamp_type_mapping = "TIMESTAMP_NTZ"
date_input_format = "YYYY-MM-DD"
date_output_format = "YY-MM-DD"
enable_unload_physical_type_optimization = false
enable_unredacted_query_syntax_error = true
error_on_nondeterministic_merge = false
error_on_nondeterministic_update = true
geography_output_format = "WKB"
geometry_output_format = "WKB"
jdbc_treat_decimal_as_int = false
jdbc_treat_timestamp_ntz_as_utc = true
jdbc_use_session_timezone = false
json_indent = 4
lock_timeout = 21222
log_level = "ERROR"
multi_statement_count = 0
network_policy = "BVYDGRAT_0D5E3DD1_F644_03DE_318A_1179886518A7"
noorder_sequence_as_default = false
odbc_treat_decimal_as_int = true
prevent_unload_to_internal_stages = true
query_tag = "some_tag"
quoted_identifiers_ignore_case = true
rows_per_resultset = 2
search_path = "$public, $current"
simulated_data_sharing_consumer = "some_consumer"
statement_queued_timeout_in_seconds = 10
statement_timeout_in_seconds = 10
strict_json_output = true
s3_stage_vpce_dns_name = "vpce-id.s3.region.vpce.amazonaws.com"
time_input_format = "HH24:MI"
time_output_format = "HH24:MI"
timestamp_day_is_always_24h = true
timestamp_input_format = "YYYY-MM-DD"
timestamp_ltz_output_format = "YYYY-MM-DD HH24:MI:SS"
timestamp_ntz_output_format = "YYYY-MM-DD HH24:MI:SS"
timestamp_output_format = "YYYY-MM-DD HH24:MI:SS"
timestamp_type_mapping = "TIMESTAMP_LTZ"
timestamp_tz_output_format = "YYYY-MM-DD HH24:MI:SS"
timezone = "Europe/Warsaw"
trace_level = "ON_EVENT"
transaction_abort_on_error = true
transaction_default_isolation_level = "READ COMMITTED"
two_digit_century_start = 1980
unsupported_ddl_action = "FAIL"
use_cached_result = false
week_of_year_policy = 1
week_start = 1
}
```
-> **Note** Instead of using fully_qualified_name, you can reference objects managed outside Terraform by constructing a correct ID, consult [identifiers guide](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/guides/identifiers#new-computed-fully-qualified-name-field-in-resources).
Expand Down Expand Up @@ -942,5 +1019,5 @@ Read-Only:
Import is supported using the following syntax:

```shell
terraform import snowflake_user.example userName
terraform import snowflake_user.example '"<user_name>"'
```
1 change: 1 addition & 0 deletions examples/resources/snowflake_legacy_service_user/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
terraform import snowflake_legacy_service_user.example '"<user_name>"'
92 changes: 92 additions & 0 deletions examples/resources/snowflake_legacy_service_user/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# minimal
resource "snowflake_legacy_service_user" "minimal" {
name = "Snowflake Legacy Service User - minimal"
}

# with all attributes set
resource "snowflake_legacy_service_user" "user" {
name = "Snowflake Legacy Service User"
login_name = "legacy_service_user"
comment = "A legacy service user of snowflake."
password = "secret"
disabled = "false"
display_name = "Snowflake Legacy Service User display name"
email = "[email protected]"

default_warehouse = "warehouse"
default_secondary_roles_option = "ALL"
default_role = "role1"
default_namespace = "some.namespace"

mins_to_unlock = 9
days_to_expiry = 8

rsa_public_key = "..."
rsa_public_key_2 = "..."

must_change_password = "true"
}

# all parameters set on the resource level
resource "snowflake_legacy_service_user" "u" {
name = "Snowflake Legacy Service User with all parameters"

abort_detached_query = true
autocommit = false
binary_input_format = "UTF8"
binary_output_format = "BASE64"
client_memory_limit = 1024
client_metadata_request_use_connection_ctx = true
client_prefetch_threads = 2
client_result_chunk_size = 48
client_result_column_case_insensitive = true
client_session_keep_alive = true
client_session_keep_alive_heartbeat_frequency = 2400
client_timestamp_type_mapping = "TIMESTAMP_NTZ"
date_input_format = "YYYY-MM-DD"
date_output_format = "YY-MM-DD"
enable_unload_physical_type_optimization = false
enable_unredacted_query_syntax_error = true
error_on_nondeterministic_merge = false
error_on_nondeterministic_update = true
geography_output_format = "WKB"
geometry_output_format = "WKB"
jdbc_treat_decimal_as_int = false
jdbc_treat_timestamp_ntz_as_utc = true
jdbc_use_session_timezone = false
json_indent = 4
lock_timeout = 21222
log_level = "ERROR"
multi_statement_count = 0
network_policy = "BVYDGRAT_0D5E3DD1_F644_03DE_318A_1179886518A7"
noorder_sequence_as_default = false
odbc_treat_decimal_as_int = true
prevent_unload_to_internal_stages = true
query_tag = "some_tag"
quoted_identifiers_ignore_case = true
rows_per_resultset = 2
search_path = "$public, $current"
simulated_data_sharing_consumer = "some_consumer"
statement_queued_timeout_in_seconds = 10
statement_timeout_in_seconds = 10
strict_json_output = true
s3_stage_vpce_dns_name = "vpce-id.s3.region.vpce.amazonaws.com"
time_input_format = "HH24:MI"
time_output_format = "HH24:MI"
timestamp_day_is_always_24h = true
timestamp_input_format = "YYYY-MM-DD"
timestamp_ltz_output_format = "YYYY-MM-DD HH24:MI:SS"
timestamp_ntz_output_format = "YYYY-MM-DD HH24:MI:SS"
timestamp_output_format = "YYYY-MM-DD HH24:MI:SS"
timestamp_type_mapping = "TIMESTAMP_LTZ"
timestamp_tz_output_format = "YYYY-MM-DD HH24:MI:SS"
timezone = "Europe/Warsaw"
trace_level = "ON_EVENT"
transaction_abort_on_error = true
transaction_default_isolation_level = "READ COMMITTED"
two_digit_century_start = 1980
unsupported_ddl_action = "FAIL"
use_cached_result = false
week_of_year_policy = 1
week_start = 1
}
1 change: 1 addition & 0 deletions examples/resources/snowflake_service_user/import.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
terraform import snowflake_service_user.example '"<user_name>"'
Loading

0 comments on commit 0e88e08

Please sign in to comment.