Repository: chanzuckerberg/terraform-provider-snowflake
Pull request #567 (2021-06-10 12:47:39)

Add data source snowflake_current_account
Creates a new data source `snowflake_current_account` which allows the account information to be read and then passed into other systems as part of a terraform deployment.

This avoids having to pass in the same information as variables to terraform and can use the information supplied by the provider.

## Test Plan

* [x] acceptance tests
* [x] unit tests

```
$ go test -v ./... -run TestCurrentAccount
?   	github.com/chanzuckerberg/terraform-provider-snowflake	[no test files]
testing: warning: no tests to run
PASS
ok  	github.com/chanzuckerberg/terraform-provider-snowflake/pkg/datasources	1.027s [no tests to run]
?   	github.com/chanzuckerberg/terraform-provider-snowflake/pkg/db	[no test files]
testing: warning: no tests to run
PASS
ok  	github.com/chanzuckerberg/terraform-provider-snowflake/pkg/provider	1.515s [no tests to run]
testing: warning: no tests to run
PASS
ok  	github.com/chanzuckerberg/terraform-provider-snowflake/pkg/resources	1.333s [no tests to run]
=== RUN   TestCurrentAccountSelect
--- PASS: TestCurrentAccountSelect (0.00s)
=== RUN   TestCurrentAccountRead
=== RUN   TestCurrentAccountRead/aws_oregon
2021/06/10 13:45:00 [DEBUG] query stmt SELECT CURRENT_ACCOUNT() AS "account", CURRENT_REGION() AS "region";
=== RUN   TestCurrentAccountRead/aws_n_virginia
2021/06/10 13:45:00 [DEBUG] query stmt SELECT CURRENT_ACCOUNT() AS "account", CURRENT_REGION() AS "region";
=== RUN   TestCurrentAccountRead/aws_canada_central
2021/06/10 13:45:00 [DEBUG] query stmt SELECT CURRENT_ACCOUNT() AS "account", CURRENT_REGION() AS "region";
=== RUN   TestCurrentAccountRead/gcp_canada_central
2021/06/10 13:45:00 [DEBUG] query stmt SELECT CURRENT_ACCOUNT() AS "account", CURRENT_REGION() AS "region";
=== RUN   TestCurrentAccountRead/azure_washington
2021/06/10 13:45:00 [DEBUG] query stmt SELECT CURRENT_ACCOUNT() AS "account", CURRENT_REGION() AS "region";
--- PASS: TestCurrentAccountRead (0.00s)
    --- PASS: TestCurrentAccountRead/aws_oregon (0.00s)
    --- PASS: TestCurrentAccountRead/aws_n_virginia (0.00s)
    --- PASS: TestCurrentAccountRead/aws_canada_central (0.00s)
    --- PASS: TestCurrentAccountRead/gcp_canada_central (0.00s)
    --- PASS: TestCurrentAccountRead/azure_washington (0.00s)
PASS
ok  	github.com/chanzuckerberg/terraform-provider-snowflake/pkg/snowflake	1.327s
?   	github.com/chanzuckerberg/terraform-provider-snowflake/pkg/testhelpers	[no test files]
testing: warning: no tests to run
PASS
ok  	github.com/chanzuckerberg/terraform-provider-snowflake/pkg/validation	1.117s [no tests to run]
?   	github.com/chanzuckerberg/terraform-provider-snowflake/pkg/version	[no test files]
```

```
$ TF_ACC=1 go test -v ./... -run TestAccCurrentAccount
?   	github.com/chanzuckerberg/terraform-provider-snowflake	[no test files]
=== RUN   TestAccCurrentAccount
=== PAUSE TestAccCurrentAccount
=== CONT  TestAccCurrentAccount
--- PASS: TestAccCurrentAccount (6.11s)
PASS
ok  	github.com/chanzuckerberg/terraform-provider-snowflake/pkg/datasources	7.036s
?   	github.com/chanzuckerberg/terraform-provider-snowflake/pkg/db	[no test files]
testing: warning: no tests to run
PASS
ok  	github.com/chanzuckerberg/terraform-provider-snowflake/pkg/provider	(cached) [no tests to run]
testing: warning: no tests to run
PASS
ok  	github.com/chanzuckerberg/terraform-provider-snowflake/pkg/resources	(cached) [no tests to run]
testing: warning: no tests to run
PASS
ok  	github.com/chanzuckerberg/terraform-provider-snowflake/pkg/snowflake	(cached) [no tests to run]
?   	github.com/chanzuckerberg/terraform-provider-snowflake/pkg/testhelpers	[no test files]
testing: warning: no tests to run
PASS
ok  	github.com/chanzuckerberg/terraform-provider-snowflake/pkg/validation	(cached) [no tests to run]
?   	github.com/chanzuckerberg/terraform-provider-snowflake/pkg/version	[no test files]
```

