Repository: chanzuckerberg/terraform-provider-snowflake (go)

Issue:
[Question] Assign resource monitor to account level?
Hey, 
it's possible to create `snowflake_resource_monitor` resources as well as assigning them to a `snowflake_warehouse`.
What I did not find is assigning a monitor to the account e.g. `ALTER ACCOUNT SET RESOURCE_MONITOR = "resource_monitor_account";`

Did I miss anything or is this currently not supported/implemented?

Pull request description:
feat: Allow setting resource monitor on account
<!-- Feel free to delete comments as you fill this in -->

Allow setting resource monitor on the account scope.

Fixes #721

## 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  -->

* [ALTER ACCOUNT](https://docs.snowflake.com/en/sql-reference/sql/alter-account.html)

Patch:
diff --git a/docs/resources/resource_monitor.md b/docs/resources/resource_monitor.md
index 38d6338a..989bc4db 100644
--- a/docs/resources/resource_monitor.md
+++ b/docs/resources/resource_monitor.md
@@ -41,6 +41,7 @@ resource snowflake_resource_monitor monitor {
 - **frequency** (String) The frequency interval at which the credit usage resets to 0. If you set a frequency for a resource monitor, you must also set START_TIMESTAMP.
 - **id** (String) The ID of this resource.
 - **notify_triggers** (Set of Number) A list of percentage thresholds at which to send an alert to subscribed users.
+- **set_for_account** (Boolean) Specifies whether the resource monitor should be applied globally to your Snowflake account.
 - **start_timestamp** (String) The date and time when the resource monitor starts monitoring credit usage for the assigned warehouses.
 - **suspend_immediate_triggers** (Set of Number) A list of percentage thresholds at which to immediately suspend all warehouses.
 - **suspend_triggers** (Set of Number) A list of percentage thresholds at which to suspend all warehouses.
diff --git a/pkg/resources/resource_monitor.go b/pkg/resources/resource_monitor.go
index 8bcb002a..626a2c3b 100644
--- a/pkg/resources/resource_monitor.go
+++ b/pkg/resources/resource_monitor.go
@@ -70,6 +70,13 @@ var resourceMonitorSchema = map[string]*schema.Schema{
 		Description: "A list of percentage thresholds at which to send an alert to subscribed users.",
 		ForceNew:    true,
 	},
+	"set_for_account": {
+		Type:        schema.TypeBool,
+		Optional:    true,
+		Description: "Specifies whether the resource monitor should be applied globally to your Snowflake account.",
+		Default:     false,
+		ForceNew:    true,
+	},
 }
 
 // ResourceMonitor returns a pointer to the resource representing a resource monitor
@@ -129,7 +136,17 @@ func CreateResourceMonitor(d *schema.ResourceData, meta interface{}) error {
 
 	d.SetId(name)
 
-	return ReadResourceMonitor(d, meta)
+	if d.Get("set_for_account").(bool) {
+		if err := snowflake.Exec(db, cb.SetOnAccount()); err != nil {
+			return errors.Wrapf(err, "error setting resource monitor %v on account", name)
+		}
+	}
+
+	if err := ReadResourceMonitor(d, meta); err != nil {
+		return err
+	}
+
+	return nil
 }
 
 // ReadResourceMonitor implements schema.ReadFunc
@@ -198,6 +215,9 @@ func ReadResourceMonitor(d *schema.ResourceData, meta interface{}) error {
 	}
 	err = d.Set("notify_triggers", nTrigs)
 
+	// Account level
+	d.Set("set_for_account", rm.Level.Valid && rm.Level.String == "ACCOUNT")
+
 	return err
 }
 
diff --git a/pkg/snowflake/resource_monitor.go b/pkg/snowflake/resource_monitor.go
index 1e97a175..a40b1d18 100644
--- a/pkg/snowflake/resource_monitor.go
+++ b/pkg/snowflake/resource_monitor.go
@@ -119,6 +119,11 @@ func (rcb *ResourceMonitorCreateBuilder) Statement() string {
 	return sb.String()
 }
 
