-
Notifications
You must be signed in to change notification settings - Fork 398
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: create snowflake_role_ownership_grant resource (#917)
- Loading branch information
1 parent
96152d7
commit 17de20f
Showing
10 changed files
with
441 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
--- | ||
# generated by https://github.com/hashicorp/terraform-plugin-docs | ||
page_title: "snowflake_role_ownership_grant Resource - terraform-provider-snowflake" | ||
subcategory: "" | ||
description: |- | ||
--- | ||
|
||
# snowflake_role_ownership_grant (Resource) | ||
|
||
|
||
|
||
|
||
|
||
<!-- schema generated by tfplugindocs --> | ||
## Schema | ||
|
||
### Required | ||
|
||
- **on_role_name** (String) The name of the role ownership is granted on. | ||
- **to_role_name** (String) The name of the role to grant ownership. Please ensure that the role that terraform is using is granted access. | ||
|
||
### Optional | ||
|
||
- **current_grants** (String) Specifies whether to remove or transfer all existing outbound privileges on the object when ownership is transferred to a new role. | ||
- **id** (String) The ID of this resource. | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
terraform import snowflake_role_ownership_grant.example rolename |
24 changes: 24 additions & 0 deletions
24
examples/resources/snowflake_role_ownership_grants/resource.tf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
resource "snowflake_role" "role" { | ||
name = "rking_test_role" | ||
comment = "for testing" | ||
} | ||
|
||
resource "snowflake_role" "other_role" { | ||
name = "rking_test_role2" | ||
} | ||
|
||
# ensure the Terraform user inherits ownership privileges for the rking_test_role role | ||
# otherwise Terraform will fail to destroy the rking_test_role2 role due to insufficient privileges | ||
resource "snowflake_role_grants" "grants" { | ||
role_name = snowflake_role.role.name | ||
|
||
roles = [ | ||
"ACCOUNTADMIN", | ||
] | ||
} | ||
|
||
resource "snowflake_role_ownership_grant" "grant" { | ||
on_role_name = snowflake_role.role.name | ||
to_role_name = snowflake_role.other_role.name | ||
current_grants = "COPY" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
package resources | ||
|
||
import ( | ||
"database/sql" | ||
"fmt" | ||
"log" | ||
"strings" | ||
|
||
"github.com/chanzuckerberg/terraform-provider-snowflake/pkg/snowflake" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" | ||
) | ||
|
||
var roleOwnershipGrantSchema = map[string]*schema.Schema{ | ||
"on_role_name": { | ||
Type: schema.TypeString, | ||
Elem: &schema.Schema{Type: schema.TypeString}, | ||
Required: true, | ||
Description: "The name of the role ownership is granted on.", | ||
ValidateFunc: func(val interface{}, key string) ([]string, []error) { | ||
return snowflake.ValidateIdentifier(val) | ||
}, | ||
}, | ||
"to_role_name": { | ||
Type: schema.TypeString, | ||
Elem: &schema.Schema{Type: schema.TypeString}, | ||
Required: true, | ||
Description: "The name of the role to grant ownership. Please ensure that the role that terraform is using is granted access.", | ||
ValidateFunc: func(val interface{}, key string) ([]string, []error) { | ||
return snowflake.ValidateIdentifier(val) | ||
}, | ||
}, | ||
"current_grants": { | ||
Type: schema.TypeString, | ||
Elem: &schema.Schema{Type: schema.TypeString}, | ||
Optional: true, | ||
Description: "Specifies whether to remove or transfer all existing outbound privileges on the object when ownership is transferred to a new role.", | ||
Default: "COPY", | ||
ValidateFunc: validation.StringInSlice([]string{ | ||
"COPY", | ||
"REVOKE", | ||
}, true), | ||
}, | ||
} | ||
|
||
func RoleOwnershipGrant() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: CreateRoleOwnershipGrant, | ||
Read: ReadRoleOwnershipGrant, | ||
Delete: DeleteRoleOwnershipGrant, | ||
Update: UpdateRoleOwnershipGrant, | ||
Schema: roleOwnershipGrantSchema, | ||
Importer: &schema.ResourceImporter{ | ||
StateContext: schema.ImportStatePassthroughContext, | ||
}, | ||
} | ||
} | ||
|
||
func CreateRoleOwnershipGrant(d *schema.ResourceData, meta interface{}) error { | ||
db := meta.(*sql.DB) | ||
onRoleName := d.Get("on_role_name").(string) | ||
toRoleName := d.Get("to_role_name").(string) | ||
currentGrants := d.Get("current_grants").(string) | ||
|
||
g := snowflake.RoleOwnershipGrant(onRoleName, currentGrants) | ||
err := snowflake.Exec(db, g.Role(toRoleName).Grant()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
d.SetId(fmt.Sprintf(`%s|%s|%s`, onRoleName, toRoleName, currentGrants)) | ||
|
||
return ReadRoleOwnershipGrant(d, meta) | ||
} | ||
|
||
func ReadRoleOwnershipGrant(d *schema.ResourceData, meta interface{}) error { | ||
db := meta.(*sql.DB) | ||
log.Println(d.Id()) | ||
onRoleName := strings.Split(d.Id(), "|")[0] | ||
currentGrants := strings.Split(d.Id(), "|")[2] | ||
|
||
stmt := fmt.Sprintf("SHOW ROLES LIKE '%s'", onRoleName) | ||
row := snowflake.QueryRow(db, stmt) | ||
|
||
grant, err := snowflake.ScanRoleOwnershipGrant(row) | ||
if err == sql.ErrNoRows { | ||
// If not found, mark resource to be removed from statefile during apply or refresh | ||
log.Printf("[DEBUG] role (%s) not found", d.Id()) | ||
d.SetId("") | ||
return nil | ||
} | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if onRoleName != grant.Name.String { | ||
return fmt.Errorf("no role found like '%s'", onRoleName) | ||
} | ||
|
||
grant.Name.String = strings.TrimPrefix(grant.Name.String, `"`) | ||
grant.Name.String = strings.TrimSuffix(grant.Name.String, `"`) | ||
err = d.Set("on_role_name", grant.Name.String) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
grant.Owner.String = strings.TrimPrefix(grant.Owner.String, `"`) | ||
grant.Owner.String = strings.TrimSuffix(grant.Owner.String, `"`) | ||
err = d.Set("to_role_name", grant.Owner.String) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
err = d.Set("current_grants", currentGrants) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func UpdateRoleOwnershipGrant(d *schema.ResourceData, meta interface{}) error { | ||
db := meta.(*sql.DB) | ||
onRoleName := d.Get("on_role_name").(string) | ||
toRoleName := d.Get("to_role_name").(string) | ||
currentGrants := d.Get("current_grants").(string) | ||
|
||
d.SetId(fmt.Sprintf(`%s|%s|%s`, onRoleName, toRoleName, currentGrants)) | ||
|
||
g := snowflake.RoleOwnershipGrant(onRoleName, currentGrants) | ||
err := snowflake.Exec(db, g.Role(toRoleName).Grant()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
return ReadRoleOwnershipGrant(d, meta) | ||
} | ||
|
||
func DeleteRoleOwnershipGrant(d *schema.ResourceData, meta interface{}) error { | ||
db := meta.(*sql.DB) | ||
onRoleName := d.Get("on_role_name").(string) | ||
currentGrants := d.Get("current_grants").(string) | ||
|
||
g := snowflake.RoleOwnershipGrant(onRoleName, currentGrants) | ||
err := snowflake.Exec(db, g.Role("ACCOUNTADMIN").Revoke()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
d.SetId("") | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package resources_test | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" | ||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" | ||
) | ||
|
||
func TestAccRoleOwnershipGrant_defaults(t *testing.T) { | ||
onRoleName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha) | ||
toRoleName := acctest.RandStringFromCharSet(10, acctest.CharSetAlpha) | ||
|
||
resource.ParallelTest(t, resource.TestCase{ | ||
Providers: providers(), | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: roleOwnershipGrantConfig(onRoleName, toRoleName), | ||
Check: resource.ComposeTestCheckFunc( | ||
resource.TestCheckResourceAttr("snowflake_role_ownership_grant.grant", "on_role_name", onRoleName), | ||
resource.TestCheckResourceAttr("snowflake_role_ownership_grant.grant", "to_role_name", toRoleName), | ||
resource.TestCheckResourceAttr("snowflake_role_ownership_grant.grant", "current_grants", "COPY"), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func roleOwnershipGrantConfig(onRoleName, toRoleName string) string { | ||
return fmt.Sprintf(` | ||
resource "snowflake_role" "role" { | ||
name = "%v" | ||
} | ||
resource "snowflake_role" "other_role" { | ||
name = "%v" | ||
} | ||
resource "snowflake_role_grants" "grants" { | ||
role_name = snowflake_role.role.name | ||
roles = [ | ||
"ACCOUNTADMIN", | ||
] | ||
} | ||
resource "snowflake_role_ownership_grant" "grant" { | ||
on_role_name = snowflake_role.role.name | ||
to_role_name = snowflake_role.other_role.name | ||
current_grants = "COPY" | ||
} | ||
`, onRoleName, toRoleName) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package resources_test | ||
|
||
import ( | ||
"database/sql" | ||
"testing" | ||
|
||
sqlmock "github.com/DATA-DOG/go-sqlmock" | ||
"github.com/chanzuckerberg/terraform-provider-snowflake/pkg/provider" | ||
"github.com/chanzuckerberg/terraform-provider-snowflake/pkg/resources" | ||
. "github.com/chanzuckerberg/terraform-provider-snowflake/pkg/testhelpers" | ||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
func TestRoleOwnershipGrant(t *testing.T) { | ||
r := require.New(t) | ||
err := resources.RoleOwnershipGrant().InternalValidate(provider.Provider().Schema, true) | ||
r.NoError(err) | ||
} | ||
|
||
func TestRoleOwnershipGrantCreate(t *testing.T) { | ||
r := require.New(t) | ||
|
||
d := roleOwnershipGrant(t, "good_name", map[string]interface{}{ | ||
"on_role_name": "good_name", | ||
"to_role_name": "other_good_name", | ||
"current_grants": "COPY", | ||
}) | ||
|
||
WithMockDb(t, func(db *sql.DB, mock sqlmock.Sqlmock) { | ||
mock.ExpectExec(`GRANT OWNERSHIP ON ROLE "good_name" TO ROLE "other_good_name" COPY CURRENT GRANTS`).WillReturnResult(sqlmock.NewResult(1, 1)) | ||
expectReadRoleOwnershipGrant(mock) | ||
err := resources.CreateRoleOwnershipGrant(d, db) | ||
r.NoError(err) | ||
}) | ||
} | ||
|
||
func TestRoleOwnershipGrantRead(t *testing.T) { | ||
r := require.New(t) | ||
|
||
d := roleOwnershipGrant(t, "good_name|other_good_name|COPY", map[string]interface{}{ | ||
"on_role_name": "good_name", | ||
"to_role_name": "other_good_name", | ||
"current_grants": "COPY", | ||
}) | ||
|
||
WithMockDb(t, func(db *sql.DB, mock sqlmock.Sqlmock) { | ||
expectReadRoleOwnershipGrant(mock) | ||
err := resources.ReadRoleOwnershipGrant(d, db) | ||
r.NoError(err) | ||
}) | ||
} | ||
|
||
func expectReadRoleOwnershipGrant(mock sqlmock.Sqlmock) { | ||
rows := sqlmock.NewRows([]string{ | ||
"created_on", | ||
"name", | ||
"is_default", | ||
"is_current", | ||
"is_inherited", | ||
"assigned_to_users", | ||
"granted_to_roles", | ||
"granted_roles", | ||
"owner", | ||
"comment", | ||
}).AddRow("_", "good_name", "", "", "", "", "", "", "other_good_name", "") | ||
mock.ExpectQuery(`SHOW ROLES LIKE 'good_name'`).WillReturnRows(rows) | ||
} | ||
|
||
func TestRoleOwnershipGrantDelete(t *testing.T) { | ||
r := require.New(t) | ||
|
||
d := roleOwnershipGrant(t, "good_name|other_good_name|COPY", map[string]interface{}{ | ||
"on_role_name": "good_name", | ||
"to_role_name": "other_good_name", | ||
"current_grants": "COPY", | ||
}) | ||
|
||
WithMockDb(t, func(db *sql.DB, mock sqlmock.Sqlmock) { | ||
|
||
mock.ExpectExec(`GRANT OWNERSHIP ON ROLE "good_name" TO ROLE "ACCOUNTADMIN" COPY CURRENT GRANTS`).WillReturnResult(sqlmock.NewResult(1, 1)) | ||
err := resources.DeleteRoleOwnershipGrant(d, db) | ||
r.NoError(err) | ||
}) | ||
} |
Oops, something went wrong.