## References

N/A

alldoami: Wanted to add some reference links: https://docs.snowflake.com/en/sql-reference/functions/current_account.html 
I didn't know this function existed 😅 
alldoami: /ok-to-test sha=a097dc4
github-actions[bot]: Integration tests success for [a097dc4](https://github.com/chanzuckerberg/terraform-provider-snowflake/actions/runs/927035058)

Patch:
diff --git a/docs/data-sources/current_account.md b/docs/data-sources/current_account.md
new file mode 100644
index 00000000..fd56e38c
--- /dev/null
+++ b/docs/data-sources/current_account.md
@@ -0,0 +1,38 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "snowflake_current_account Data Source - terraform-provider-snowflake"
+subcategory: ""
+description: |-
+  
+---
+
+# snowflake_current_account (Data Source)
+
+
+
+## Example Usage
+
+```terraform
+data "snowflake_current_account" "this" {}
+
+resource "aws_ssm_parameter" "snowflake_account_url" {
+  name  = "/snowflake/account_url"
+  type  = "String"
+  value = data.snowflake_current_account.this.url
+}
+```
+
+<!-- schema generated by tfplugindocs -->
+## Schema
+
+### Optional
+
+- **id** (String) The ID of this resource.
+
+### Read-Only
+
+- **account** (String) The Snowflake Account ID; as returned by CURRENT_ACCOUNT().
+- **region** (String) The Snowflake Region; as returned by CURRENT_REGION()
+- **url** (String) The Snowflake URL.
+
+
diff --git a/examples/data-sources/snowflake_current_account/data-source.tf b/examples/data-sources/snowflake_current_account/data-source.tf
new file mode 100644
index 00000000..eff3a174
--- /dev/null
+++ b/examples/data-sources/snowflake_current_account/data-source.tf
@@ -0,0 +1,7 @@
+data "snowflake_current_account" "this" {}
+
+resource "aws_ssm_parameter" "snowflake_account_url" {
+  name  = "/snowflake/account_url"
+  type  = "String"
+  value = data.snowflake_current_account.this.url
+}
diff --git a/pkg/datasources/current_account.go b/pkg/datasources/current_account.go
new file mode 100644
index 00000000..52b5b5de
--- /dev/null
+++ b/pkg/datasources/current_account.go
@@ -0,0 +1,64 @@
+package datasources
+
+import (
+	"database/sql"
+	"fmt"
+	"log"
+
+	"github.com/chanzuckerberg/terraform-provider-snowflake/pkg/snowflake"
+	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+)
+
+var currentAccountSchema = map[string]*schema.Schema{
+	"account": {
+		Type:        schema.TypeString,
+		Computed:    true,
+		Description: "The Snowflake Account ID; as returned by CURRENT_ACCOUNT().",
+	},
+
+	"region": {
+		Type:        schema.TypeString,
+		Computed:    true,
+		Description: "The Snowflake Region; as returned by CURRENT_REGION()",
+	},
+
+	"url": {
+		Type:        schema.TypeString,
+		Computed:    true,
+		Description: "The Snowflake URL.",
+	},
+}
+
+// CurrentAccount the Snowflake current account resource
+func CurrentAccount() *schema.Resource {
+	return &schema.Resource{
+		Read:   ReadCurrentAccount,
+		Schema: currentAccountSchema,
+	}
+}
+
+// ReadCurrentAccount read the current snowflake account information
+func ReadCurrentAccount(d *schema.ResourceData, meta interface{}) error {
+	db := meta.(*sql.DB)
+	acc, err := snowflake.ReadCurrentAccount(db)
+
+	if err != nil {
+		log.Printf("[DEBUG] current_account failed to decode")
+		d.SetId("")
+		return nil
+	}
+
+	d.SetId(fmt.Sprintf("%s.%s", acc.Account, acc.Region))
+	d.Set("account", acc.Account)
+	d.Set("region", acc.Region)
+	url, err := acc.AccountURL()
+
+	if err != nil {
+		log.Printf("[DEBUG] generating snowflake url failed")
+		d.SetId("")
+		return nil
+	}
+
+	d.Set("url", url)
+	return nil
+}
diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go
index c2ade9da..089805dc 100644
--- a/pkg/provider/provider.go
+++ b/pkg/provider/provider.go
@@ -190,6 +190,7 @@ func getResources() map[string]*schema.Resource {
 
 func getDataSources() map[string]*schema.Resource {
 	dataSources := map[string]*schema.Resource{
+		"snowflake_current_account":                   datasources.CurrentAccount(),
 		"snowflake_system_generate_scim_access_token": datasources.SystemGenerateSCIMAccessToken(),
 		"snowflake_system_get_aws_sns_iam_policy":     datasources.SystemGetAWSSNSIAMPolicy(),
 		"snowflake_system_get_privatelink_config":     datasources.SystemGetPrivateLinkConfig(),
diff --git a/pkg/snowflake/current_account.go b/pkg/snowflake/current_account.go
new file mode 100644
index 00000000..f9b5f821
--- /dev/null
+++ b/pkg/snowflake/current_account.go
@@ -0,0 +1,68 @@
+package snowflake
+
+import (
+	"database/sql"
+	"fmt"
+	"strings"
+
+	"github.com/jmoiron/sqlx"
+)
+
+// taken from https://docs.snowflake.com/en/user-guide/admin-account-identifier.html#snowflake-region-ids
+var regionMapping = map[string]string{
+	"aws_us_west_2":          "", // left black as this is the default
+	"aws_us_east_2":          "us-east-2.aws",
+	"aws_us_east_1":          "us-east-1",
+	"aws_us_east_1_gov":      "us-east-1-gov.aws",
+	"aws_ca_central_1":       "ca-central-1.aws",
+	"aws_eu_west_1":          "eu-west-1",
+	"aws_eu_west_2":          "eu-west-2.aws",
+	"aws_eu_central_1":       "eu-central-1",
+	"aws_ap_northeast_1":     "ap-northeast-1.aws",
+	"aws_ap_south_1":         "ap-south-1.aws",
+	"aws_ap_southeast_1":     "ap-southeast-1",
+	"aws_ap_southeast_2":     "ap-southeast-2",
+	"gcp_us_central1":        "us-central1.gcp",
+	"gcp_europe_west2":       "europe-west2.gcp",
+	"gcp_europe_west4":       "europe-west4.gcp",
+	"azure_westus2":          "west-us-2.azure",
+	"azure_eastus2":          "east-us-2.azure",
+	"azure_usgovvirginia":    "us-gov-virginia.azure",
+	"azure_canadacentral":    "canada-central.azure",
+	"azure_westeurope":       "west-europe.azure",
+	"azure_southeastasia":    "southeast-asia.azure",
+	"azure_switzerlandnorth": "switzerland-north.azure",
+	"azure_australiaeast":    "australia-east.azure",
+}
+
+func SelectCurrentAccount() string {
+	return `SELECT CURRENT_ACCOUNT() AS "account", CURRENT_REGION() AS "region";`
+}
+
+type account struct {
+	Account string `db:"account"`
+	Region  string `db:"region"`
+}
+
+func ScanCurrentAccount(row *sqlx.Row) (*account, error) {
+	acc := &account{}
+	err := row.StructScan(acc)
+	return acc, err
+}
+
+func ReadCurrentAccount(db *sql.DB) (*account, error) {
+	row := QueryRow(db, SelectCurrentAccount())
+	return ScanCurrentAccount(row)
+}
+
+func (acc *account) AccountURL() (string, error) {
+	if region_id, ok := regionMapping[strings.ToLower(acc.Region)]; ok {
+		account_id := acc.Account
+		if len(region_id) > 0 {
+			account_id = fmt.Sprintf("%s.%s", account_id, region_id)
+		}
+		return fmt.Sprintf("https://%s.snowflakecomputing.com", account_id), nil
+	}
+
+	return "", fmt.Errorf("Failed to map Snowflake account region %s to a region_id", acc.Region)
+}


Test patch:
diff --git a/pkg/datasources/current_account_acceptance_test.go b/pkg/datasources/current_account_acceptance_test.go
new file mode 100644
index 00000000..9b5438df
--- /dev/null
+++ b/pkg/datasources/current_account_acceptance_test.go
@@ -0,0 +1,30 @@
+package datasources_test
+
+import (
+	"testing"
+
+	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
+)
+
+func TestAccCurrentAccount(t *testing.T) {
+	resource.ParallelTest(t, resource.TestCase{
+		Providers: providers(),
+		Steps: []resource.TestStep{
+			{
+				Config: currentAccount(),
+				Check: resource.ComposeTestCheckFunc(
+					resource.TestCheckResourceAttrSet("data.snowflake_current_account.p", "account"),
+					resource.TestCheckResourceAttrSet("data.snowflake_current_account.p", "region"),
+					resource.TestCheckResourceAttrSet("data.snowflake_current_account.p", "url"),
+				),
+			},
+		},
+	})
+}
+
+func currentAccount() string {
+	s := `
+	data snowflake_current_account p {}
+	`
+	return s
+}
diff --git a/pkg/snowflake/current_account_test.go b/pkg/snowflake/current_account_test.go
new file mode 100644
index 00000000..082e73eb
--- /dev/null
+++ b/pkg/snowflake/current_account_test.go
@@ -0,0 +1,71 @@
+package snowflake_test
+
+import (
+	sqlmock "github.com/DATA-DOG/go-sqlmock"
+	"github.com/chanzuckerberg/terraform-provider-snowflake/pkg/snowflake"
+	"github.com/jmoiron/sqlx"
+	"github.com/stretchr/testify/require"
+	"testing"
+)
+
+func TestCurrentAccountSelect(t *testing.T) {
+	r := require.New(t)
+	r.Equal(`SELECT CURRENT_ACCOUNT() AS "account", CURRENT_REGION() AS "region";`, snowflake.SelectCurrentAccount())
+}
+
+func TestCurrentAccountRead(t *testing.T) {
+	type testCaseEntry struct {
+		account string
+		region  string
+		url     string
+	}
+
+	testCases := map[string]testCaseEntry{
+		"aws oregon": {
+			"ab1234",
+			"AWS_US_WEST_2",
+			"https://ab1234.snowflakecomputing.com",
+		},
+		"aws n virginia": {
+			"cd5678",
+			"AWS_US_EAST_1",
+			"https://cd5678.us-east-1.snowflakecomputing.com",
+		},
+		"aws canada central": {
+			"ef9012",
+			"AWS_CA_CENTRAL_1",
+			"https://ef9012.ca-central-1.aws.snowflakecomputing.com",
+		},
+		"gcp canada central": {
+			"gh3456",
+			"gcp_us_central1",
+			"https://gh3456.us-central1.gcp.snowflakecomputing.com",
+		},
+		"azure washington": {
+			"ij7890",
+			"azure_westus2",
+			"https://ij7890.west-us-2.azure.snowflakecomputing.com",
+		},
+	}
+
+	for name, testCase := range testCases {
+		t.Run(name, func(t *testing.T) {
+			r := require.New(t)
+			mockDB, mock, err := sqlmock.New()
+			r.NoError(err)
+			defer mockDB.Close()
+			sqlxDB := sqlx.NewDb(mockDB, "sqlmock")
+
+			rows := sqlmock.NewRows([]string{"account", "region"}).AddRow(testCase.account, testCase.region)
+			mock.ExpectQuery(`SELECT CURRENT_ACCOUNT\(\) AS "account", CURRENT_REGION\(\) AS "region";`).WillReturnRows(rows)
+
+			acc, err := snowflake.ReadCurrentAccount(sqlxDB.DB)
+			r.NoError(err)
+			r.Equal(testCase.account, acc.Account)
+			r.Equal(testCase.region, acc.Region)
+			url, err := acc.AccountURL()
+			r.NoError(err)
+			r.Equal(testCase.url, url)
+		})
+	}
+}