Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add a new account roles data source #3257

Merged
merged 11 commits into from
Dec 12, 2024
3 changes: 3 additions & 0 deletions MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ across different versions.

## v0.99.0 ➞ v0.100.0

### snowflake_roles data source deprecation
`snowflake_roles` is now deprecated in favor of `snowflake_account_roles` with the same schema and behavior. It will be removed with the v1 release. Please adjust your configuration files.

### snowflake_tag_association resource changes
#### *(behavior change)* new id format
In order to provide more functionality for tagging objects, we have changed the resource id from `"TAG_DATABASE"."TAG_SCHEMA"."TAG_NAME"` to `"TAG_DATABASE"."TAG_SCHEMA"."TAG_NAME"|TAG_VALUE|OBJECT_TYPE`. This allows to group tags associations per tag ID, tag value and object type in one resource.
Expand Down
99 changes: 99 additions & 0 deletions docs/data-sources/account_roles.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
---
page_title: "snowflake_account_roles Data Source - terraform-provider-snowflake"
subcategory: ""
description: |-
Data source used to get details of filtered account roles. Filtering is aligned with the current possibilities for SHOW ROLES https://docs.snowflake.com/en/sql-reference/sql/show-roles query (like and in_class are all supported). The results of SHOW are encapsulated in one output collection.
---

# snowflake_account_roles (Data Source)

