diff --git a/MIGRATION_GUIDE.md b/MIGRATION_GUIDE.md
index 0b71ba3b3b..dfdb8f7aef 100644
--- a/MIGRATION_GUIDE.md
+++ b/MIGRATION_GUIDE.md
@@ -9,6 +9,14 @@ across different versions.
## v0.97.0 ➞ v0.98.0
+### *(new feature)* snowflake_connections datasource
+Added a new datasource enabling querying and filtering connections. Notes:
+- all results are stored in `connections` field.
+- `like` field enables connections filtering.
+- SHOW CONNECTIONS output is enclosed in `show_output` field inside `connections`.
+ It's important to limit the records and calls to Snowflake to the minimum. That's why we recommend assessing which information you need from the data source and then providing strong filters and turning off additional fields for better plan performance.
+
+
### *(new feature)* connection resources
Added a new resources for managing connections. We decided to split connection into two separate resources based on whether the connection is primary or a replica (secondary). i.e.:
diff --git a/docs/data-sources/connections.md b/docs/data-sources/connections.md
new file mode 100644
index 0000000000..5dff3c63ad
--- /dev/null
+++ b/docs/data-sources/connections.md
@@ -0,0 +1,79 @@
+---
+page_title: "snowflake_connections Data Source - terraform-provider-snowflake"
+subcategory: ""
+description: |-
+ Datasource used to get details of filtered connections. Filtering is aligned with the current possibilities for SHOW CONNECTIONS https://docs.snowflake.com/en/sql-reference/sql/show-connections query. The results of SHOW is encapsulated in one output collection connections.
+---
+
+!> **V1 release candidate** This data source is a release candidate for the V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the data source if needed. Any errors reported will be resolved with a higher priority. We encourage checking this data source out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0970--v0980) to use it.
+
+# snowflake_connections (Data Source)
+
+Datasource used to get details of filtered connections. Filtering is aligned with the current possibilities for [SHOW CONNECTIONS](https://docs.snowflake.com/en/sql-reference/sql/show-connections) query. The results of SHOW is encapsulated in one output collection `connections`.
+
+## Example Usage
+
+```terraform
+# Simple usage
+data "snowflake_connections" "simple" {
+}
+
+output "simple_output" {
+ value = data.snowflake_connections.simple.connections
+}
+
+# Filtering (like)
+data "snowflake_connections" "like" {
+ like = "connection-name"
+}
+
+output "like_output" {
+ value = data.snowflake_connections.like.connections
+}
+
+# Filtering by prefix (like)
+data "snowflake_connections" "like_prefix" {
+ like = "prefix%"
+}
+
+output "like_prefix_output" {
+ value = data.snowflake_connections.like_prefix.connections
+}
+```
+
+
+## Schema
+
+### Optional
+
+- `like` (String) Filters the output with **case-insensitive** pattern, with support for SQL wildcard characters (`%` and `_`).
+
+### Read-Only
+
+- `connections` (List of Object) Holds the aggregated output of all connections details queries. (see [below for nested schema](#nestedatt--connections))
+- `id` (String) The ID of this resource.
+
+
+### Nested Schema for `connections`
+
+Read-Only:
+
+- `show_output` (List of Object) (see [below for nested schema](#nestedobjatt--connections--show_output))
+
+
+### Nested Schema for `connections.show_output`
+
+Read-Only:
+
+- `account_locator` (String)
+- `account_name` (String)
+- `comment` (String)
+- `connection_url` (String)
+- `created_on` (String)
+- `failover_allowed_to_accounts` (List of String)
+- `is_primary` (Boolean)
+- `name` (String)
+- `organization_name` (String)
+- `primary` (String)
+- `region_group` (String)
+- `snowflake_region` (String)
diff --git a/examples/data-sources/snowflake_connections/data-source.tf b/examples/data-sources/snowflake_connections/data-source.tf
new file mode 100644
index 0000000000..b32fd92e5e
--- /dev/null
+++ b/examples/data-sources/snowflake_connections/data-source.tf
@@ -0,0 +1,25 @@
+# Simple usage
+data "snowflake_connections" "simple" {
+}
+
+output "simple_output" {
+ value = data.snowflake_connections.simple.connections
+}
+
+# Filtering (like)
+data "snowflake_connections" "like" {
+ like = "connection-name"
+}
+
+output "like_output" {
+ value = data.snowflake_connections.like.connections
+}
+
+# Filtering by prefix (like)
+data "snowflake_connections" "like_prefix" {
+ like = "prefix%"
+}
+
+output "like_prefix_output" {
+ value = data.snowflake_connections.like_prefix.connections
+}
diff --git a/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/connection_show_output_ext.go b/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/connection_show_output_ext.go
index 3bbf12c196..1e45b6794e 100644
--- a/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/connection_show_output_ext.go
+++ b/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert/connection_show_output_ext.go
@@ -7,6 +7,11 @@ import (
"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
)
+func (c *ConnectionShowOutputAssert) HasCreatedOnNotEmpty() *ConnectionShowOutputAssert {
+ c.AddAssertion(assert.ResourceShowOutputValuePresent("created_on"))
+ return c
+}
+
func (c *ConnectionShowOutputAssert) HasPrimaryIdentifier(expected sdk.ExternalObjectIdentifier) *ConnectionShowOutputAssert {
c.AddAssertion(assert.ResourceShowOutputValueSet("primary", expected.FullyQualifiedName()))
return c
diff --git a/pkg/datasources/connections.go b/pkg/datasources/connections.go
new file mode 100644
index 0000000000..ef03ffa7f1
--- /dev/null
+++ b/pkg/datasources/connections.go
@@ -0,0 +1,68 @@
+package datasources
+
+import (
+ "context"
+
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/provider"
+ "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/sdk"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+)
+
+var connectionsSchema = map[string]*schema.Schema{
+ "like": likeSchema,
+ "connections": {
+ Type: schema.TypeList,
+ Computed: true,
+ Description: "Holds the aggregated output of all connections details queries.",
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ resources.ShowOutputAttributeName: {
+ Type: schema.TypeList,
+ Computed: true,
+ Description: "Holds the output of SHOW CONNECTIONS.",
+ Elem: &schema.Resource{
+ Schema: schemas.ShowConnectionSchema,
+ },
+ },
+ },
+ },
+ },
+}
+
+func Connections() *schema.Resource {
+ return &schema.Resource{
+ ReadContext: ReadConnections,
+ Schema: connectionsSchema,
+ Description: "Datasource used to get details of filtered connections. Filtering is aligned with the current possibilities for [SHOW CONNECTIONS](https://docs.snowflake.com/en/sql-reference/sql/show-connections) query. The results of SHOW is encapsulated in one output collection `connections`.",
+ }
+}
+
+func ReadConnections(ctx context.Context, d *schema.ResourceData, meta any) diag.Diagnostics {
+ client := meta.(*provider.Context).Client
+ req := sdk.ShowConnectionRequest{}
+
+ handleLike(d, &req.Like)
+
+ connections, err := client.Connections.Show(ctx, &req)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ d.SetId("connections_read")
+
+ flattenedConnections := make([]map[string]any, len(connections))
+ for i, connection := range connections {
+ connection := connection
+ flattenedConnections[i] = map[string]any{
+ resources.ShowOutputAttributeName: []map[string]any{schemas.ConnectionToSchema(&connection)},
+ }
+ }
+ if err := d.Set("connections", flattenedConnections); err != nil {
+ return diag.FromErr(err)
+ }
+
+ return nil
+}
diff --git a/pkg/datasources/connections_acceptance_test.go b/pkg/datasources/connections_acceptance_test.go
new file mode 100644
index 0000000000..470eab5c5b
--- /dev/null
+++ b/pkg/datasources/connections_acceptance_test.go
@@ -0,0 +1,222 @@
+package datasources_test
+
+import (
+ "fmt"
+ "strings"
+ "testing"
+
+ acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/resourceshowoutputassert"
+ accConfig "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/config/model"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testenvs"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/provider/resources"
+ "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
+ "github.com/hashicorp/terraform-plugin-testing/helper/resource"
+ "github.com/hashicorp/terraform-plugin-testing/tfversion"
+)
+
+func connectionsData() string {
+ return `
+ data "snowflake_connections" "test" {
+ depends_on = [snowflake_primary_connection.test]
+ }`
+}
+
+func TestAcc_Connections_Minimal(t *testing.T) {
+ // TODO: [SNOW-1002023]: Unskip; Business Critical Snowflake Edition needed
+ // _ = testenvs.GetOrSkipTest(t, testenvs.TestFailoverGroups)
+
+ accountId := acc.TestClient().Account.GetAccountIdentifier(t)
+ id := acc.TestClient().Ids.RandomAccountObjectIdentifier()
+ connectionModel := model.PrimaryConnection("test", id.Name())
+
+ primaryConnectionAsExternalId := sdk.NewExternalObjectIdentifier(accountId, id)
+
+ dataConnections := accConfig.FromModel(t, connectionModel) + connectionsData()
+
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
+ PreCheck: func() { acc.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.RequireAbove(tfversion.Version1_5_0),
+ },
+ CheckDestroy: acc.CheckDestroy(t, resources.PrimaryConnection),
+ Steps: []resource.TestStep{
+ {
+ Config: dataConnections,
+ Check: assert.AssertThat(t,
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_connections.test", "connections.#", "1")),
+ resourceshowoutputassert.ConnectionShowOutput(t, "snowflake_primary_connection.test").
+ HasName(id.Name()).
+ HasCreatedOnNotEmpty().
+ HasSnowflakeRegion(acc.TestClient().Context.CurrentRegion(t)).
+ HasAccountLocator(acc.TestClient().GetAccountLocator()).
+ HasAccountName(accountId.AccountName()).
+ HasOrganizationName(accountId.OrganizationName()).
+ HasComment("").
+ HasIsPrimary(true).
+ HasPrimaryIdentifier(primaryConnectionAsExternalId).
+ HasFailoverAllowedToAccounts(accountId).
+ HasConnectionUrl(
+ acc.TestClient().Connection.GetConnectionUrl(accountId.OrganizationName(), id.Name()),
+ ),
+ ),
+ },
+ },
+ })
+}
+
+func TestAcc_Connections_Complete(t *testing.T) {
+ // TODO: [SNOW-1002023]: Unskip; Business Critical Snowflake Edition needed
+ _ = testenvs.GetOrSkipTest(t, testenvs.TestFailoverGroups)
+
+ accountId := acc.TestClient().Account.GetAccountIdentifier(t)
+ secondaryAccountId := acc.SecondaryTestClient().Account.GetAccountIdentifier(t)
+
+ id := acc.TestClient().Ids.RandomAccountObjectIdentifier()
+ connectionModel := model.PrimaryConnection("test", id.Name()).
+ WithEnableFailover(secondaryAccountId).
+ WithComment("test comment")
+
+ primaryConnectionAsExternalId := sdk.NewExternalObjectIdentifier(accountId, id)
+
+ dataConnections := accConfig.FromModel(t, connectionModel) + connectionsData()
+
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
+ PreCheck: func() { acc.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.RequireAbove(tfversion.Version1_5_0),
+ },
+ CheckDestroy: acc.CheckDestroy(t, resources.PrimaryConnection),
+ Steps: []resource.TestStep{
+ {
+ Config: dataConnections,
+ Check: assert.AssertThat(t,
+ assert.Check(resource.TestCheckResourceAttr("data.snowflake_connections.test", "connections.#", "1")),
+ resourceshowoutputassert.ConnectionShowOutput(t, "snowflake_connection.test").
+ HasName(id.Name()).
+ HasCreatedOnNotEmpty().
+ HasSnowflakeRegion(acc.TestClient().Context.CurrentRegion(t)).
+ HasAccountLocator(acc.TestClient().GetAccountLocator()).
+ HasAccountName(accountId.AccountName()).
+ HasOrganizationName(accountId.OrganizationName()).
+ HasComment("test comment").
+ HasIsPrimary(true).
+ HasPrimaryIdentifier(primaryConnectionAsExternalId).
+ HasFailoverAllowedToAccounts(accountId, secondaryAccountId).
+ HasConnectionUrl(
+ acc.TestClient().Connection.GetConnectionUrl(accountId.OrganizationName(), id.Name()),
+ ),
+ ),
+ },
+ },
+ })
+}
+
+func TestAcc_Connections_Filtering(t *testing.T) {
+ // TODO: [SNOW-1002023]: Unskip; Business Critical Snowflake Edition needed
+ _ = testenvs.GetOrSkipTest(t, testenvs.TestFailoverGroups)
+
+ // TODO: [SNOW-1788041] - need to uppercase as connection name in snowflake is returned in uppercase
+ prefix := random.AlphaN(4)
+ prefix = strings.ToUpper(prefix)
+
+ idOne := acc.TestClient().Ids.RandomAccountObjectIdentifierWithPrefix(prefix)
+ idTwo := acc.TestClient().Ids.RandomAccountObjectIdentifierWithPrefix(prefix)
+ idThree := acc.TestClient().Ids.RandomAccountObjectIdentifier()
+
+ connectionModelOne := model.PrimaryConnection("c1", idOne.Name())
+ connectionModelTwo := model.PrimaryConnection("c2", idTwo.Name())
+ connectionModelThree := model.PrimaryConnection("c3", idThree.Name())
+
+ configWithLike := accConfig.FromModel(t, connectionModelOne) +
+ accConfig.FromModel(t, connectionModelTwo) +
+ accConfig.FromModel(t, connectionModelThree)
+
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
+ PreCheck: func() { acc.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.RequireAbove(tfversion.Version1_5_0),
+ },
+ CheckDestroy: acc.CheckDestroy(t, resources.PrimaryConnection),
+ Steps: []resource.TestStep{
+ // with like
+ {
+ Config: configWithLike + connectionDatasourceWithLike(prefix+"%"),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr("data.snowflake_connections.test", "connections.#", "2"),
+ ),
+ },
+ },
+ })
+}
+
+func TestAcc_Connections_FilteringWithReplica(t *testing.T) {
+ // TODO: [SNOW-1002023]: Unskip; Business Critical Snowflake Edition needed
+ _ = testenvs.GetOrSkipTest(t, testenvs.TestFailoverGroups)
+
+ // TODO: [SNOW-1788041] - need to uppercase as connection name in snowflake is returned in uppercase
+ prefix := random.AlphaN(4)
+ prefix = strings.ToUpper(prefix)
+
+ idOne := acc.TestClient().Ids.RandomAccountObjectIdentifierWithPrefix(prefix)
+ idTwo := acc.SecondaryTestClient().Ids.RandomAccountObjectIdentifierWithPrefix(prefix)
+
+ accountId := acc.TestClient().Account.GetAccountIdentifier(t)
+
+ _, cleanup := acc.SecondaryTestClient().Connection.Create(t, idTwo)
+ t.Cleanup(cleanup)
+
+ primaryConnectionAsExternalId := sdk.NewExternalObjectIdentifier(accountId, idTwo)
+ acc.SecondaryTestClient().Connection.Alter(t, sdk.NewAlterConnectionRequest(idTwo).
+ WithEnableConnectionFailover(*sdk.NewEnableConnectionFailoverRequest([]sdk.AccountIdentifier{accountId})))
+
+ connectionModelOne := model.PrimaryConnection("c1", idOne.Name())
+ connectionModelTwo := model.SecondaryConnection("c2", primaryConnectionAsExternalId.FullyQualifiedName(), idTwo.Name())
+
+ configWithLike := accConfig.FromModel(t, connectionModelOne) +
+ accConfig.FromModel(t, connectionModelTwo)
+
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
+ PreCheck: func() { acc.TestAccPreCheck(t) },
+ TerraformVersionChecks: []tfversion.TerraformVersionCheck{
+ tfversion.RequireAbove(tfversion.Version1_5_0),
+ },
+ CheckDestroy: acc.ComposeCheckDestroy(t, resources.PrimaryConnection, resources.SecondaryConnection),
+ Steps: []resource.TestStep{
+ // with like
+ {
+ Config: configWithLike + connectionAndSecondaryConnectionDatasourceWithLike(prefix+"%"),
+ Check: resource.ComposeTestCheckFunc(
+ resource.TestCheckResourceAttr("data.snowflake_connections.test", "connections.#", "2"),
+ ),
+ },
+ },
+ })
+}
+
+func connectionDatasourceWithLike(like string) string {
+ return fmt.Sprintf(`
+ data "snowflake_connections" "test" {
+ depends_on = [snowflake_primary_connection.c1, snowflake_primary_connection.c2, snowflake_primary_connection.c3]
+
+ like = "%s"
+ }
+`, like)
+}
+
+func connectionAndSecondaryConnectionDatasourceWithLike(like string) string {
+ return fmt.Sprintf(`
+ data "snowflake_connections" "test" {
+ depends_on = [snowflake_primary_connection.c1, snowflake_secondary_connection.c2]
+
+ like = "%s"
+ }
+`, like)
+}
diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go
index 725879873a..335a868351 100644
--- a/pkg/provider/provider.go
+++ b/pkg/provider/provider.go
@@ -570,6 +570,7 @@ func getDataSources() map[string]*schema.Resource {
return map[string]*schema.Resource{
"snowflake_accounts": datasources.Accounts(),
"snowflake_alerts": datasources.Alerts(),
+ "snowflake_connections": datasources.Connections(),
"snowflake_cortex_search_services": datasources.CortexSearchServices(),
"snowflake_current_account": datasources.CurrentAccount(),
"snowflake_current_role": datasources.CurrentRole(),
diff --git a/templates/data-sources/connections.md.tmpl b/templates/data-sources/connections.md.tmpl
new file mode 100644
index 0000000000..e974f2a33b
--- /dev/null
+++ b/templates/data-sources/connections.md.tmpl
@@ -0,0 +1,24 @@
+---
+page_title: "{{.Name}} {{.Type}} - {{.ProviderName}}"
+subcategory: ""
+description: |-
+{{ if gt (len (split .Description "")) 1 -}}
+{{ index (split .Description "") 1 | plainmarkdown | trimspace | prefixlines " " }}
+{{- else -}}
+{{ .Description | plainmarkdown | trimspace | prefixlines " " }}
+{{- end }}
+---
+
+!> **V1 release candidate** This data source is a release candidate for the V1. We do not expect significant changes in it before the V1. We will welcome any feedback and adjust the data source if needed. Any errors reported will be resolved with a higher priority. We encourage checking this data source out before the V1 release. Please follow the [migration guide](https://github.com/Snowflake-Labs/terraform-provider-snowflake/blob/main/MIGRATION_GUIDE.md#v0970--v0980) to use it.
+
+# {{.Name}} ({{.Type}})
+
+{{ .Description | trimspace }}
+
+{{ if .HasExample -}}
+## Example Usage
+
+{{ tffile (printf "examples/data-sources/%s/data-source.tf" .Name)}}
+{{- end }}
+
+{{ .SchemaMarkdown | trimspace }}