+// SetOnAccount returns the SQL query that will set the resource monitor globally on your Snowflake account
+func (rcb *ResourceMonitorCreateBuilder) SetOnAccount() string {
+	return fmt.Sprintf(`ALTER ACCOUNT SET RESOURCE_MONITOR = "%v"`, rcb.name)
+}
+
 type resourceMonitor struct {
 	Name                 sql.NullString `db:"name"`
 	CreditQuota          sql.NullString `db:"credit_quota"`


Test patch:
diff --git a/pkg/resources/resource_monitor_acceptance_test.go b/pkg/resources/resource_monitor_acceptance_test.go
index 3281f580..bd3db91f 100644
--- a/pkg/resources/resource_monitor_acceptance_test.go
+++ b/pkg/resources/resource_monitor_acceptance_test.go
@@ -21,6 +21,7 @@ func TestAcc_ResourceMonitor(t *testing.T) {
 				Check: resource.ComposeTestCheckFunc(
 					resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "name", name),
 					resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "credit_quota", "100"),
+					resource.TestCheckResourceAttr("snowflake_resource_monitor.test", "set_for_account", "false"),
 				),
 			},
 			// IMPORT
@@ -36,8 +37,9 @@ func TestAcc_ResourceMonitor(t *testing.T) {
 func resourceMonitorConfig(accName string) string {
 	return fmt.Sprintf(`
 resource "snowflake_resource_monitor" "test" {
-	name           = "%v"
-	credit_quota   = 100
+	name            = "%v"
+	credit_quota    = 100
+	set_for_account = false
 }
 `, accName)
 }
diff --git a/pkg/resources/resource_monitor_test.go b/pkg/resources/resource_monitor_test.go
index bfceeb9b..58d6f0c3 100644
--- a/pkg/resources/resource_monitor_test.go
+++ b/pkg/resources/resource_monitor_test.go
@@ -29,6 +29,7 @@ func TestResourceMonitorCreate(t *testing.T) {
 		"notify_triggers":            []interface{}{75, 88},
 		"suspend_triggers":           []interface{}{99},
 		"suspend_immediate_triggers": []interface{}{105},
+		"set_for_account":            true,
 	}
 
 	d := schema.TestResourceDataRaw(t, resources.ResourceMonitor().Schema, in)
@@ -38,6 +39,7 @@ func TestResourceMonitorCreate(t *testing.T) {
 		mock.ExpectExec(
 			`^CREATE RESOURCE MONITOR "good_name" CREDIT_QUOTA=100 TRIGGERS ON 99 PERCENT DO SUSPEND ON 105 PERCENT DO SUSPEND_IMMEDIATE ON 88 PERCENT DO NOTIFY ON 75 PERCENT DO NOTIFY$`,
 		).WillReturnResult(sqlmock.NewResult(1, 1))
+		mock.ExpectExec(`^ALTER ACCOUNT SET RESOURCE_MONITOR = "good_name"$`).WillReturnResult(sqlmock.NewResult(1, 1))
 
 		expectReadResourceMonitor(mock)
 		err := resources.CreateResourceMonitor(d, db)
@@ -51,7 +53,7 @@ func expectReadResourceMonitor(mock sqlmock.Sqlmock) {
 		"frequency", "start_time", "end_time", "notify_at", "suspend_at",
 		"suspend_immediately_at", "created_on", "owner", "comment",
 	}).AddRow(
-		"good_name", 100.00, 0.00, 100.00, "", "MONTHLY", "2001-01-01 00:00:00.000 -0700",
+		"good_name", 100.00, 0.00, 100.00, "ACCOUNT", "MONTHLY", "2001-01-01 00:00:00.000 -0700",
 		"", "75%,88%", "99%", "105%", "2001-01-01 00:00:00.000 -0700", "ACCOUNTADMIN", "")
 	mock.ExpectQuery(`^SHOW RESOURCE MONITORS LIKE 'good_name'$`).WillReturnRows(rows)
 }
diff --git a/pkg/snowflake/resource_monitor_test.go b/pkg/snowflake/resource_monitor_test.go
index 04eb4c16..8be9c2b8 100644
--- a/pkg/snowflake/resource_monitor_test.go
+++ b/pkg/snowflake/resource_monitor_test.go
@@ -34,3 +34,12 @@ func TestResourceMonitor(t *testing.T) {
 	q = cb.Statement()
 	r.Equal(`CREATE RESOURCE MONITOR "resource_monitor" FREQUENCY='YEARLY' CREDIT_QUOTA=666 TRIGGERS ON 80 PERCENT DO NOTIFY ON 90 PERCENT DO NOTIFY ON 95 PERCENT DO SUSPEND ON 100 PERCENT DO SUSPEND_IMMEDIATE`, q)
 }
+
+func TestResourceMonitorSetOnAccount(t *testing.T) {
+	r := require.New(t)
+	s := snowflake.ResourceMonitor("test_resource_monitor")
+	r.NotNil(s)
+
+	q := s.Create().SetOnAccount()
+	r.Equal(`ALTER ACCOUNT SET RESOURCE_MONITOR = "test_resource_monitor"`, q)
+}