Skip to content

Commit

Permalink
feat: Add a new account roles data source (#3257)
Browse files Browse the repository at this point in the history
<!-- Feel free to delete comments as you fill this in -->
- add a new data source for account rolers to be consistent with
resources
- prove that database role with the same prefix is not in the output of
SHOW ROLES
- deprecate the `roles` data source (it will be removed in v1-ready
branch)
<!-- summary of changes -->

## Test Plan
<!-- detail ways in which this PR has been tested or needs to be tested
-->
* [x] acceptance tests
<!-- add more below if you think they are relevant -->
* [ ] …

## References
<!-- issues documentation links, etc  -->
https://docs.snowflake.com/en/sql-reference/sql/show-roles
  • Loading branch information
sfc-gh-jmichalak authored Dec 12, 2024
1 parent c4f1e8f commit b3d6b9e
Show file tree
Hide file tree
Showing 25 changed files with 450 additions and 25 deletions.
20 changes: 20 additions & 0 deletions MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,26 @@ across different versions.
## v0.99.0 ➞ v0.100.0

### *(new feature)* Account role data source
Added a new `snowflake_account_roles` data source for account roles. Now it reflects It's based on `snowflake_roles` data source.
`account_roles` field now organizes output of show under `show_output` field.

Before:
```terraform
output "simple_output" {
value = data.snowflake_roles.test.roles[0].show_output[0].name
}
```
After:
```terraform
output "simple_output" {
value = data.snowflake_account_roles.test.account_roles[0].show_output[0].name
}
```

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

### snowflake_account_parameter resource changes

#### *(behavior change)* resource deletion
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 @@ -12,6 +12,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>

Data source 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 @@ -371,4 +371,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: 1 addition & 2 deletions docs/resources/database.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ description: |-

!> **Note** The provider does not detect external changes on database type. In this case, remove the database of wrong type manually with `terraform destroy` and recreate the resource. It will be addressed in the future.

!> **Note** A database cannot be dropped successfully if it contains network rule-network policy associations. The error looks like `098507 (2BP01): Cannot drop database DATABASE as it includes network rule - policy associations.
`. Currently, the provider does not unassign such objects automatically. Before dropping the resource, first unassign the network rule from the relevant objects. See [guide](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/guides/unassigning_policies) for more details.
!> **Note** A database cannot be dropped successfully if it contains network rule-network policy associations. The error looks like `098507 (2BP01): Cannot drop database DATABASE as it includes network rule - policy associations.`. Currently, the provider does not unassign such objects automatically. Before dropping the resource, first unassign the network rule from the relevant objects. See [guide](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/guides/unassigning_policies) for more details.

# snowflake_database (Resource)

Expand Down
3 changes: 1 addition & 2 deletions docs/resources/secondary_database.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ description: |-

!> **Note** The provider does not detect external changes on database type. In this case, remove the database of wrong type manually with `terraform destroy` and recreate the resource. It will be addressed in the future.

!> **Note** A database cannot be dropped successfully if it contains network rule-network policy associations. The error looks like `098507 (2BP01): Cannot drop database DATABASE as it includes network rule - policy associations.
`. Currently, the provider does not unassign such objects automatically. Before dropping the resource, first unassign the network rule from the relevant objects. See [guide](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/guides/unassigning_policies) for more details.
!> **Note** A database cannot be dropped successfully if it contains network rule-network policy associations. The error looks like `098507 (2BP01): Cannot drop database DATABASE as it includes network rule - policy associations.`. Currently, the provider does not unassign such objects automatically. Before dropping the resource, first unassign the network rule from the relevant objects. See [guide](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/guides/unassigning_policies) for more details.

# snowflake_secondary_database (Resource)

Expand Down
3 changes: 1 addition & 2 deletions docs/resources/shared_database.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@ description: |-

!> **Note** The provider does not detect external changes on database type. In this case, remove the database of wrong type manually with `terraform destroy` and recreate the resource. It will be addressed in the future.

!> **Note** A database cannot be dropped successfully if it contains network rule-network policy associations. The error looks like `098507 (2BP01): Cannot drop database DATABASE as it includes network rule - policy associations.
`. Currently, the provider does not unassign such objects automatically. Before dropping the resource, first unassign the network rule from the relevant objects. See [guide](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/guides/unassigning_policies) for more details.
!> **Note** A database cannot be dropped successfully if it contains network rule-network policy associations. The error looks like `098507 (2BP01): Cannot drop database DATABASE as it includes network rule - policy associations.`. Currently, the provider does not unassign such objects automatically. Before dropping the resource, first unassign the network rule from the relevant objects. See [guide](https://registry.terraform.io/providers/Snowflake-Labs/snowflake/latest/docs/guides/unassigning_policies) for more details.

# snowflake_shared_database (Resource)

Expand Down
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

0 comments on commit b3d6b9e

Please sign in to comment.