diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md
index eec752d556..90a84e5c2b 100644
--- a/MIGRATION_GUIDE.md
+++ b/MIGRATION_GUIDE.md
@@ -255,6 +255,19 @@ Type changes:
Validation changes:
- `default_secondary_roles` - only 1-element lists with `"ALL"` element are now supported. Check [Snowflake docs](https://docs.snowflake.com/en/sql-reference/sql/create-user#optional-object-properties-objectproperties) for more details.
+#### *(breaking change)* refactored snowflake_users datasource
+Changes:
+- account checking logic was entirely removed
+- `pattern` renamed to `like`
+- `like`, `starts_with`, and `limit` filters added
+- `SHOW USERS` output is enclosed in `show_output` field inside `users` (all the previous fields in `users` map were removed)
+- Added outputs from **DESC USER** and **SHOW PARAMETERS IN USER** (they can be turned off by declaring `with_describe = false` and `with_parameters = false`, **they're turned on by default**).
+ The additional parameters call **DESC USER** (with `with_describe` turned on) and **SHOW PARAMETERS IN USER** (with `with_parameters` turned on) **per user** returned by **SHOW USERS**.
+ The outputs of both commands are held in `users` entry, where **DESC USER** is saved in the `describe_output` field, and **SHOW PARAMETERS IN USER** in the `parameters` field.
+ 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.
+
+Connected issues: [#2902](https://github.com/Snowflake-Labs/terraform-provider-snowflake/pull/2902)
+
## v0.94.0 ➞ v0.94.1
### changes in snowflake_schema
diff --git a/docs/data-sources/users.md b/docs/data-sources/users.md
index 6a3bc45366..3ba291158e 100644
--- a/docs/data-sources/users.md
+++ b/docs/data-sources/users.md
@@ -2,48 +2,958 @@
page_title: "snowflake_users Data Source - terraform-provider-snowflake"
subcategory: ""
description: |-
-
+ Datasource used to get details of filtered users. Filtering is aligned with the current possibilities for SHOW USERS https://docs.snowflake.com/en/sql-reference/sql/show-users query. The results of SHOW, DESCRIBE, and SHOW PARAMETERS IN are encapsulated in one output collection.
---
-# snowflake_users (Data Source)
+!> **V1 release candidate** This data source 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 data source if needed. Any errors reported will be resolved with a higher priority. We encourage checking this data source 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_users (Data Source)
+Datasource used to get details of filtered users. Filtering is aligned with the current possibilities for [SHOW USERS](https://docs.snowflake.com/en/sql-reference/sql/show-users) query. The results of SHOW, DESCRIBE, and SHOW PARAMETERS IN are encapsulated in one output collection.
## Example Usage
```terraform
-data "snowflake_users" "current" {
- pattern = "user1"
+# Simple usage
+data "snowflake_users" "simple" {
+}
+
+output "simple_output" {
+ value = data.snowflake_users.simple.users
+}
+
+# Filtering (like)
+data "snowflake_users" "like" {
+ like = "user-name"
+}
+
+output "like_output" {
+ value = data.snowflake_users.like.users
+}
+
+# Filtering (starts_with)
+data "snowflake_users" "starts_with" {
+ starts_with = "user-"
+}
+
+output "starts_with_output" {
+ value = data.snowflake_users.starts_with.users
+}
+
+# Filtering (limit)
+data "snowflake_users" "limit" {
+ limit {
+ rows = 10
+ from = "user-"
+ }
+}
+
+output "limit_output" {
+ value = data.snowflake_users.limit.users
+}
+
+# Without additional data (to limit the number of calls make for every found user)
+data "snowflake_users" "only_show" {
+ # with_describe is turned on by default and it calls DESCRIBE USER for every user found and attaches its output to users.*.describe_output field
+ with_describe = false
+
+ # with_parameters is turned on by default and it calls SHOW PARAMETERS FOR USER for every user found and attaches its output to users.*.parameters field
+ with_parameters = false
+}
+
+output "only_show_output" {
+ value = data.snowflake_users.only_show.users
+}
+
+# Ensure the number of users is equal to at least one element (with the use of postcondition)
+data "snowflake_users" "assert_with_postcondition" {
+ starts_with = "user-name"
+ lifecycle {
+ postcondition {
+ condition = length(self.users) > 0
+ error_message = "there should be at least one user"
+ }
+ }
+}
+
+# Ensure the number of users is equal to at exactly one element (with the use of check block)
+check "user_check" {
+ data "snowflake_users" "assert_with_check_block" {
+ like = "user-name"
+ }
+
+ assert {
+ condition = length(data.snowflake_users.assert_with_check_block.users) == 1
+ error_message = "users filtered by '${data.snowflake_users.assert_with_check_block.like}' returned ${length(data.snowflake_users.assert_with_check_block.users)} users where one was expected"
+ }
}
```
## Schema
-### Required
+### Optional
-- `pattern` (String) Users pattern for which to return metadata. Please refer to LIKE keyword from snowflake documentation : https://docs.snowflake.com/en/sql-reference/sql/show-users.html#parameters
+- `like` (String) Filters the output with **case-insensitive** pattern, with support for SQL wildcard characters (`%` and `_`).
+- `limit` (Block List, Max: 1) Limits the number of rows returned. If the `limit.from` is set, then the limit wll start from the first element matched by the expression. The expression is only used to match with the first element, later on the elements are not matched by the prefix, but you can enforce a certain pattern with `starts_with` or `like`. (see [below for nested schema](#nestedblock--limit))
+- `starts_with` (String) Filters the output with **case-sensitive** characters indicating the beginning of the object name.
+- `with_describe` (Boolean) Runs DESC USER for each user returned by SHOW USERS. The output of describe is saved to the description field. By default this value is set to true.
+- `with_parameters` (Boolean) Runs SHOW PARAMETERS FOR USER for each user returned by SHOW USERS. The output of describe is saved to the parameters field as a map. By default this value is set to true.
### Read-Only
- `id` (String) The ID of this resource.
-- `users` (List of Object) The users in the database (see [below for nested schema](#nestedatt--users))
+- `users` (List of Object) Holds the aggregated output of all user details queries. (see [below for nested schema](#nestedatt--users))
+
+
+### Nested Schema for `limit`
+
+Required:
+
+- `rows` (Number) The maximum number of rows to return.
+
+Optional:
+
+- `from` (String) Specifies a **case-sensitive** pattern that is used to match object name. After the first match, the limit on the number of rows will be applied.
+
### Nested Schema for `users`
Read-Only:
+- `describe_output` (List of Object) (see [below for nested schema](#nestedobjatt--users--describe_output))
+- `parameters` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters))
+- `show_output` (List of Object) (see [below for nested schema](#nestedobjatt--users--show_output))
+
+
+### Nested Schema for `users.describe_output`
+
+Read-Only:
+
+- `comment` (String)
+- `custom_landing_page_url` (String)
+- `custom_landing_page_url_flush_next_ui_load` (Boolean)
+- `days_to_expiry` (Number)
+- `default_namespace` (String)
+- `default_role` (String)
+- `default_secondary_roles` (String)
+- `default_warehouse` (String)
+- `disabled` (Boolean)
+- `display_name` (String)
+- `email` (String)
+- `ext_authn_duo` (Boolean)
+- `ext_authn_uid` (String)
+- `first_name` (String)
+- `last_name` (String)
+- `login_name` (String)
+- `middle_name` (String)
+- `mins_to_bypass_mfa` (Number)
+- `mins_to_bypass_network_policy` (Number)
+- `mins_to_unlock` (Number)
+- `must_change_password` (Boolean)
+- `name` (String)
+- `password` (String)
+- `password_last_set_time` (String)
+- `rsa_public_key` (String)
+- `rsa_public_key2` (String)
+- `rsa_public_key2_fp` (String)
+- `rsa_public_key_fp` (String)
+- `snowflake_lock` (Boolean)
+- `snowflake_support` (Boolean)
+
+
+
+### Nested Schema for `users.parameters`
+
+Read-Only:
+
+- `abort_detached_query` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--abort_detached_query))
+- `autocommit` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--autocommit))
+- `binary_input_format` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--binary_input_format))
+- `binary_output_format` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--binary_output_format))
+- `client_memory_limit` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--client_memory_limit))
+- `client_metadata_request_use_connection_ctx` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--client_metadata_request_use_connection_ctx))
+- `client_prefetch_threads` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--client_prefetch_threads))
+- `client_result_chunk_size` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--client_result_chunk_size))
+- `client_result_column_case_insensitive` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--client_result_column_case_insensitive))
+- `client_session_keep_alive` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--client_session_keep_alive))
+- `client_session_keep_alive_heartbeat_frequency` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--client_session_keep_alive_heartbeat_frequency))
+- `client_timestamp_type_mapping` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--client_timestamp_type_mapping))
+- `date_input_format` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--date_input_format))
+- `date_output_format` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--date_output_format))
+- `enable_unload_physical_type_optimization` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--enable_unload_physical_type_optimization))
+- `enable_unredacted_query_syntax_error` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--enable_unredacted_query_syntax_error))
+- `error_on_nondeterministic_merge` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--error_on_nondeterministic_merge))
+- `error_on_nondeterministic_update` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--error_on_nondeterministic_update))
+- `geography_output_format` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--geography_output_format))
+- `geometry_output_format` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--geometry_output_format))
+- `jdbc_treat_decimal_as_int` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--jdbc_treat_decimal_as_int))
+- `jdbc_treat_timestamp_ntz_as_utc` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--jdbc_treat_timestamp_ntz_as_utc))
+- `jdbc_use_session_timezone` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--jdbc_use_session_timezone))
+- `json_indent` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--json_indent))
+- `lock_timeout` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--lock_timeout))
+- `log_level` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--log_level))
+- `multi_statement_count` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--multi_statement_count))
+- `network_policy` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--network_policy))
+- `noorder_sequence_as_default` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--noorder_sequence_as_default))
+- `odbc_treat_decimal_as_int` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--odbc_treat_decimal_as_int))
+- `prevent_unload_to_internal_stages` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--prevent_unload_to_internal_stages))
+- `query_tag` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--query_tag))
+- `quoted_identifiers_ignore_case` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--quoted_identifiers_ignore_case))
+- `rows_per_resultset` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--rows_per_resultset))
+- `s3_stage_vpce_dns_name` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--s3_stage_vpce_dns_name))
+- `search_path` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--search_path))
+- `simulated_data_sharing_consumer` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--simulated_data_sharing_consumer))
+- `statement_queued_timeout_in_seconds` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--statement_queued_timeout_in_seconds))
+- `statement_timeout_in_seconds` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--statement_timeout_in_seconds))
+- `strict_json_output` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--strict_json_output))
+- `time_input_format` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--time_input_format))
+- `time_output_format` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--time_output_format))
+- `timestamp_day_is_always_24h` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--timestamp_day_is_always_24h))
+- `timestamp_input_format` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--timestamp_input_format))
+- `timestamp_ltz_output_format` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--timestamp_ltz_output_format))
+- `timestamp_ntz_output_format` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--timestamp_ntz_output_format))
+- `timestamp_output_format` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--timestamp_output_format))
+- `timestamp_type_mapping` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--timestamp_type_mapping))
+- `timestamp_tz_output_format` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--timestamp_tz_output_format))
+- `timezone` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--timezone))
+- `trace_level` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--trace_level))
+- `transaction_abort_on_error` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--transaction_abort_on_error))
+- `transaction_default_isolation_level` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--transaction_default_isolation_level))
+- `two_digit_century_start` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--two_digit_century_start))
+- `unsupported_ddl_action` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--unsupported_ddl_action))
+- `use_cached_result` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--use_cached_result))
+- `week_of_year_policy` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--week_of_year_policy))
+- `week_start` (List of Object) (see [below for nested schema](#nestedobjatt--users--parameters--week_start))
+
+
+### Nested Schema for `users.parameters.abort_detached_query`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.autocommit`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.binary_input_format`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.binary_output_format`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.client_memory_limit`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.client_metadata_request_use_connection_ctx`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.client_prefetch_threads`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.client_result_chunk_size`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.client_result_column_case_insensitive`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.client_session_keep_alive`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.client_session_keep_alive_heartbeat_frequency`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.client_timestamp_type_mapping`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.date_input_format`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.date_output_format`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.enable_unload_physical_type_optimization`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.enable_unredacted_query_syntax_error`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.error_on_nondeterministic_merge`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.error_on_nondeterministic_update`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.geography_output_format`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.geometry_output_format`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.jdbc_treat_decimal_as_int`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.jdbc_treat_timestamp_ntz_as_utc`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.jdbc_use_session_timezone`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.json_indent`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.lock_timeout`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.log_level`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.multi_statement_count`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.network_policy`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.noorder_sequence_as_default`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.odbc_treat_decimal_as_int`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.prevent_unload_to_internal_stages`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.query_tag`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.quoted_identifiers_ignore_case`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.rows_per_resultset`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.s3_stage_vpce_dns_name`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.search_path`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.simulated_data_sharing_consumer`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.statement_queued_timeout_in_seconds`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.statement_timeout_in_seconds`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.strict_json_output`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.time_input_format`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.time_output_format`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.timestamp_day_is_always_24h`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.timestamp_input_format`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.timestamp_ltz_output_format`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.timestamp_ntz_output_format`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.timestamp_output_format`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.timestamp_type_mapping`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.timestamp_tz_output_format`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.timezone`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.trace_level`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.transaction_abort_on_error`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.transaction_default_isolation_level`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.two_digit_century_start`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.unsupported_ddl_action`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.use_cached_result`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.week_of_year_policy`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+### Nested Schema for `users.parameters.week_start`
+
+Read-Only:
+
+- `default` (String)
+- `description` (String)
+- `key` (String)
+- `level` (String)
+- `value` (String)
+
+
+
+
+### Nested Schema for `users.show_output`
+
+Read-Only:
+
- `comment` (String)
+- `created_on` (String)
+- `days_to_expiry` (String)
- `default_namespace` (String)
- `default_role` (String)
-- `default_secondary_roles` (Set of String)
+- `default_secondary_roles` (String)
- `default_warehouse` (String)
- `disabled` (Boolean)
- `display_name` (String)
- `email` (String)
+- `expires_at_time` (String)
+- `ext_authn_duo` (Boolean)
+- `ext_authn_uid` (String)
- `first_name` (String)
+- `has_mfa` (Boolean)
+- `has_password` (Boolean)
- `has_rsa_public_key` (Boolean)
- `last_name` (String)
+- `last_success_login` (String)
+- `locked_until_time` (String)
- `login_name` (String)
+- `mins_to_bypass_mfa` (String)
+- `mins_to_unlock` (String)
+- `must_change_password` (Boolean)
- `name` (String)
+- `owner` (String)
+- `snowflake_lock` (Boolean)
+- `type` (String)
diff --git a/examples/data-sources/snowflake_users/data-source.tf b/examples/data-sources/snowflake_users/data-source.tf
index 117dd0d224..1cce26ac20 100644
--- a/examples/data-sources/snowflake_users/data-source.tf
+++ b/examples/data-sources/snowflake_users/data-source.tf
@@ -1,3 +1,73 @@
-data "snowflake_users" "current" {
- pattern = "user1"
-}
\ No newline at end of file
+# Simple usage
+data "snowflake_users" "simple" {
+}
+
+output "simple_output" {
+ value = data.snowflake_users.simple.users
+}
+
+# Filtering (like)
+data "snowflake_users" "like" {
+ like = "user-name"
+}
+
+output "like_output" {
+ value = data.snowflake_users.like.users
+}
+
+# Filtering (starts_with)
+data "snowflake_users" "starts_with" {
+ starts_with = "user-"
+}
+
+output "starts_with_output" {
+ value = data.snowflake_users.starts_with.users
+}
+
+# Filtering (limit)
+data "snowflake_users" "limit" {
+ limit {
+ rows = 10
+ from = "user-"
+ }
+}
+
+output "limit_output" {
+ value = data.snowflake_users.limit.users
+}
+
+# Without additional data (to limit the number of calls make for every found user)
+data "snowflake_users" "only_show" {
+ # with_describe is turned on by default and it calls DESCRIBE USER for every user found and attaches its output to users.*.describe_output field
+ with_describe = false
+
+ # with_parameters is turned on by default and it calls SHOW PARAMETERS FOR USER for every user found and attaches its output to users.*.parameters field
+ with_parameters = false
+}
+
+output "only_show_output" {
+ value = data.snowflake_users.only_show.users
+}
+
+# Ensure the number of users is equal to at least one element (with the use of postcondition)
+data "snowflake_users" "assert_with_postcondition" {
+ starts_with = "user-name"
+ lifecycle {
+ postcondition {
+ condition = length(self.users) > 0
+ error_message = "there should be at least one user"
+ }
+ }
+}
+
+# Ensure the number of users is equal to at exactly one element (with the use of check block)
+check "user_check" {
+ data "snowflake_users" "assert_with_check_block" {
+ like = "user-name"
+ }
+
+ assert {
+ condition = length(data.snowflake_users.assert_with_check_block.users) == 1
+ error_message = "users filtered by '${data.snowflake_users.assert_with_check_block.like}' returned ${length(data.snowflake_users.assert_with_check_block.users)} users where one was expected"
+ }
+}
diff --git a/pkg/acceptance/bettertestspoc/assert/resource_assertions.go b/pkg/acceptance/bettertestspoc/assert/resource_assertions.go
index 13fa6b1ae2..3a404a4bcf 100644
--- a/pkg/acceptance/bettertestspoc/assert/resource_assertions.go
+++ b/pkg/acceptance/bettertestspoc/assert/resource_assertions.go
@@ -21,10 +21,11 @@ var (
// ResourceAssert is an embeddable struct that should be used to construct new resource assertions (for resource, show output, parameters, etc.).
// It implements both TestCheckFuncProvider and ImportStateCheckFuncProvider which makes it easy to create new resource assertions.
type ResourceAssert struct {
- name string
- id string
- prefix string
- assertions []ResourceAssertion
+ name string
+ id string
+ prefix string
+ assertions []ResourceAssertion
+ additionalPrefix string
}
// NewResourceAssert creates a ResourceAssert where the resource name should be used as a key for assertions.
@@ -45,11 +46,22 @@ func NewImportedResourceAssert(id string, prefix string) *ResourceAssert {
}
}
+// NewDatasourceAssert creates a ResourceAssert for data sources.
+func NewDatasourceAssert(name string, prefix string, additionalPrefix string) *ResourceAssert {
+ return &ResourceAssert{
+ name: name,
+ prefix: prefix,
+ assertions: make([]ResourceAssertion, 0),
+ additionalPrefix: additionalPrefix,
+ }
+}
+
type resourceAssertionType string
const (
- resourceAssertionTypeValueSet = "VALUE_SET"
- resourceAssertionTypeValueNotSet = "VALUE_NOT_SET"
+ resourceAssertionTypeValueSet = "VALUE_SET"
+ resourceAssertionTypeValueNotSet = "VALUE_NOT_SET"
+ resourceAssertionTypeValuePresent = "VALUE_PRESENT"
)
type ResourceAssertion struct {
@@ -59,6 +71,7 @@ type ResourceAssertion struct {
}
func (r *ResourceAssert) AddAssertion(assertion ResourceAssertion) {
+ assertion.fieldName = r.additionalPrefix + assertion.fieldName
r.assertions = append(r.assertions, assertion)
}
@@ -92,6 +105,11 @@ func ResourceShowOutputValueSet(fieldName string, expected string) ResourceAsser
return ResourceAssertion{fieldName: showOutputPrefix + fieldName, expectedValue: expected, resourceAssertionType: resourceAssertionTypeValueSet}
}
+// TODO [SNOW-1501905]: generate assertions with resourceAssertionTypeValuePresent
+func ResourceShowOutputValuePresent(fieldName string) ResourceAssertion {
+ return ResourceAssertion{fieldName: showOutputPrefix + fieldName, resourceAssertionType: resourceAssertionTypeValuePresent}
+}
+
const (
parametersPrefix = "parameters.0."
parametersValueSuffix = ".0.value"
@@ -137,6 +155,11 @@ func (r *ResourceAssert) ToTerraformTestCheckFunc(t *testing.T) resource.TestChe
errCut, _ := strings.CutPrefix(err.Error(), fmt.Sprintf("%s: ", r.name))
result = append(result, fmt.Errorf("%s %s assertion [%d/%d]: failed with error: %s", r.name, r.prefix, i+1, len(r.assertions), errCut))
}
+ case resourceAssertionTypeValuePresent:
+ if err := resource.TestCheckResourceAttrSet(r.name, a.fieldName)(s); err != nil {
+ errCut, _ := strings.CutPrefix(err.Error(), fmt.Sprintf("%s: ", r.name))
+ result = append(result, fmt.Errorf("%s %s assertion [%d/%d]: failed with error: %s", r.name, r.prefix, i+1, len(r.assertions), errCut))
+ }
}
}
@@ -159,6 +182,8 @@ func (r *ResourceAssert) ToTerraformImportStateCheckFunc(t *testing.T) resource.
}
case resourceAssertionTypeValueNotSet:
panic("implement")
+ case resourceAssertionTypeValuePresent:
+ panic("implement")
}
}
diff --git a/pkg/acceptance/bettertestspoc/assert/resourceparametersassert/users_datasource_parameters_ext.go b/pkg/acceptance/bettertestspoc/assert/resourceparametersassert/users_datasource_parameters_ext.go
new file mode 100644
index 0000000000..77e2af50b9
--- /dev/null
+++ b/pkg/acceptance/bettertestspoc/assert/resourceparametersassert/users_datasource_parameters_ext.go
@@ -0,0 +1,18 @@
+package resourceparametersassert
+
+import (
+ "testing"
+
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert"
+)
+
+// UsersDatasourceParameters is a temporary workaround to have better parameter assertions in data source acceptance tests.
+func UsersDatasourceParameters(t *testing.T, name string) *UserResourceParametersAssert {
+ t.Helper()
+
+ u := UserResourceParametersAssert{
+ ResourceAssert: assert.NewDatasourceAssert("data."+name, "parameters", "users.0."),
+ }
+ u.AddAssertion(assert.ValueSet("parameters.#", "1"))
+ return &u
+}
diff --git a/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/user_show_output_ext.go b/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/user_show_output_ext.go
new file mode 100644
index 0000000000..9daf9a672f
--- /dev/null
+++ b/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/user_show_output_ext.go
@@ -0,0 +1,25 @@
+package resourceshowoutputassert
+
+import (
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert"
+)
+
+func (u *UserShowOutputAssert) HasCreatedOnNotEmpty() *UserShowOutputAssert {
+ u.AddAssertion(assert.ResourceShowOutputValuePresent("created_on"))
+ return u
+}
+
+func (u *UserShowOutputAssert) HasDaysToExpiryNotEmpty() *UserShowOutputAssert {
+ u.AddAssertion(assert.ResourceShowOutputValuePresent("days_to_expiry"))
+ return u
+}
+
+func (u *UserShowOutputAssert) HasMinsToUnlockNotEmpty() *UserShowOutputAssert {
+ u.AddAssertion(assert.ResourceShowOutputValuePresent("mins_to_unlock"))
+ return u
+}
+
+func (u *UserShowOutputAssert) HasMinsToBypassMfaNotEmpty() *UserShowOutputAssert {
+ u.AddAssertion(assert.ResourceShowOutputValuePresent("mins_to_bypass_mfa"))
+ return u
+}
diff --git a/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/users_datasource_show_output_ext.go b/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/users_datasource_show_output_ext.go
new file mode 100644
index 0000000000..76887e6bcd
--- /dev/null
+++ b/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/users_datasource_show_output_ext.go
@@ -0,0 +1,18 @@
+package resourceshowoutputassert
+
+import (
+ "testing"
+
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert"
+)
+
+// UsersDatasourceShowOutput is a temporary workaround to have better show output assertions in data source acceptance tests.
+func UsersDatasourceShowOutput(t *testing.T, name string) *UserShowOutputAssert {
+ t.Helper()
+
+ u := UserShowOutputAssert{
+ ResourceAssert: assert.NewDatasourceAssert("data."+name, "show_output", "users.0."),
+ }
+ u.AddAssertion(assert.ValueSet("show_output.#", "1"))
+ return &u
+}
diff --git a/pkg/datasources/testdata/TestAcc_Users/without_user/test.tf b/pkg/datasources/testdata/TestAcc_Users/without_user/test.tf
new file mode 100644
index 0000000000..622994fe7f
--- /dev/null
+++ b/pkg/datasources/testdata/TestAcc_Users/without_user/test.tf
@@ -0,0 +1,10 @@
+data "snowflake_users" "test" {
+ like = "non-existing-user"
+
+ lifecycle {
+ postcondition {
+ condition = length(self.users) > 0
+ error_message = "there should be at least one user"
+ }
+ }
+}
diff --git a/pkg/datasources/users.go b/pkg/datasources/users.go
index e620ac6ee5..18fe56371a 100644
--- a/pkg/datasources/users.go
+++ b/pkg/datasources/users.go
@@ -2,93 +2,87 @@ package datasources
import (
"context"
- "fmt"
- "log"
- "strings"
"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider"
-
- "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/resources"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/schemas"
"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)
var usersSchema = map[string]*schema.Schema{
- "pattern": {
- Type: schema.TypeString,
- Required: true,
- Description: "Users pattern for which to return metadata. Please refer to LIKE keyword from " +
- "snowflake documentation : https://docs.snowflake.com/en/sql-reference/sql/show-users.html#parameters",
+ "with_describe": {
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: true,
+ Description: "Runs DESC USER for each user returned by SHOW USERS. The output of describe is saved to the description field. By default this value is set to true.",
},
- "users": {
+ "with_parameters": {
+ Type: schema.TypeBool,
+ Optional: true,
+ Default: true,
+ Description: "Runs SHOW PARAMETERS FOR USER for each user returned by SHOW USERS. The output of describe is saved to the parameters field as a map. By default this value is set to true.",
+ },
+ "like": {
+ Type: schema.TypeString,
+ Optional: true,
+ Description: "Filters the output with **case-insensitive** pattern, with support for SQL wildcard characters (`%` and `_`).",
+ },
+ "starts_with": {
+ Type: schema.TypeString,
+ Optional: true,
+ Description: "Filters the output with **case-sensitive** characters indicating the beginning of the object name.",
+ },
+ "limit": {
Type: schema.TypeList,
- Computed: true,
- Description: "The users in the database",
+ Optional: true,
+ Description: "Limits the number of rows returned. If the `limit.from` is set, then the limit wll start from the first element matched by the expression. The expression is only used to match with the first element, later on the elements are not matched by the prefix, but you can enforce a certain pattern with `starts_with` or `like`.",
+ MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
- "name": {
- Type: schema.TypeString,
- Computed: true,
- },
- "login_name": {
- Type: schema.TypeString,
- Optional: true,
- Computed: true,
- },
- "comment": {
- Type: schema.TypeString,
- Optional: true,
- Computed: true,
- },
- "disabled": {
- Type: schema.TypeBool,
- Optional: true,
- Computed: true,
- },
- "default_warehouse": {
- Type: schema.TypeString,
- Optional: true,
- Computed: true,
+ "rows": {
+ Type: schema.TypeInt,
+ Required: true,
+ Description: "The maximum number of rows to return.",
},
- "default_namespace": {
- Type: schema.TypeString,
- Optional: true,
- Computed: true,
+ "from": {
+ Type: schema.TypeString,
+ Optional: true,
+ Description: "Specifies a **case-sensitive** pattern that is used to match object name. After the first match, the limit on the number of rows will be applied.",
},
- "default_role": {
- Type: schema.TypeString,
- Optional: true,
- Computed: true,
- },
- "default_secondary_roles": {
- Type: schema.TypeSet,
- Elem: &schema.Schema{Type: schema.TypeString},
- Optional: true,
- Computed: true,
- },
- "has_rsa_public_key": {
- Type: schema.TypeBool,
- Computed: true,
- },
- "email": {
- Type: schema.TypeString,
- Optional: true,
- Computed: true,
- },
- "display_name": {
- Type: schema.TypeString,
- Computed: true,
- Optional: true,
+ },
+ },
+ },
+ "users": {
+ Type: schema.TypeList,
+ Computed: true,
+ Description: "Holds the aggregated output of all user details queries.",
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ resources.ShowOutputAttributeName: {
+ Type: schema.TypeList,
+ Computed: true,
+ Description: "Holds the output of SHOW USERS.",
+ Elem: &schema.Resource{
+ Schema: schemas.ShowUserSchema,
+ },
},
- "first_name": {
- Type: schema.TypeString,
- Optional: true,
- Computed: true,
+ resources.DescribeOutputAttributeName: {
+ Type: schema.TypeList,
+ Computed: true,
+ Description: "Holds the output of DESCRIBE USER.",
+ Elem: &schema.Resource{
+ Schema: schemas.UserDescribeSchema,
+ },
},
- "last_name": {
- Type: schema.TypeString,
- Optional: true,
- Computed: true,
+ resources.ParametersAttributeName: {
+ Type: schema.TypeList,
+ Computed: true,
+ Description: "Holds the output of SHOW PARAMETERS FOR USER.",
+ Elem: &schema.Resource{
+ Schema: schemas.ShowUserParametersSchema,
+ },
},
},
},
@@ -97,57 +91,76 @@ var usersSchema = map[string]*schema.Schema{
func Users() *schema.Resource {
return &schema.Resource{
- Read: ReadUsers,
- Schema: usersSchema,
- Importer: &schema.ResourceImporter{
- StateContext: schema.ImportStatePassthroughContext,
- },
+ ReadContext: ReadUsers,
+ Schema: usersSchema,
+ Description: "Datasource used to get details of filtered users. Filtering is aligned with the current possibilities for [SHOW USERS](https://docs.snowflake.com/en/sql-reference/sql/show-users) query. The results of SHOW, DESCRIBE, and SHOW PARAMETERS IN are encapsulated in one output collection.",
}
}
-func ReadUsers(d *schema.ResourceData, meta interface{}) error {
+func ReadUsers(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
client := meta.(*provider.Context).Client
- ctx := context.Background()
+ var opts sdk.ShowUserOptions
- userPattern := d.Get("pattern").(string)
+ if likePattern, ok := d.GetOk("like"); ok {
+ opts.Like = &sdk.Like{
+ Pattern: sdk.String(likePattern.(string)),
+ }
+ }
- account, err1 := client.ContextFunctions.CurrentAccount(ctx)
- region, err2 := client.ContextFunctions.CurrentRegion(ctx)
- if err1 != nil || err2 != nil {
- log.Print("[DEBUG] unable to retrieve current account")
- d.SetId("")
- return nil
+ if startsWith, ok := d.GetOk("starts_with"); ok {
+ opts.StartsWith = sdk.String(startsWith.(string))
}
- d.SetId(fmt.Sprintf("%s.%s", account, region))
- extractedUsers, err := client.Users.Show(ctx, &sdk.ShowUserOptions{
- Like: &sdk.Like{Pattern: sdk.String(userPattern)},
- })
+ if limit, ok := d.GetOk("limit"); ok && len(limit.([]any)) == 1 {
+ limitMap := limit.([]any)[0].(map[string]any)
+
+ rows := limitMap["rows"].(int)
+ opts.Limit = &rows
+
+ if from, ok := limitMap["from"].(string); ok {
+ opts.From = &from
+ }
+ }
+
+ users, err := client.Users.Show(ctx, &opts)
if err != nil {
- log.Printf("[DEBUG] no users found in account (%s)", d.Id())
- d.SetId("")
- return nil
+ return diag.FromErr(err)
}
+ d.SetId("users_read")
+
+ flattenedUsers := make([]map[string]any, len(users))
- users := make([]map[string]any, len(extractedUsers))
-
- for i, user := range extractedUsers {
- users[i] = map[string]any{
- "name": user.Name,
- "login_name": user.LoginName,
- "comment": user.Comment,
- "disabled": user.Disabled,
- "default_warehouse": user.DefaultWarehouse,
- "default_namespace": user.DefaultNamespace,
- "default_role": user.DefaultRole,
- "default_secondary_roles": strings.Split(helpers.ListContentToString(user.DefaultSecondaryRoles), ","),
- "has_rsa_public_key": user.HasRsaPublicKey,
- "email": user.Email,
- "display_name": user.DisplayName,
- "first_name": user.FirstName,
- "last_name": user.LastName,
+ for i, user := range users {
+ user := user
+ var userDescription []map[string]any
+ if d.Get("with_describe").(bool) {
+ describeResult, err := client.Users.Describe(ctx, user.ID())
+ if err != nil {
+ return diag.FromErr(err)
+ }
+ userDescription = schemas.UserDescriptionToSchema(*describeResult)
}
+
+ var userParameters []map[string]any
+ if d.Get("with_parameters").(bool) {
+ parameters, err := client.Users.ShowParameters(ctx, user.ID())
+ if err != nil {
+ return diag.FromErr(err)
+ }
+ userParameters = []map[string]any{schemas.UserParametersToSchema(parameters)}
+ }
+
+ flattenedUsers[i] = map[string]any{
+ resources.ShowOutputAttributeName: []map[string]any{schemas.UserToSchema(&user)},
+ resources.DescribeOutputAttributeName: userDescription,
+ resources.ParametersAttributeName: userParameters,
+ }
+ }
+
+ err = d.Set("users", flattenedUsers)
+ if err != nil {
+ return diag.FromErr(err)
}
- return d.Set("users", users)
+ return nil
}
diff --git a/pkg/datasources/users_acceptance_test.go b/pkg/datasources/users_acceptance_test.go
index 4f06e8419d..1ea103e079 100644
--- a/pkg/datasources/users_acceptance_test.go
+++ b/pkg/datasources/users_acceptance_test.go
@@ -2,55 +2,271 @@ package datasources_test
import (
"fmt"
+ "regexp"
+ "strings"
"testing"
acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceparametersassert"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config/model"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/tfversion"
)
-func TestAcc_Users(t *testing.T) {
- userName := acc.TestClient().Ids.Alpha()
+func TestAcc_Users_Complete(t *testing.T) {
+ id := acc.TestClient().Ids.RandomAccountObjectIdentifier()
+
+ comment := random.Comment()
+ pass := random.Password()
+ key1, key1Fp := random.GenerateRSAPublicKey(t)
+ key2, key2Fp := random.GenerateRSAPublicKey(t)
+
+ userModelNoAttributes := model.User("u", id.Name())
+ userModelAllAttributes := model.User("u", id.Name()).
+ WithPassword(pass).
+ WithLoginName(id.Name() + "_login").
+ WithDisplayName("Display Name").
+ WithFirstName("Jan").
+ WithMiddleName("Jakub").
+ WithLastName("Testowski").
+ WithEmail("fake@email.com").
+ WithMustChangePassword("true").
+ WithDisabled("false").
+ WithDaysToExpiry(8).
+ WithMinsToUnlock(9).
+ WithDefaultWarehouse("some_warehouse").
+ WithDefaultNamespace("some.namespace").
+ WithDefaultRole("some_role").
+ WithDefaultSecondaryRolesStringList("ALL").
+ WithMinsToBypassMfa(10).
+ WithRsaPublicKey(key1).
+ WithRsaPublicKey2(key2).
+ WithComment(comment).
+ WithDisableMfa("true")
+
resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
tfversion.RequireAbove(tfversion.Version1_5_0),
},
+ CheckDestroy: acc.CheckDestroy(t, resources.User),
Steps: []resource.TestStep{
{
- Config: users(userName),
+ Config: config.FromModel(t, userModelAllAttributes) + datasourceWithLike(),
+ Check: assert.AssertThat(t,
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.#", "1")),
+ resourceshowoutputassert.UsersDatasourceShowOutput(t, "snowflake_users.test").
+ HasName(id.Name()).
+ HasCreatedOnNotEmpty().
+ HasLoginName(fmt.Sprintf("%s_LOGIN", id.Name())).
+ HasDisplayName("Display Name").
+ HasFirstName("Jan").
+ HasLastName("Testowski").
+ HasEmail("fake@email.com").
+ HasMustChangePassword(true).
+ HasDisabled(false).
+ HasSnowflakeLock(false).
+ HasDaysToExpiryNotEmpty().
+ HasMinsToUnlockNotEmpty().
+ HasDefaultWarehouse("some_warehouse").
+ HasDefaultNamespace("some.namespace").
+ HasDefaultRole("some_role").
+ HasDefaultSecondaryRoles(`["ALL"]`).
+ HasMinsToBypassMfaNotEmpty().
+ HasHasRsaPublicKey(true).
+ HasComment(comment),
+ resourceparametersassert.UsersDatasourceParameters(t, "snowflake_users.test").
+ HasAllDefaults(),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.name", id.Name())),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.comment", comment)),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.display_name", "Display Name")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.login_name", fmt.Sprintf("%s_LOGIN", id.Name()))),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.first_name", "Jan")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.middle_name", "Jakub")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.last_name", "Testowski")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.email", "fake@email.com")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.password", "********")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.must_change_password", "true")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.disabled", "false")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.snowflake_lock", "false")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.snowflake_support", "false")),
+ assert.Check(resource.TestCheckResourceAttrSet("data.snowflake_users.test", "users.0.describe_output.0.days_to_expiry")),
+ assert.Check(resource.TestCheckResourceAttrSet("data.snowflake_users.test", "users.0.describe_output.0.mins_to_unlock")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.default_warehouse", "some_warehouse")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.default_namespace", "some.namespace")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.default_role", "some_role")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.default_secondary_roles", `["ALL"]`)),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.ext_authn_duo", "false")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.ext_authn_uid", "")),
+ assert.Check(resource.TestCheckResourceAttrSet("data.snowflake_users.test", "users.0.describe_output.0.mins_to_bypass_mfa")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.mins_to_bypass_network_policy", "0")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.rsa_public_key", key1)),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.rsa_public_key_fp", "SHA256:"+key1Fp)),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.rsa_public_key2", key2)),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.rsa_public_key2_fp", "SHA256:"+key2Fp)),
+ assert.Check(resource.TestCheckResourceAttrSet("data.snowflake_users.test", "users.0.describe_output.0.password_last_set_time")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.custom_landing_page_url", "")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.custom_landing_page_url_flush_next_ui_load", "false")),
+ ),
+ },
+ {
+ Config: config.FromModel(t, userModelNoAttributes) + datasourceWithLike(),
+ Check: assert.AssertThat(t,
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.#", "1")),
+ resourceshowoutputassert.UsersDatasourceShowOutput(t, "snowflake_users.test").
+ HasName(id.Name()).
+ HasCreatedOnNotEmpty().
+ HasLoginName(strings.ToUpper(id.Name())).
+ HasDisplayName("").
+ HasFirstName("").
+ HasLastName("").
+ HasEmail("").
+ HasMustChangePassword(false).
+ HasDisabled(false).
+ HasSnowflakeLock(false).
+ HasDaysToExpiry("").
+ HasMinsToUnlock("").
+ HasDefaultWarehouse("").
+ HasDefaultNamespace("").
+ HasDefaultRole("").
+ HasDefaultSecondaryRoles("").
+ HasMinsToBypassMfa("").
+ HasHasRsaPublicKey(false).
+ HasComment(""),
+ resourceparametersassert.UsersDatasourceParameters(t, "snowflake_users.test").
+ HasAllDefaults(),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.name", id.Name())),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.comment", "")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.display_name", "")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.login_name", strings.ToUpper(id.Name()))),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.first_name", "")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.middle_name", "")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.last_name", "")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.email", "")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.password", "")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.must_change_password", "false")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.disabled", "false")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.snowflake_lock", "false")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.snowflake_support", "false")),
+ assert.Check(resource.TestCheckResourceAttrSet("data.snowflake_users.test", "users.0.describe_output.0.days_to_expiry")),
+ assert.Check(resource.TestCheckResourceAttrSet("data.snowflake_users.test", "users.0.describe_output.0.mins_to_unlock")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.default_warehouse", "")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.default_namespace", "")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.default_role", "")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.default_secondary_roles", "")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.ext_authn_duo", "false")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.ext_authn_uid", "")),
+ assert.Check(resource.TestCheckResourceAttrSet("data.snowflake_users.test", "users.0.describe_output.0.mins_to_bypass_mfa")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.mins_to_bypass_network_policy", "0")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.rsa_public_key", "")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.rsa_public_key_fp", "")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.rsa_public_key2", "")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.rsa_public_key2_fp", "")),
+ assert.Check(resource.TestCheckResourceAttrSet("data.snowflake_users.test", "users.0.describe_output.0.password_last_set_time")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.custom_landing_page_url", "")),
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_users.test", "users.0.describe_output.0.custom_landing_page_url_flush_next_ui_load", "false")),
+ ),
+ },
+ },
+ })
+}
+
+func TestAcc_Users_DifferentFiltering(t *testing.T) {
+ prefix := random.AlphaN(4)
+ id := acc.TestClient().Ids.RandomAccountObjectIdentifierWithPrefix(prefix)
+ id2 := acc.TestClient().Ids.RandomAccountObjectIdentifierWithPrefix(prefix)
+ id3 := acc.TestClient().Ids.RandomAccountObjectIdentifier()
+
+ userModel := model.User("u", id.Name())
+ user2Model := model.User("u2", id2.Name())
+ user3Model := model.User("u3", id3.Name())
+
+ allUsersConfig := config.FromModel(t, userModel) + config.FromModel(t, user2Model) + config.FromModel(t, user3Model)
+
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.RequireAbove(tfversion.Version1_5_0),
+ },
+ CheckDestroy: acc.CheckDestroy(t, resources.User),
+ Steps: []resource.TestStep{
+ {
+ Config: allUsersConfig + datasourceWithLikeMultipleUsers(),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr("data.snowflake_users.test", "users.#", "1"),
+ ),
+ },
+ {
+ Config: allUsersConfig + datasourceWithStartsWithMultipleUsers(prefix),
Check: resource.ComposeTestCheckFunc(
- resource.TestCheckResourceAttrSet("data.snowflake_users.u", "users.#"),
- resource.TestCheckResourceAttr("data.snowflake_users.u", "users.#", "1"),
- resource.TestCheckResourceAttr("data.snowflake_users.u", "users.0.name", userName),
- resource.TestCheckResourceAttr("data.snowflake_users.u", "users.0.disabled", "false"),
+ resource.TestCheckResourceAttr("data.snowflake_users.test", "users.#", "2"),
+ ),
+ },
+ {
+ Config: allUsersConfig + datasourceWithLimitMultipleUsers(1, prefix),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr("data.snowflake_users.test", "users.#", "1"),
),
},
},
})
}
-func users(userName string) string {
+func datasourceWithLike() string {
+ return `
+ data "snowflake_users" "test" {
+ like = snowflake_user.u.name
+ }
+`
+}
+
+func datasourceWithLikeMultipleUsers() string {
+ return `
+ data "snowflake_users" "test" {
+ depends_on = [snowflake_user.u, snowflake_user.u2, snowflake_user.u3]
+ like = snowflake_user.u.name
+ }
+`
+}
+
+func datasourceWithStartsWithMultipleUsers(startsWith string) string {
return fmt.Sprintf(`
- resource "snowflake_user" "u" {
- name = "%s"
- comment = "test comment"
- login_name = "%s_login"
- display_name = "Display Name"
- first_name = "Alex"
- last_name = "Kita"
- email = "fake@email.com"
- disabled = false
- default_warehouse="foo"
- default_role="FOO"
- default_secondary_roles = ["ALL"]
- default_namespace="FOO"
+ data "snowflake_users" "test" {
+ depends_on = [snowflake_user.u, snowflake_user.u2, snowflake_user.u3]
+ starts_with = "%s"
}
+`, startsWith)
+}
- data snowflake_users "u" {
- pattern = "%s"
- depends_on = [snowflake_user.u]
+func datasourceWithLimitMultipleUsers(rows int, from string) string {
+ return fmt.Sprintf(`
+ data "snowflake_users" "test" {
+ depends_on = [snowflake_user.u, snowflake_user.u2, snowflake_user.u3]
+ limit {
+ rows = %d
+ from = "%s"
+ }
}
- `, userName, userName, userName)
+`, rows, from)
+}
+
+func TestAcc_Users_UserNotFound_WithPostConditions(t *testing.T) {
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.RequireAbove(tfversion.Version1_5_0),
+ },
+ Steps: []resource.TestStep{
+ {
+ ConfigDirectory: acc.ConfigurationDirectory("TestAcc_Users/without_user"),
+ ExpectError: regexp.MustCompile("there should be at least one user"),
+ },
+ },
+ })
}
diff --git a/pkg/resources/database_acceptance_test.go b/pkg/resources/database_acceptance_test.go
index 9debdb1a3a..ee0efcf51e 100644
--- a/pkg/resources/database_acceptance_test.go
+++ b/pkg/resources/database_acceptance_test.go
@@ -815,7 +815,7 @@ func TestAcc_Database_IntParameter(t *testing.T) {
},
Check: assert.AssertThat(t,
assert.Check(resource.TestCheckResourceAttr("snowflake_database.test", "data_retention_time_in_days", "1")),
- objectparametersassert.DatabaseParameters(t, id).HasDataRetentionTimeInDays(25).HasDataRetentionTimeInDaysLevel(sdk.ParameterTypeDatabase),
+ objectparametersassert.DatabaseParameters(t, id).HasDataRetentionTimeInDays(1).HasDataRetentionTimeInDaysLevel(sdk.ParameterTypeDatabase),
),
},
// remove the param from config
diff --git a/pkg/schemas/user_describe.go b/pkg/schemas/user_describe.go
new file mode 100644
index 0000000000..8292999d39
--- /dev/null
+++ b/pkg/schemas/user_describe.go
@@ -0,0 +1,231 @@
+package schemas
+
+import (
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+)
+
+// UserDescribeSchema represents output of DESCRIBE query for the single UserDetails.
+var UserDescribeSchema = map[string]*schema.Schema{
+ "name": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "comment": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "display_name": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "login_name": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "first_name": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "middle_name": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "last_name": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "email": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "password": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "must_change_password": {
+ Type: schema.TypeBool,
+ Computed: true,
+ },
+ "disabled": {
+ Type: schema.TypeBool,
+ Computed: true,
+ },
+ "snowflake_lock": {
+ Type: schema.TypeBool,
+ Computed: true,
+ },
+ "snowflake_support": {
+ Type: schema.TypeBool,
+ Computed: true,
+ },
+ "days_to_expiry": {
+ Type: schema.TypeFloat,
+ Computed: true,
+ },
+ "mins_to_unlock": {
+ Type: schema.TypeInt,
+ Computed: true,
+ },
+ "default_warehouse": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "default_namespace": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "default_role": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "default_secondary_roles": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "ext_authn_duo": {
+ Type: schema.TypeBool,
+ Computed: true,
+ },
+ "ext_authn_uid": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "mins_to_bypass_mfa": {
+ Type: schema.TypeInt,
+ Computed: true,
+ },
+ "mins_to_bypass_network_policy": {
+ Type: schema.TypeInt,
+ Computed: true,
+ },
+ "rsa_public_key": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "rsa_public_key_fp": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "rsa_public_key2": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "rsa_public_key2_fp": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "password_last_set_time": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "custom_landing_page_url": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "custom_landing_page_url_flush_next_ui_load": {
+ Type: schema.TypeBool,
+ Computed: true,
+ },
+}
+
+var _ = UserDescribeSchema
+
+func UserDescriptionToSchema(userDetails sdk.UserDetails) []map[string]any {
+ userDetailsSchema := make(map[string]any)
+ if userDetails.Name != nil {
+ userDetailsSchema["name"] = userDetails.Name.Value
+ }
+ if userDetails.Comment != nil {
+ userDetailsSchema["comment"] = userDetails.Comment.Value
+ }
+ if userDetails.DisplayName != nil {
+ userDetailsSchema["display_name"] = userDetails.DisplayName.Value
+ }
+ if userDetails.LoginName != nil {
+ userDetailsSchema["login_name"] = userDetails.LoginName.Value
+ }
+ if userDetails.FirstName != nil {
+ userDetailsSchema["first_name"] = userDetails.FirstName.Value
+ }
+ if userDetails.MiddleName != nil {
+ userDetailsSchema["middle_name"] = userDetails.MiddleName.Value
+ }
+ if userDetails.LastName != nil {
+ userDetailsSchema["last_name"] = userDetails.LastName.Value
+ }
+ if userDetails.Email != nil {
+ userDetailsSchema["email"] = userDetails.Email.Value
+ }
+ if userDetails.Password != nil {
+ userDetailsSchema["password"] = userDetails.Password.Value
+ }
+ if userDetails.MustChangePassword != nil {
+ userDetailsSchema["must_change_password"] = userDetails.MustChangePassword.Value
+ }
+ if userDetails.Disabled != nil {
+ userDetailsSchema["disabled"] = userDetails.Disabled.Value
+ }
+ if userDetails.SnowflakeLock != nil {
+ userDetailsSchema["snowflake_lock"] = userDetails.SnowflakeLock.Value
+ }
+ if userDetails.SnowflakeSupport != nil {
+ userDetailsSchema["snowflake_support"] = userDetails.SnowflakeSupport.Value
+ }
+ if userDetails.DaysToExpiry != nil && userDetails.DaysToExpiry.Value != nil {
+ userDetailsSchema["days_to_expiry"] = *userDetails.DaysToExpiry.Value
+ }
+ if userDetails.MinsToUnlock != nil && userDetails.MinsToUnlock.Value != nil {
+ userDetailsSchema["mins_to_unlock"] = *userDetails.MinsToUnlock.Value
+ }
+ if userDetails.DefaultWarehouse != nil {
+ userDetailsSchema["default_warehouse"] = userDetails.DefaultWarehouse.Value
+ }
+ if userDetails.DefaultNamespace != nil {
+ userDetailsSchema["default_namespace"] = userDetails.DefaultNamespace.Value
+ }
+ if userDetails.DefaultRole != nil {
+ userDetailsSchema["default_role"] = userDetails.DefaultRole.Value
+ }
+ if userDetails.DefaultSecondaryRoles != nil {
+ userDetailsSchema["default_secondary_roles"] = userDetails.DefaultSecondaryRoles.Value
+ }
+ if userDetails.ExtAuthnDuo != nil {
+ userDetailsSchema["ext_authn_duo"] = userDetails.ExtAuthnDuo.Value
+ }
+ if userDetails.ExtAuthnUid != nil {
+ userDetailsSchema["ext_authn_uid"] = userDetails.ExtAuthnUid.Value
+ }
+ if userDetails.MinsToBypassMfa != nil && userDetails.MinsToBypassMfa.Value != nil {
+ userDetailsSchema["mins_to_bypass_mfa"] = userDetails.MinsToBypassMfa.Value
+ }
+ if userDetails.MinsToBypassNetworkPolicy != nil && userDetails.MinsToBypassNetworkPolicy.Value != nil {
+ userDetailsSchema["mins_to_bypass_network_policy"] = userDetails.MinsToBypassNetworkPolicy.Value
+ }
+ if userDetails.RsaPublicKey != nil {
+ userDetailsSchema["rsa_public_key"] = userDetails.RsaPublicKey.Value
+ }
+ if userDetails.RsaPublicKeyFp != nil {
+ userDetailsSchema["rsa_public_key_fp"] = userDetails.RsaPublicKeyFp.Value
+ }
+ if userDetails.RsaPublicKey2 != nil {
+ userDetailsSchema["rsa_public_key2"] = userDetails.RsaPublicKey2.Value
+ }
+ if userDetails.RsaPublicKey2Fp != nil {
+ userDetailsSchema["rsa_public_key2_fp"] = userDetails.RsaPublicKey2Fp.Value
+ }
+ if userDetails.PasswordLastSetTime != nil {
+ userDetailsSchema["password_last_set_time"] = userDetails.PasswordLastSetTime.Value
+ }
+ if userDetails.CustomLandingPageUrl != nil {
+ userDetailsSchema["custom_landing_page_url"] = userDetails.CustomLandingPageUrl.Value
+ }
+ if userDetails.CustomLandingPageUrlFlushNextUiLoad != nil {
+ userDetailsSchema["custom_landing_page_url_flush_next_ui_load"] = userDetails.CustomLandingPageUrlFlushNextUiLoad.Value
+ }
+ return []map[string]any{
+ userDetailsSchema,
+ }
+}
+
+var _ = UserDescriptionToSchema
diff --git a/pkg/schemas/user_ext.go b/pkg/schemas/user_ext.go
new file mode 100644
index 0000000000..2b261bb08f
--- /dev/null
+++ b/pkg/schemas/user_ext.go
@@ -0,0 +1,17 @@
+package schemas
+
+// This init is currently necessary to add sensitiveness to schemas which are generated (without such an underlying functionality yet).
+func init() {
+ // show output does not contain password or middle_name
+ ShowUserSchema["login_name"].Sensitive = true
+ ShowUserSchema["first_name"].Sensitive = true
+ ShowUserSchema["last_name"].Sensitive = true
+ ShowUserSchema["email"].Sensitive = true
+
+ UserDescribeSchema["login_name"].Sensitive = true
+ UserDescribeSchema["first_name"].Sensitive = true
+ UserDescribeSchema["middle_name"].Sensitive = true
+ UserDescribeSchema["last_name"].Sensitive = true
+ UserDescribeSchema["email"].Sensitive = true
+ UserDescribeSchema["password"].Sensitive = true
+}
diff --git a/templates/data-sources/users.md.tmpl b/templates/data-sources/users.md.tmpl
new file mode 100644
index 0000000000..d3ff8d9c6c
--- /dev/null
+++ b/templates/data-sources/users.md.tmpl
@@ -0,0 +1,24 @@
+---
+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 data source 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 data source if needed. Any errors reported will be resolved with a higher priority. We encourage checking this data source 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/data-sources/%s/data-source.tf" .Name)}}
+{{- end }}
+
+{{ .SchemaMarkdown | trimspace }}