Data source used to get details of filtered account roles. Filtering is aligned with the current possibilities for [SHOW ROLES](https://docs.snowflake.com/en/sql-reference/sql/show-roles) query (`like` and `in_class` are all supported). The results of SHOW are encapsulated in one output collection.

## Example Usage

```terraform
# Simple usage
data "snowflake_account_roles" "simple" {
}

output "simple_output" {
value = data.snowflake_account_roles.simple.roles
}

# Filtering (like)
data "snowflake_account_roles" "like" {
like = "role-name"
}

output "like_output" {
value = data.snowflake_account_roles.like.roles
}

# Filtering (in class)
data "snowflake_account_roles" "in_class" {
in_class = "SNOWFLAKE.CORE.BUDGET"
}

output "in_class_output" {
value = data.snowflake_account_roles.in_class.roles
}

# Ensure the number of roles is equal to at least one element (with the use of postcondition)
data "snowflake_account_roles" "assert_with_postcondition" {
like = "role-name-%"
lifecycle {
postcondition {
condition = length(self.roles) > 0
error_message = "there should be at least one role"
}
}
}

# Ensure the number of roles is equal to at exactly one element (with the use of check block)
check "role_check" {
data "snowflake_account_roles" "assert_with_check_block" {
like = "role-name"
}

assert {
condition = length(data.snowflake_account_roles.assert_with_check_block.roles) == 1
error_message = "Roles filtered by '${data.snowflake_account_roles.assert_with_check_block.like}' returned ${length(data.snowflake_account_roles.assert_with_check_block.roles)} roles where one was expected"
}
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Optional

- `in_class` (String) Filters the SHOW GRANTS output by class name.
- `like` (String) Filters the output with **case-insensitive** pattern, with support for SQL wildcard characters (`%` and `_`).

### Read-Only

- `account_roles` (List of Object) Holds the aggregated output of all account role details queries. (see [below for nested schema](#nestedatt--account_roles))
- `id` (String) The ID of this resource.

<a id="nestedatt--account_roles"></a>
### Nested Schema for `account_roles`

Read-Only:

- `show_output` (List of Object) (see [below for nested schema](#nestedobjatt--account_roles--show_output))

<a id="nestedobjatt--account_roles--show_output"></a>
### Nested Schema for `account_roles.show_output`

Read-Only:

- `assigned_to_users` (Number)
- `comment` (String)
- `created_on` (String)
- `granted_roles` (Number)
- `granted_to_roles` (Number)
- `is_current` (Boolean)
- `is_default` (Boolean)
- `is_inherited` (Boolean)
- `name` (String)
- `owner` (String)
2 changes: 1 addition & 1 deletion docs/data-sources/role.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ description: |-

# snowflake_role (Data Source)

~> **Deprecation** This resource is deprecated and will be removed in a future major version release. Please use [snowflake_roles](./roles) instead. <deprecation>
~> **Deprecation** This resource is deprecated and will be removed in a future major version release. Please use [snowflake_account_roles](./account_roles) instead. <deprecation>

## Example Usage

Expand Down
2 changes: 2 additions & 0 deletions docs/data-sources/roles.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ description: |-

# snowflake_roles (Data Source)

~> **Deprecation** This resource is deprecated and will be removed in a future major version release. Please use [snowflake_account_roles](./account_roles) instead. <deprecation>

Datasource used to get details of filtered roles. Filtering is aligned with the current possibilities for [SHOW ROLES](https://docs.snowflake.com/en/sql-reference/sql/show-roles) query (`like` and `in_class` are all supported). The results of SHOW are encapsulated in one output collection.

## Example Usage
Expand Down
3 changes: 2 additions & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -370,4 +370,5 @@ provider "snowflake" {

## Currently deprecated datasources

- [snowflake_role](./docs/data-sources/role) - use [snowflake_roles](./docs/data-sources/roles) instead
- [snowflake_role](./docs/data-sources/role) - use [snowflake_account_roles](./docs/data-sources/account_roles) instead
- [snowflake_roles](./docs/data-sources/roles) - use [snowflake_account_roles](./docs/data-sources/account_roles) instead
3 changes: 2 additions & 1 deletion examples/additional/deprecated_datasources.MD
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
## Currently deprecated datasources

- [snowflake_role](./docs/data-sources/role) - use [snowflake_roles](./docs/data-sources/roles) instead
- [snowflake_role](./docs/data-sources/role) - use [snowflake_account_roles](./docs/data-sources/account_roles) instead
- [snowflake_roles](./docs/data-sources/roles) - use [snowflake_account_roles](./docs/data-sources/account_roles) instead
48 changes: 48 additions & 0 deletions examples/data-sources/snowflake_account_roles/data-source.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Simple usage
data "snowflake_account_roles" "simple" {
}

output "simple_output" {
value = data.snowflake_account_roles.simple.roles
}

# Filtering (like)
data "snowflake_account_roles" "like" {
like = "role-name"
}

output "like_output" {
value = data.snowflake_account_roles.like.roles
}

# Filtering (in class)
data "snowflake_account_roles" "in_class" {
in_class = "SNOWFLAKE.CORE.BUDGET"
}

output "in_class_output" {
value = data.snowflake_account_roles.in_class.roles
}

# Ensure the number of roles is equal to at least one element (with the use of postcondition)
data "snowflake_account_roles" "assert_with_postcondition" {
like = "role-name-%"
lifecycle {
postcondition {
condition = length(self.roles) > 0
error_message = "there should be at least one role"
}
}
}

# Ensure the number of roles is equal to at exactly one element (with the use of check block)
check "role_check" {
data "snowflake_account_roles" "assert_with_check_block" {
like = "role-name"
}

assert {
condition = length(data.snowflake_account_roles.assert_with_check_block.roles) == 1
error_message = "Roles filtered by '${data.snowflake_account_roles.assert_with_check_block.like}' returned ${length(data.snowflake_account_roles.assert_with_check_block.roles)} roles where one was expected"
}
}
1 change: 1 addition & 0 deletions pkg/acceptance/bettertestspoc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,7 @@ func (w *WarehouseDatasourceShowOutputAssert) IsEmpty() {
- generate assertions checking that time is not empty - we often do not compare time fields by value, but check if they are set
- utilize `ContainsExactlyInAnyOrder` function in `pkg/acceptance/bettertestspoc/assert/commons.go` to create asserts on collections that are order independent
- Additional asserts for sets and lists that wouldn't rely on the order of items saved to the state (SNOW-1706544)
- this should also support nested sets and lists (see `accountRolesDataSourceContainsRole` for example)
- support generating provider config and use generated configs in `pkg/provider/provider_acceptance_test.go`
- add config builders for other block types (Variable, Output, Locals, Module, Terraform)
- add provider to resource/datasource models (use in the grant_ownership_acceptance_test)
Expand Down
94 changes: 94 additions & 0 deletions pkg/datasources/account_roles.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package datasources

import (
"context"
"fmt"

"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/datasources"

"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/internal/provider"

"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 accountRolesSchema = map[string]*schema.Schema{
"like": likeSchema,
"in_class": {
Type: schema.TypeString,
Optional: true,
ValidateDiagFunc: resources.IsValidIdentifier[sdk.SchemaObjectIdentifier](),
Description: "Filters the SHOW GRANTS output by class name.",
},
"account_roles": {
Type: schema.TypeList,
Computed: true,
Description: "Holds the aggregated output of all account role details queries.",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
resources.ShowOutputAttributeName: {
Type: schema.TypeList,
Computed: true,
Description: "Holds the output of SHOW ROLES.",
Elem: &schema.Resource{
Schema: schemas.ShowRoleSchema,
},
},
},
},
},
}

func AccountRoles() *schema.Resource {
return &schema.Resource{
ReadContext: TrackingReadWrapper(datasources.AccountRoles, ReadAccountRoles),
Schema: accountRolesSchema,
Description: "Data source used to get details of filtered account roles. Filtering is aligned with the current possibilities for [SHOW ROLES](https://docs.snowflake.com/en/sql-reference/sql/show-roles) query (`like` and `in_class` are all supported). The results of SHOW are encapsulated in one output collection.",
}
}

func ReadAccountRoles(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
client := meta.(*provider.Context).Client

req := sdk.NewShowRoleRequest()

handleLike(d, &req.Like)

if className, ok := d.GetOk("in_class"); ok {
req.WithInClass(sdk.RolesInClass{
Class: sdk.NewSchemaObjectIdentifierFromFullyQualifiedName(className.(string)),
})
}

roles, err := client.Roles.Show(ctx, req)
if err != nil {
return diag.Diagnostics{
diag.Diagnostic{
Severity: diag.Error,
Summary: "Failed to show account roles",
Detail: fmt.Sprintf("Error: %s", err),
},
}
}

d.SetId("account_roles_read")

flattenedAccountRoles := make([]map[string]any, len(roles))
for i, role := range roles {
role := role
flattenedAccountRoles[i] = map[string]any{
resources.ShowOutputAttributeName: []map[string]any{schemas.RoleToSchema(&role)},
}
}

err = d.Set("account_roles", flattenedAccountRoles)
if err != nil {
return diag.FromErr(err)
}

return nil
}
Loading
Loading