diff --git a/pkg/resources/account_grant.go b/pkg/resources/account_grant.go index 78db4e1b1e..a7d5aa42e4 100644 --- a/pkg/resources/account_grant.go +++ b/pkg/resources/account_grant.go @@ -74,6 +74,7 @@ func AccountGrant() *TerraformGrantResource { func CreateAccountGrant(d *schema.ResourceData, meta interface{}) error { priv := d.Get("privilege").(string) grantOption := d.Get("with_grant_option").(bool) + roles := expandStringList(d.Get("roles").(*schema.Set).List()) builder := snowflake.AccountGrant() @@ -86,6 +87,7 @@ func CreateAccountGrant(d *schema.ResourceData, meta interface{}) error { ResourceName: "ACCOUNT", Privilege: priv, GrantOption: grantOption, + Roles: roles, } dataIDInput, err := grantID.String() if err != nil { diff --git a/pkg/resources/account_grant_test.go b/pkg/resources/account_grant_test.go index ad3ce13fef..e65447b44f 100644 --- a/pkg/resources/account_grant_test.go +++ b/pkg/resources/account_grant_test.go @@ -45,7 +45,7 @@ func TestAccountGrantCreate(t *testing.T) { //lintignore:AT003 func TestAccountGrantRead(t *testing.T) { r := require.New(t) - d := accountGrant(t, "ACCOUNT|||MANAGE GRANTS|true", map[string]interface{}{ + d := accountGrant(t, "ACCOUNT|||MANAGE GRANTS||true", map[string]interface{}{ "privilege": "MANAGE GRANTS", "roles": []interface{}{"test-role-1", "test-role-2"}, "with_grant_option": true, @@ -63,7 +63,7 @@ func TestAccountGrantRead(t *testing.T) { func TestMonitorExecution(t *testing.T) { r := require.New(t) - d := accountGrant(t, "ACCOUNT|||MONITOR EXECUTION|true", map[string]interface{}{ + d := accountGrant(t, "ACCOUNT|||MONITOR EXECUTION||true", map[string]interface{}{ "privilege": "MONITOR EXECUTION", "roles": []interface{}{"test-role-1", "test-role-2"}, "with_grant_option": true, @@ -81,7 +81,7 @@ func TestMonitorExecution(t *testing.T) { func TestExecuteTask(t *testing.T) { r := require.New(t) - d := accountGrant(t, "ACCOUNT|||EXECUTE TASK|false", map[string]interface{}{ + d := accountGrant(t, "ACCOUNT|||EXECUTE TASK||false", map[string]interface{}{ "privilege": "EXECUTE TASK", "roles": []interface{}{"test-role-1", "test-role-2"}, "with_grant_option": false, @@ -110,7 +110,7 @@ func expectReadAccountGrant(mock sqlmock.Sqlmock) { func TestApplyMaskingPolicy(t *testing.T) { r := require.New(t) - d := accountGrant(t, "ACCOUNT|||APPLY MASKING POLICY|true", map[string]interface{}{ + d := accountGrant(t, "ACCOUNT|||APPLY MASKING POLICY||true", map[string]interface{}{ "privilege": "APPLY MASKING POLICY", "roles": []interface{}{"test-role-1", "test-role-2"}, "with_grant_option": true, diff --git a/pkg/resources/database_grant.go b/pkg/resources/database_grant.go index 1223b047d2..309765ac2d 100644 --- a/pkg/resources/database_grant.go +++ b/pkg/resources/database_grant.go @@ -36,6 +36,7 @@ var databaseGrantSchema = map[string]*schema.Schema{ Type: schema.TypeSet, Elem: &schema.Schema{Type: schema.TypeString}, Optional: true, + ForceNew: true, Description: "Grants privilege to these roles.", }, "shares": { @@ -77,6 +78,7 @@ func CreateDatabaseGrant(d *schema.ResourceData, meta interface{}) error { builder := snowflake.DatabaseGrant(dbName) priv := d.Get("privilege").(string) grantOption := d.Get("with_grant_option").(bool) + roles := expandStringList(d.Get("roles").(*schema.Set).List()) err := createGenericGrant(d, meta, builder) if err != nil { @@ -87,6 +89,7 @@ func CreateDatabaseGrant(d *schema.ResourceData, meta interface{}) error { ResourceName: dbName, Privilege: priv, GrantOption: grantOption, + Roles: roles, } dataIDInput, err := grant.String() if err != nil { diff --git a/pkg/resources/database_grant_test.go b/pkg/resources/database_grant_test.go index f5316514f8..6abfa6e5f1 100644 --- a/pkg/resources/database_grant_test.go +++ b/pkg/resources/database_grant_test.go @@ -48,7 +48,7 @@ func TestDatabaseGrantCreate(t *testing.T) { func TestDatabaseGrantRead(t *testing.T) { r := require.New(t) - d := databaseGrant(t, "test-database|||USAGE|false", map[string]interface{}{ + d := databaseGrant(t, "test-database|||USAGE||false", map[string]interface{}{ "database_name": "test-database", "privilege": "USAGE", "roles": []interface{}{}, diff --git a/pkg/resources/external_table_grant.go b/pkg/resources/external_table_grant.go index 11c1de5af7..b9a692fc5b 100644 --- a/pkg/resources/external_table_grant.go +++ b/pkg/resources/external_table_grant.go @@ -98,6 +98,7 @@ func CreateExternalTableGrant(d *schema.ResourceData, meta interface{}) error { priv := d.Get("privilege").(string) futureExternalTables := d.Get("on_future").(bool) grantOption := d.Get("with_grant_option").(bool) + roles := expandStringList(d.Get("roles").(*schema.Set).List()) if (externalTableName == "") && !futureExternalTables { return errors.New("external_table_name must be set unless on_future is true.") @@ -124,6 +125,7 @@ func CreateExternalTableGrant(d *schema.ResourceData, meta interface{}) error { ObjectName: externalTableName, Privilege: priv, GrantOption: grantOption, + Roles: roles, } dataIDInput, err := grant.String() if err != nil { diff --git a/pkg/resources/external_table_grant_test.go b/pkg/resources/external_table_grant_test.go index e69ee1b625..900c13c403 100644 --- a/pkg/resources/external_table_grant_test.go +++ b/pkg/resources/external_table_grant_test.go @@ -48,7 +48,7 @@ func TestExternalTableGrantCreate(t *testing.T) { func TestExternalTableGrantRead(t *testing.T) { r := require.New(t) - d := externalTableGrant(t, "test-db|PUBLIC|test-external-table|SELECT|false", map[string]interface{}{ + d := externalTableGrant(t, "test-db|PUBLIC|test-external-table|SELECT||false", map[string]interface{}{ "external_table_name": "test-external-table", "schema_name": "PUBLIC", "database_name": "test-db", diff --git a/pkg/resources/file_format_grant.go b/pkg/resources/file_format_grant.go index c16317f589..a17f40cced 100644 --- a/pkg/resources/file_format_grant.go +++ b/pkg/resources/file_format_grant.go @@ -90,6 +90,7 @@ func CreateFileFormatGrant(d *schema.ResourceData, meta interface{}) error { priv := d.Get("privilege").(string) futureFileFormats := d.Get("on_future").(bool) grantOption := d.Get("with_grant_option").(bool) + roles := expandStringList(d.Get("roles").(*schema.Set).List()) if (fileFormatName == "") && !futureFileFormats { return errors.New("file_format_name must be set unless on_future is true.") @@ -116,6 +117,7 @@ func CreateFileFormatGrant(d *schema.ResourceData, meta interface{}) error { ObjectName: fileFormatName, Privilege: priv, GrantOption: grantOption, + Roles: roles, } dataIDInput, err := grant.String() if err != nil { diff --git a/pkg/resources/file_format_grant_test.go b/pkg/resources/file_format_grant_test.go index e03587a6e6..0f6b09ec15 100644 --- a/pkg/resources/file_format_grant_test.go +++ b/pkg/resources/file_format_grant_test.go @@ -45,7 +45,7 @@ func TestFileFormatGrantCreate(t *testing.T) { func TestFileFormatGrantRead(t *testing.T) { r := require.New(t) - d := fileFormatGrant(t, "test-db|PUBLIC|test-file-format|USAGE|false", map[string]interface{}{ + d := fileFormatGrant(t, "test-db|PUBLIC|test-file-format|USAGE||false", map[string]interface{}{ "file_format_name": "test-file-format", "schema_name": "PUBLIC", "database_name": "test-db", diff --git a/pkg/resources/function_grant.go b/pkg/resources/function_grant.go index 3e80ec0ed3..d26d6154df 100644 --- a/pkg/resources/function_grant.go +++ b/pkg/resources/function_grant.go @@ -137,6 +137,7 @@ func CreateFunctionGrant(d *schema.ResourceData, meta interface{}) error { futureFunctions := d.Get("on_future").(bool) grantOption := d.Get("with_grant_option").(bool) arguments = d.Get("arguments").([]interface{}) + roles := expandStringList(d.Get("roles").(*schema.Set).List()) if (functionName == "") && !futureFunctions { return errors.New("function_name must be set unless on_future is true.") @@ -169,6 +170,7 @@ func CreateFunctionGrant(d *schema.ResourceData, meta interface{}) error { ObjectName: functionSignature, Privilege: priv, GrantOption: grantOption, + Roles: roles, } dataIDInput, err := grant.String() if err != nil { diff --git a/pkg/resources/function_grant_test.go b/pkg/resources/function_grant_test.go index 273b34d260..85fbf023ec 100644 --- a/pkg/resources/function_grant_test.go +++ b/pkg/resources/function_grant_test.go @@ -56,7 +56,7 @@ func TestFunctionGrantCreate(t *testing.T) { func TestFunctionGrantRead(t *testing.T) { r := require.New(t) - d := functionGrant(t, "test-db|PUBLIC|test-function(A ARRAY, B STRING):STRING|USAGE|false", map[string]interface{}{ + d := functionGrant(t, "test-db|PUBLIC|test-function(A ARRAY, B STRING):STRING|USAGE||false", map[string]interface{}{ "function_name": "test-function", "arguments": []interface{}{map[string]interface{}{ "name": "a", diff --git a/pkg/resources/grant_helpers.go b/pkg/resources/grant_helpers.go index 83d1056256..cf6701981f 100644 --- a/pkg/resources/grant_helpers.go +++ b/pkg/resources/grant_helpers.go @@ -81,17 +81,19 @@ type grantID struct { SchemaName string ObjectName string Privilege string + Roles []string GrantOption bool } // String() takes in a grantID object and returns a pipe-delimited string: -// resourceName|schemaName|ObjectName|Privilege|GrantOption +// resourceName|schemaName|ObjectName|Privilege|Roles|GrantOption func (gi *grantID) String() (string, error) { var buf bytes.Buffer csvWriter := csv.NewWriter(&buf) csvWriter.Comma = grantIDDelimiter grantOption := fmt.Sprintf("%v", gi.GrantOption) - dataIdentifiers := [][]string{{gi.ResourceName, gi.SchemaName, gi.ObjectName, gi.Privilege, grantOption}} + roles := strings.Join(gi.Roles, ",") + dataIdentifiers := [][]string{{gi.ResourceName, gi.SchemaName, gi.ObjectName, gi.Privilege, roles, grantOption}} err := csvWriter.WriteAll(dataIdentifiers) if err != nil { return "", err @@ -100,7 +102,7 @@ func (gi *grantID) String() (string, error) { return strGrantID, nil } -// grantIDFromString() takes in a pipe-delimited string: resourceName|schemaName|ObjectName|Privilege +// grantIDFromString() takes in a pipe-delimited string: resourceName|schemaName|ObjectName|Privilege|Roles // and returns a grantID object func grantIDFromString(stringID string) (*grantID, error) { reader := csv.NewReader(strings.NewReader(stringID)) @@ -113,12 +115,12 @@ func grantIDFromString(stringID string) (*grantID, error) { if len(lines) != 1 { return nil, fmt.Errorf("1 line per grant") } - if len(lines[0]) != 4 && len(lines[0]) != 5 { - return nil, fmt.Errorf("4 or 5 fields allowed") + if len(lines[0]) != 5 && len(lines[0]) != 6 { + return nil, fmt.Errorf("5 or 6 fields allowed") } grantOption := false - if len(lines[0]) == 5 && lines[0][4] == "true" { + if len(lines[0]) == 6 && lines[0][5] == "true" { grantOption = true } @@ -127,6 +129,7 @@ func grantIDFromString(stringID string) (*grantID, error) { SchemaName: lines[0][1], ObjectName: lines[0][2], Privilege: lines[0][3], + Roles: strings.Split(lines[0][4], ","), GrantOption: grantOption, } return grantResult, nil @@ -176,7 +179,7 @@ func createGenericGrant(d *schema.ResourceData, meta interface{}, builder snowfl func readGenericGrant( d *schema.ResourceData, meta interface{}, - schema map[string]*schema.Schema, + grantSchema map[string]*schema.Schema, builder snowflake.GrantBuilder, futureObjects bool, validPrivileges PrivilegeSet) error { @@ -243,11 +246,13 @@ func readGenericGrant( } } + existingRoles := d.Get("roles").(*schema.Set) var roles, shares []string // Now see which roles have our privilege for roleName, privileges := range rolePrivileges { // Where priv is not all so it should match exactly - if privileges.hasString(priv) { + // Match to currently assigned roles or let everything through if no specific role grants + if privileges.hasString(priv) && (existingRoles.Contains(roleName) || existingRoles.Len() == 0) { roles = append(roles, roleName) } } @@ -269,7 +274,7 @@ func readGenericGrant( return err } - _, sharesOk := schema["shares"] + _, sharesOk := grantSchema["shares"] if sharesOk && !futureObjects { err = d.Set("shares", shares) if err != nil { diff --git a/pkg/resources/grant_helpers_internal_test.go b/pkg/resources/grant_helpers_internal_test.go index aa4b0e77a8..f7c1b91493 100644 --- a/pkg/resources/grant_helpers_internal_test.go +++ b/pkg/resources/grant_helpers_internal_test.go @@ -10,7 +10,7 @@ import ( func TestGrantIDFromString(t *testing.T) { r := require.New(t) // Vanilla without GrantOption - id := "database_name|schema|view_name|privilege" + id := "database_name|schema|view_name|privilege|test1,test2" grant, err := grantIDFromString(id) r.NoError(err) @@ -21,7 +21,7 @@ func TestGrantIDFromString(t *testing.T) { r.Equal(false, grant.GrantOption) // Vanilla with GrantOption - id = "database_name|schema|view_name|privilege|true" + id = "database_name|schema|view_name|privilege|test1,test2|true" grant, err = grantIDFromString(id) r.NoError(err) @@ -44,17 +44,17 @@ func TestGrantIDFromString(t *testing.T) { // Bad ID -- not enough fields id = "database|name-privilege" _, err = grantIDFromString(id) - r.Equal(fmt.Errorf("4 or 5 fields allowed"), err) + r.Equal(fmt.Errorf("5 or 6 fields allowed"), err) // Bad ID -- privilege in wrong area id = "database||name-privilege" _, err = grantIDFromString(id) - r.Equal(fmt.Errorf("4 or 5 fields allowed"), err) + r.Equal(fmt.Errorf("5 or 6 fields allowed"), err) // too many fields - id = "database_name|schema|view_name|privilege|false|2" + id = "database_name|schema|view_name|privilege|false|2|too-many" _, err = grantIDFromString(id) - r.Equal(fmt.Errorf("4 or 5 fields allowed"), err) + r.Equal(fmt.Errorf("5 or 6 fields allowed"), err) // 0 lines id = "" @@ -70,24 +70,24 @@ func TestGrantIDFromString(t *testing.T) { func TestGrantStruct(t *testing.T) { r := require.New(t) - // Vanilla grant := &grantID{ ResourceName: "database_name", SchemaName: "schema", ObjectName: "view_name", Privilege: "priv", + Roles: []string{"test1", "test2"}, GrantOption: true, } gID, err := grant.String() r.NoError(err) - r.Equal("database_name|schema|view_name|priv|true", gID) + r.Equal("database_name|schema|view_name|priv|test1,test2|true", gID) // Empty grant grant = &grantID{} gID, err = grant.String() r.NoError(err) - r.Equal("||||false", gID) + r.Equal("|||||false", gID) // Grant with extra delimiters grant = &grantID{ @@ -95,6 +95,7 @@ func TestGrantStruct(t *testing.T) { SchemaName: "schema|name", ObjectName: "view|name", Privilege: "priv", + Roles: []string{"test3", "test4"}, GrantOption: false, } gID, err = grant.String() @@ -105,5 +106,6 @@ func TestGrantStruct(t *testing.T) { r.Equal("schema|name", newGrant.SchemaName) r.Equal("view|name", newGrant.ObjectName) r.Equal("priv", newGrant.Privilege) + r.Equal([]string{"test3", "test4"}, newGrant.Roles) r.Equal(false, newGrant.GrantOption) } diff --git a/pkg/resources/integration_grant.go b/pkg/resources/integration_grant.go index de6bc25ef6..a81ce973b3 100644 --- a/pkg/resources/integration_grant.go +++ b/pkg/resources/integration_grant.go @@ -63,6 +63,8 @@ func CreateIntegrationGrant(d *schema.ResourceData, meta interface{}) error { w := d.Get("integration_name").(string) priv := d.Get("privilege").(string) grantOption := d.Get("with_grant_option").(bool) + roles := expandStringList(d.Get("roles").(*schema.Set).List()) + builder := snowflake.IntegrationGrant(w) err := createGenericGrant(d, meta, builder) @@ -74,6 +76,7 @@ func CreateIntegrationGrant(d *schema.ResourceData, meta interface{}) error { ResourceName: w, Privilege: priv, GrantOption: grantOption, + Roles: roles, } dataIDInput, err := grant.String() if err != nil { diff --git a/pkg/resources/integration_grant_test.go b/pkg/resources/integration_grant_test.go index 2e74f29c2b..5f0f18a8f3 100644 --- a/pkg/resources/integration_grant_test.go +++ b/pkg/resources/integration_grant_test.go @@ -45,7 +45,7 @@ func TestIntegrationGrantCreate(t *testing.T) { func TestIntegrationGrantRead(t *testing.T) { r := require.New(t) - d := integrationGrant(t, "test-integration|||IMPORTED PRIVILIGES|false", map[string]interface{}{ + d := integrationGrant(t, "test-integration|||IMPORTED PRIVILIGES||false", map[string]interface{}{ "integration_name": "test-integration", "privilege": "IMPORTED PRIVILIGES", "roles": []interface{}{"test-role-1", "test-role-2"}, diff --git a/pkg/resources/masking_policy_grant.go b/pkg/resources/masking_policy_grant.go index 592fecb735..eea280976b 100644 --- a/pkg/resources/masking_policy_grant.go +++ b/pkg/resources/masking_policy_grant.go @@ -81,6 +81,7 @@ func CreateMaskingPolicyGrant(d *schema.ResourceData, meta interface{}) error { schemaName := d.Get("schema_name").(string) priv := d.Get("privilege").(string) grantOption := d.Get("with_grant_option").(bool) + roles := expandStringList(d.Get("roles").(*schema.Set).List()) builder := snowflake.MaskingPolicyGrant(dbName, schemaName, maskingPolicyName) @@ -95,6 +96,7 @@ func CreateMaskingPolicyGrant(d *schema.ResourceData, meta interface{}) error { ObjectName: maskingPolicyName, Privilege: priv, GrantOption: grantOption, + Roles: roles, } dataIDInput, err := grant.String() if err != nil { diff --git a/pkg/resources/masking_policy_grant_test.go b/pkg/resources/masking_policy_grant_test.go index 1bb9a4a52b..19bcff88a7 100644 --- a/pkg/resources/masking_policy_grant_test.go +++ b/pkg/resources/masking_policy_grant_test.go @@ -44,7 +44,7 @@ func TestMaskingPolicyGrantCreate(t *testing.T) { func TestMaskingPolicyGrantRead(t *testing.T) { r := require.New(t) - d := maskingPolicyGrant(t, "test-db|PUBLIC|test-masking-policy|APPLY|false", map[string]interface{}{ + d := maskingPolicyGrant(t, "test-db|PUBLIC|test-masking-policy|APPLY||false", map[string]interface{}{ "masking_policy_name": "test-masking-policy", "schema_name": "PUBLIC", "database_name": "test-db", diff --git a/pkg/resources/materialized_view_grant.go b/pkg/resources/materialized_view_grant.go index 3ea2e8229e..7376bc418f 100644 --- a/pkg/resources/materialized_view_grant.go +++ b/pkg/resources/materialized_view_grant.go @@ -104,6 +104,7 @@ func CreateMaterializedViewGrant(d *schema.ResourceData, meta interface{}) error priv := d.Get("privilege").(string) futureMaterializedViews := d.Get("on_future").(bool) grantOption := d.Get("with_grant_option").(bool) + roles := expandStringList(d.Get("roles").(*schema.Set).List()) if (schemaName == "") && !futureMaterializedViews { return errors.New("schema_name must be set unless on_future is true.") @@ -134,6 +135,7 @@ func CreateMaterializedViewGrant(d *schema.ResourceData, meta interface{}) error ObjectName: materializedViewName, Privilege: priv, GrantOption: grantOption, + Roles: roles, } dataIDInput, err := grant.String() if err != nil { diff --git a/pkg/resources/materialized_view_grant_test.go b/pkg/resources/materialized_view_grant_test.go index a0abb8ce3d..b7fc328736 100644 --- a/pkg/resources/materialized_view_grant_test.go +++ b/pkg/resources/materialized_view_grant_test.go @@ -48,7 +48,7 @@ func TestMaterializedViewGrantCreate(t *testing.T) { func TestMaterializedViewGrantRead(t *testing.T) { r := require.New(t) - d := materializedViewGrant(t, "test-db|PUBLIC|test-materialized-view|SELECT|false", map[string]interface{}{ + d := materializedViewGrant(t, "test-db|PUBLIC|test-materialized-view|SELECT||false", map[string]interface{}{ "materialized_view_name": "test-materialized-view", "schema_name": "PUBLIC", "database_name": "test-db", diff --git a/pkg/resources/pipe_grant.go b/pkg/resources/pipe_grant.go index 86f4a196d2..a5cb707436 100644 --- a/pkg/resources/pipe_grant.go +++ b/pkg/resources/pipe_grant.go @@ -91,6 +91,7 @@ func CreatePipeGrant(d *schema.ResourceData, meta interface{}) error { priv := d.Get("privilege").(string) futurePipes := d.Get("on_future").(bool) grantOption := d.Get("with_grant_option").(bool) + roles := expandStringList(d.Get("roles").(*schema.Set).List()) if (pipeName == "") && !futurePipes { return errors.New("pipe_name must be set unless on_future is true.") @@ -117,6 +118,7 @@ func CreatePipeGrant(d *schema.ResourceData, meta interface{}) error { ObjectName: pipeName, Privilege: priv, GrantOption: grantOption, + Roles: roles, } dataIDInput, err := grant.String() if err != nil { diff --git a/pkg/resources/procedure_grant.go b/pkg/resources/procedure_grant.go index 0d854e8a27..15c236ff50 100644 --- a/pkg/resources/procedure_grant.go +++ b/pkg/resources/procedure_grant.go @@ -137,6 +137,7 @@ func CreateProcedureGrant(d *schema.ResourceData, meta interface{}) error { futureProcedures := d.Get("on_future").(bool) grantOption := d.Get("with_grant_option").(bool) arguments = d.Get("arguments").([]interface{}) + roles := expandStringList(d.Get("roles").(*schema.Set).List()) if (procedureName == "") && !futureProcedures { return errors.New("procedure_name must be set unless on_future is true.") @@ -169,6 +170,7 @@ func CreateProcedureGrant(d *schema.ResourceData, meta interface{}) error { ObjectName: procedureSignature, Privilege: priv, GrantOption: grantOption, + Roles: roles, } dataIDInput, err := grant.String() if err != nil { diff --git a/pkg/resources/procedure_grant_test.go b/pkg/resources/procedure_grant_test.go index 92d0aa7dde..98a69b7c01 100644 --- a/pkg/resources/procedure_grant_test.go +++ b/pkg/resources/procedure_grant_test.go @@ -56,7 +56,7 @@ func TestProcedureGrantCreate(t *testing.T) { func TestProcedureGrantRead(t *testing.T) { r := require.New(t) - d := procedureGrant(t, "test-db|PUBLIC|test-procedure(A ARRAY, B STRING):STRING|USAGE|false", map[string]interface{}{ + d := procedureGrant(t, "test-db|PUBLIC|test-procedure(A ARRAY, B STRING):STRING|USAGE||false", map[string]interface{}{ "procedure_name": "test-procedure", "arguments": []interface{}{map[string]interface{}{ "name": "a", diff --git a/pkg/resources/resource_monitor_grant.go b/pkg/resources/resource_monitor_grant.go index d87a6fe639..f832ac263e 100644 --- a/pkg/resources/resource_monitor_grant.go +++ b/pkg/resources/resource_monitor_grant.go @@ -62,6 +62,7 @@ func CreateResourceMonitorGrant(d *schema.ResourceData, meta interface{}) error priv := d.Get("privilege").(string) grantOption := d.Get("with_grant_option").(bool) builder := snowflake.ResourceMonitorGrant(w) + roles := expandStringList(d.Get("roles").(*schema.Set).List()) err := createGenericGrant(d, meta, builder) if err != nil { @@ -72,6 +73,7 @@ func CreateResourceMonitorGrant(d *schema.ResourceData, meta interface{}) error ResourceName: w, Privilege: priv, GrantOption: grantOption, + Roles: roles, } dataIDInput, err := grant.String() if err != nil { diff --git a/pkg/resources/resource_monitor_grant_test.go b/pkg/resources/resource_monitor_grant_test.go index b6096f0efa..0fef299deb 100644 --- a/pkg/resources/resource_monitor_grant_test.go +++ b/pkg/resources/resource_monitor_grant_test.go @@ -45,7 +45,7 @@ func TestResourceMonitorGrantCreate(t *testing.T) { func TestResourceMonitorGrantRead(t *testing.T) { r := require.New(t) - d := resourceMonitorGrant(t, "test-monitor|||MONITOR|false", map[string]interface{}{ + d := resourceMonitorGrant(t, "test-monitor|||MONITOR||false", map[string]interface{}{ "monitor_name": "test-monitor", "privilege": "MONITOR", "roles": []interface{}{"test-role-1", "test-role-2"}, diff --git a/pkg/resources/role_grants.go b/pkg/resources/role_grants.go index 37ec3905e7..1cdf05cf87 100644 --- a/pkg/resources/role_grants.go +++ b/pkg/resources/role_grants.go @@ -3,11 +3,13 @@ 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/jmoiron/sqlx" + "github.com/pkg/errors" ) func RoleGrants() *schema.Resource { @@ -57,6 +59,16 @@ func CreateRoleGrants(d *schema.ResourceData, meta interface{}) error { return fmt.Errorf("no users or roles specified for role grants") } + grant := &grantID{ + ResourceName: roleName, + Roles: roles, + } + dataIDInput, err := grant.String() + d.SetId(dataIDInput) + + if err != nil { + return errors.Wrap(err, "error creating role grant") + } for _, role := range roles { err := grantRoleToRole(db, roleName, role) if err != nil { @@ -70,7 +82,7 @@ func CreateRoleGrants(d *schema.ResourceData, meta interface{}) error { return err } } - d.SetId(roleName) + return ReadRoleGrants(d, meta) } @@ -96,7 +108,12 @@ type roleGrant struct { func ReadRoleGrants(d *schema.ResourceData, meta interface{}) error { db := meta.(*sql.DB) - roleName := d.Id() + log.Println(d.Id()) + grantID, err := grantIDFromString(d.Id()) + if err != nil { + return err + } + roleName := grantID.ResourceName tfRoles := expandStringList(d.Get("roles").(*schema.Set).List()) tfUsers := expandStringList(d.Get("users").(*schema.Set).List()) diff --git a/pkg/resources/role_grants_test.go b/pkg/resources/role_grants_test.go index b131b76f57..4d61abd5cf 100644 --- a/pkg/resources/role_grants_test.go +++ b/pkg/resources/role_grants_test.go @@ -56,7 +56,7 @@ func expectReadRoleGrants(mock sqlmock.Sqlmock) { func TestRoleGrantsRead(t *testing.T) { r := require.New(t) - d := roleGrants(t, "good_name", map[string]interface{}{ + d := roleGrants(t, "good_name||||role1,role2|false", map[string]interface{}{ "role_name": "good_name", "roles": []interface{}{"role1", "role2"}, "users": []interface{}{"user1", "user2"}, @@ -74,7 +74,7 @@ func TestRoleGrantsRead(t *testing.T) { func TestRoleGrantsDelete(t *testing.T) { r := require.New(t) - d := roleGrants(t, "drop_it", map[string]interface{}{ + d := roleGrants(t, "drop_it||||role1,role2|false", map[string]interface{}{ "role_name": "drop_it", "roles": []interface{}{"role1", "role2"}, "users": []interface{}{"user1", "user2"}, diff --git a/pkg/resources/row_access_policy_grant.go b/pkg/resources/row_access_policy_grant.go index f708911c16..9ad29955e0 100644 --- a/pkg/resources/row_access_policy_grant.go +++ b/pkg/resources/row_access_policy_grant.go @@ -81,6 +81,7 @@ func CreateRowAccessPolicyGrant(d *schema.ResourceData, meta interface{}) error schemaName := d.Get("schema_name").(string) priv := d.Get("privilege").(string) grantOption := d.Get("with_grant_option").(bool) + roles := expandStringList(d.Get("roles").(*schema.Set).List()) builder := snowflake.RowAccessPolicyGrant(dbName, schemaName, rowAccessPolicyName) @@ -95,6 +96,7 @@ func CreateRowAccessPolicyGrant(d *schema.ResourceData, meta interface{}) error ObjectName: rowAccessPolicyName, Privilege: priv, GrantOption: grantOption, + Roles: roles, } dataIDInput, err := grant.String() if err != nil { diff --git a/pkg/resources/row_access_policy_grant_test.go b/pkg/resources/row_access_policy_grant_test.go index 88312256b6..2a82d43b53 100644 --- a/pkg/resources/row_access_policy_grant_test.go +++ b/pkg/resources/row_access_policy_grant_test.go @@ -44,7 +44,7 @@ func TestRowAccessPolicyGrantCreate(t *testing.T) { func TestRowAccessPolicyGrantRead(t *testing.T) { r := require.New(t) - d := rowAccessPolicyGrant(t, "test-db|PUBLIC|test-row-access-policy|APPLY|false", map[string]interface{}{ + d := rowAccessPolicyGrant(t, "test-db|PUBLIC|test-row-access-policy|APPLY||false", map[string]interface{}{ "row_access_policy_name": "test-row-access-policy", "schema_name": "PUBLIC", "database_name": "test-db", diff --git a/pkg/resources/schema_grant.go b/pkg/resources/schema_grant.go index aab717300c..7e33df4e73 100644 --- a/pkg/resources/schema_grant.go +++ b/pkg/resources/schema_grant.go @@ -101,18 +101,19 @@ func SchemaGrant() *TerraformGrantResource { // CreateSchemaGrant implements schema.CreateFunc func CreateSchemaGrant(d *schema.ResourceData, meta interface{}) error { - var schema string + var schemaName string if _, ok := d.GetOk("schema_name"); ok { - schema = d.Get("schema_name").(string) + schemaName = d.Get("schema_name").(string) } else { - schema = "" + schemaName = "" } db := d.Get("database_name").(string) priv := d.Get("privilege").(string) onFuture := d.Get("on_future").(bool) grantOption := d.Get("with_grant_option").(bool) + roles := expandStringList(d.Get("roles").(*schema.Set).List()) - if (schema == "") && !onFuture { + if (schemaName == "") && !onFuture { return errors.New("schema_name must be set unless on_future is true.") } @@ -120,7 +121,7 @@ func CreateSchemaGrant(d *schema.ResourceData, meta interface{}) error { if onFuture { builder = snowflake.FutureSchemaGrant(db) } else { - builder = snowflake.SchemaGrant(db, schema) + builder = snowflake.SchemaGrant(db, schemaName) } err := createGenericGrant(d, meta, builder) @@ -130,9 +131,10 @@ func CreateSchemaGrant(d *schema.ResourceData, meta interface{}) error { grantID := &grantID{ ResourceName: db, - SchemaName: schema, + SchemaName: schemaName, Privilege: priv, GrantOption: grantOption, + Roles: roles, } dataIDInput, err := grantID.String() if err != nil { diff --git a/pkg/resources/schema_grant_test.go b/pkg/resources/schema_grant_test.go index 13a6ceb947..3f236c0bb9 100644 --- a/pkg/resources/schema_grant_test.go +++ b/pkg/resources/schema_grant_test.go @@ -58,7 +58,7 @@ func TestSchemaGrantCreate(t *testing.T) { func TestSchemaGrantRead(t *testing.T) { r := require.New(t) - d := schemaGrant(t, "test-db|test-schema||USAGE|false", map[string]interface{}{ + d := schemaGrant(t, "test-db|test-schema||USAGE||false", map[string]interface{}{ "schema_name": "test-schema", "database_name": "test-db", "privilege": "USAGE", diff --git a/pkg/resources/sequence_grant.go b/pkg/resources/sequence_grant.go index 8116d70584..02e17cf437 100644 --- a/pkg/resources/sequence_grant.go +++ b/pkg/resources/sequence_grant.go @@ -90,6 +90,7 @@ func CreateSequenceGrant(d *schema.ResourceData, meta interface{}) error { priv := d.Get("privilege").(string) futureSequences := d.Get("on_future").(bool) grantOption := d.Get("with_grant_option").(bool) + roles := expandStringList(d.Get("roles").(*schema.Set).List()) if (sequenceName == "") && !futureSequences { return errors.New("sequence_name must be set unless on_future is true.") @@ -116,6 +117,7 @@ func CreateSequenceGrant(d *schema.ResourceData, meta interface{}) error { ObjectName: sequenceName, Privilege: priv, GrantOption: grantOption, + Roles: roles, } dataIDInput, err := grant.String() if err != nil { diff --git a/pkg/resources/sequence_grant_test.go b/pkg/resources/sequence_grant_test.go index b98fc6be1f..2ad8025e4c 100644 --- a/pkg/resources/sequence_grant_test.go +++ b/pkg/resources/sequence_grant_test.go @@ -45,7 +45,7 @@ func TestSequenceGrantCreate(t *testing.T) { func TestSequenceGrantRead(t *testing.T) { r := require.New(t) - d := sequenceGrant(t, "test-db|PUBLIC|test-sequence|USAGE|false", map[string]interface{}{ + d := sequenceGrant(t, "test-db|PUBLIC|test-sequence|USAGE||false", map[string]interface{}{ "sequence_name": "test-sequence", "schema_name": "PUBLIC", "database_name": "test-db", diff --git a/pkg/resources/stage_grant_test.go b/pkg/resources/stage_grant_test.go index ca59231510..3c92bc5b09 100644 --- a/pkg/resources/stage_grant_test.go +++ b/pkg/resources/stage_grant_test.go @@ -51,7 +51,7 @@ func TestStageGrantCreate(t *testing.T) { func TestStageGrantRead(t *testing.T) { r := require.New(t) - d := stageGrant(t, "test-db|test-schema|test-stage|USAGE|false", map[string]interface{}{ + d := stageGrant(t, "test-db|test-schema|test-stage|USAGE||false", map[string]interface{}{ "stage_name": "test-stage", "schema_name": "test-schema", "database_name": "test-db", diff --git a/pkg/resources/stream_grant.go b/pkg/resources/stream_grant.go index 8c16fbf30f..c4e29cf4f2 100644 --- a/pkg/resources/stream_grant.go +++ b/pkg/resources/stream_grant.go @@ -90,6 +90,7 @@ func CreateStreamGrant(d *schema.ResourceData, meta interface{}) error { priv := d.Get("privilege").(string) futureStreams := d.Get("on_future").(bool) grantOption := d.Get("with_grant_option").(bool) + roles := expandStringList(d.Get("roles").(*schema.Set).List()) if (streamName == "") && !futureStreams { return errors.New("stream_name must be set unless on_future is true.") @@ -116,6 +117,7 @@ func CreateStreamGrant(d *schema.ResourceData, meta interface{}) error { ObjectName: streamName, Privilege: priv, GrantOption: grantOption, + Roles: roles, } dataIDInput, err := grant.String() if err != nil { diff --git a/pkg/resources/stream_grant_test.go b/pkg/resources/stream_grant_test.go index 7b9e1514ed..c89d3f39e9 100644 --- a/pkg/resources/stream_grant_test.go +++ b/pkg/resources/stream_grant_test.go @@ -45,7 +45,7 @@ func TestStreamGrantCreate(t *testing.T) { func TestStreamGrantRead(t *testing.T) { r := require.New(t) - d := streamGrant(t, "test-db|PUBLIC|test-stream|SELECT|false", map[string]interface{}{ + d := streamGrant(t, "test-db|PUBLIC|test-stream|SELECT||false", map[string]interface{}{ "stream_name": "test-stream", "schema_name": "PUBLIC", "database_name": "test-db", diff --git a/pkg/resources/table_grant.go b/pkg/resources/table_grant.go index ac659acfc1..0e131d4169 100644 --- a/pkg/resources/table_grant.go +++ b/pkg/resources/table_grant.go @@ -112,6 +112,7 @@ func CreateTableGrant(d *schema.ResourceData, meta interface{}) error { priv := d.Get("privilege").(string) onFuture := d.Get("on_future").(bool) grantOption := d.Get("with_grant_option").(bool) + roles := expandStringList(d.Get("roles").(*schema.Set).List()) if (schemaName == "") && !onFuture { return errors.New("schema_name must be set unless on_future is true.") @@ -139,6 +140,7 @@ func CreateTableGrant(d *schema.ResourceData, meta interface{}) error { SchemaName: schemaName, Privilege: priv, GrantOption: grantOption, + Roles: roles, } if !onFuture { grantID.ObjectName = tableName diff --git a/pkg/resources/table_grant_test.go b/pkg/resources/table_grant_test.go index 3a21d5ad53..16e0dbc9ce 100644 --- a/pkg/resources/table_grant_test.go +++ b/pkg/resources/table_grant_test.go @@ -51,7 +51,7 @@ func TestTableGrantUpdate(t *testing.T) { r := require.New(t) // d := schema.TestResourceDataRaw(t, resources.TableGrant().Resource.Schema, in) - d := tableGrant(t, "test-db|PUBLIC|test-table|SELECT|false", map[string]interface{}{ + d := tableGrant(t, "test-db|PUBLIC|test-table|SELECT||false", map[string]interface{}{ "table_name": "test-table", "schema_name": "PUBLIC", "database_name": "test-db", diff --git a/pkg/resources/task_grant.go b/pkg/resources/task_grant.go index b5c7380c2b..c0e9b34d86 100644 --- a/pkg/resources/task_grant.go +++ b/pkg/resources/task_grant.go @@ -91,6 +91,7 @@ func CreateTaskGrant(d *schema.ResourceData, meta interface{}) error { priv := d.Get("privilege").(string) futureTasks := d.Get("on_future").(bool) grantOption := d.Get("with_grant_option").(bool) + roles := expandStringList(d.Get("roles").(*schema.Set).List()) if (taskName == "") && !futureTasks { return errors.New("task_name must be set unless on_future is true.") @@ -117,6 +118,7 @@ func CreateTaskGrant(d *schema.ResourceData, meta interface{}) error { ObjectName: taskName, Privilege: priv, GrantOption: grantOption, + Roles: roles, } dataIDInput, err := grant.String() if err != nil { diff --git a/pkg/resources/task_grant_test.go b/pkg/resources/task_grant_test.go index dbfc382881..f5b75584d8 100644 --- a/pkg/resources/task_grant_test.go +++ b/pkg/resources/task_grant_test.go @@ -45,7 +45,7 @@ func TestTaskGrantCreate(t *testing.T) { func TestTaskGrantRead(t *testing.T) { r := require.New(t) - d := taskGrant(t, "test-db|PUBLIC|test-task|OPERATE|false", map[string]interface{}{ + d := taskGrant(t, "test-db|PUBLIC|test-task|OPERATE||false", map[string]interface{}{ "task_name": "test-task", "schema_name": "PUBLIC", "database_name": "test-db", diff --git a/pkg/resources/view_grant.go b/pkg/resources/view_grant.go index c0b0b5c3f9..03e6ce5f42 100644 --- a/pkg/resources/view_grant.go +++ b/pkg/resources/view_grant.go @@ -107,6 +107,7 @@ func CreateViewGrant(d *schema.ResourceData, meta interface{}) error { priv := d.Get("privilege").(string) futureViews := d.Get("on_future").(bool) grantOption := d.Get("with_grant_option").(bool) + roles := expandStringList(d.Get("roles").(*schema.Set).List()) if (schemaName == "") && !futureViews { return errors.New("schema_name must be set unless on_future is true.") @@ -137,6 +138,7 @@ func CreateViewGrant(d *schema.ResourceData, meta interface{}) error { ObjectName: viewName, Privilege: priv, GrantOption: grantOption, + Roles: roles, } dataIDInput, err := grant.String() if err != nil { diff --git a/pkg/resources/view_grant_test.go b/pkg/resources/view_grant_test.go index 7f35e79254..b40aa189d3 100644 --- a/pkg/resources/view_grant_test.go +++ b/pkg/resources/view_grant_test.go @@ -48,7 +48,7 @@ func TestViewGrantCreate(t *testing.T) { func TestViewGrantRead(t *testing.T) { r := require.New(t) - d := viewGrant(t, "test-db|PUBLIC|test-view|SELECT|false", map[string]interface{}{ + d := viewGrant(t, "test-db|PUBLIC|test-view|SELECT||false", map[string]interface{}{ "view_name": "test-view", "schema_name": "PUBLIC", "database_name": "test-db", diff --git a/pkg/resources/warehouse_grant.go b/pkg/resources/warehouse_grant.go index 9d90880a6c..bf54c9788b 100644 --- a/pkg/resources/warehouse_grant.go +++ b/pkg/resources/warehouse_grant.go @@ -68,6 +68,7 @@ func CreateWarehouseGrant(d *schema.ResourceData, meta interface{}) error { priv := d.Get("privilege").(string) grantOption := d.Get("with_grant_option").(bool) builder := snowflake.WarehouseGrant(w) + roles := expandStringList(d.Get("roles").(*schema.Set).List()) err := createGenericGrant(d, meta, builder) if err != nil { @@ -78,6 +79,7 @@ func CreateWarehouseGrant(d *schema.ResourceData, meta interface{}) error { ResourceName: w, Privilege: priv, GrantOption: grantOption, + Roles: roles, } dataIDInput, err := grant.String() if err != nil {