diff --git a/docs/data-sources/cloud_accounts.md b/docs/data-sources/cloud_accounts.md
new file mode 100644
index 0000000..6d66215
--- /dev/null
+++ b/docs/data-sources/cloud_accounts.md
@@ -0,0 +1,49 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "astra_cloud_accounts Data Source - terraform-provider-astra"
+subcategory: ""
+description: |-
+ Retrieve a list of Cloud Accounts within an Organization
+---
+
+# astra_cloud_accounts (Data Source)
+
+Retrieve a list of Cloud Accounts within an Organization
+
+## Example Usage
+
+```terraform
+# AWS example
+data "astra_cloud_accounts" "awsaccounts" {
+ cloud_provider = "aws"
+ region = "us-east-1"
+}
+
+# GCP example
+data "astra_cloud_accounts" "gcpaccounts" {
+ cloud_provider = "gcp"
+ region = "us-east1"
+}
+```
+
+
+## Schema
+
+### Required
+
+- `cloud_provider` (String) The cloud provider where the Customer Key exists (Currently supported: aws, gcp)
+- `region` (String) Cloud provider region
+
+### Read-Only
+
+- `id` (String) The ID of this resource.
+- `results` (List of Object) The list of Cloud Accounts for the given Organization. (see [below for nested schema](#nestedatt--results))
+
+
+### Nested Schema for `results`
+
+Read-Only:
+
+- `organization_id` (String)
+- `provider` (String)
+- `provider_id` (String)
diff --git a/docs/data-sources/customer_key.md b/docs/data-sources/customer_key.md
new file mode 100644
index 0000000..0c847e3
--- /dev/null
+++ b/docs/data-sources/customer_key.md
@@ -0,0 +1,35 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "astra_customer_key Data Source - terraform-provider-astra"
+subcategory: ""
+description: |-
+ Retrieve a Customer Key for a given cloud provider and region
+---
+
+# astra_customer_key (Data Source)
+
+Retrieve a Customer Key for a given cloud provider and region
+
+## Example Usage
+
+```terraform
+# Read in a customer key for a given cloud provider and region
+data "astra_customer_key" "key" {
+ cloud_provider = "aws"
+ region = "us-east-1"
+}
+```
+
+
+## Schema
+
+### Required
+
+- `cloud_provider` (String) The cloud provider where the Customer Key exists (Currently supported: aws, gcp)
+- `region` (String) Cloud provider region
+
+### Read-Only
+
+- `id` (String) The ID of this resource.
+- `key_id` (String) The Customer Key ID
+- `organization_id` (String) Organization ID
diff --git a/docs/data-sources/customer_keys.md b/docs/data-sources/customer_keys.md
new file mode 100644
index 0000000..a998dd7
--- /dev/null
+++ b/docs/data-sources/customer_keys.md
@@ -0,0 +1,37 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "astra_customer_keys Data Source - terraform-provider-astra"
+subcategory: ""
+description: |-
+ Retrieve a list of Customer Keys within an Organization
+---
+
+# astra_customer_keys (Data Source)
+
+Retrieve a list of Customer Keys within an Organization
+
+## Example Usage
+
+```terraform
+# Read in all customer keys for the given Organization
+data "astra_customer_keys" "keys" {
+}
+```
+
+
+## Schema
+
+### Read-Only
+
+- `id` (String) The ID of this resource.
+- `results` (List of Object) The list of Customer Keys for the given Organization. (see [below for nested schema](#nestedatt--results))
+
+
+### Nested Schema for `results`
+
+Read-Only:
+
+- `cloud_provider` (String)
+- `key_id` (String)
+- `organization_id` (String)
+- `region` (String)
diff --git a/docs/data-sources/database.md b/docs/data-sources/database.md
index 29d2ea6..e36825e 100644
--- a/docs/data-sources/database.md
+++ b/docs/data-sources/database.md
@@ -39,7 +39,7 @@ data "astra_database" "db" {
- `keyspace` (String) Initial keyspace
- `name` (String) Database name (user provided)
- `node_count` (Number) Node count (not relevant for serverless databases)
-- `organization_id` (String) Ordg id (system generated)
+- `organization_id` (String) Organization id (system generated)
- `owner_id` (String) Owner id (system generated)
- `regions` (List of String) Cloud provider region. Get list of supported regions from regions data-source
- `replication_factor` (Number) Replication Factor (not relevant for serverless databases)
diff --git a/docs/data-sources/databases.md b/docs/data-sources/databases.md
index 3a22388..d0c50e0 100644
--- a/docs/data-sources/databases.md
+++ b/docs/data-sources/databases.md
@@ -28,7 +28,7 @@ output "existing_dbs" {
### Optional
- `cloud_provider` (String) The cloud provider
-- `status` (String) Status flter. Only return databases with matching status, if supplied. Otherwise return all databases matching other requirements
+- `status` (String) Status filter. Only return databases with matching status, if supplied. Otherwise return all databases matching other requirements
### Read-Only
diff --git a/docs/resources/customer_key.md b/docs/resources/customer_key.md
new file mode 100644
index 0000000..b6f232e
--- /dev/null
+++ b/docs/resources/customer_key.md
@@ -0,0 +1,52 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "astra_customer_key Resource - terraform-provider-astra"
+subcategory: ""
+description: |-
+ astra_customer_key provides a Customer Key resource for Astra's Bring Your Own Key (BYOK). Note that DELETE is not supported through Terraform currently. A support ticket must be created to delete Customer Keys in Astra. WARNING: Deleting a key from Astra will result in an outage. Please see https://docs.datastax.com/en/astra-db-serverless/administration/delete-customer-keys.html for more information.
+---
+
+# astra_customer_key (Resource)
+
+`astra_customer_key` provides a Customer Key resource for Astra's Bring Your Own Key (BYOK). Note that DELETE is not supported through Terraform currently. A support ticket must be created to delete Customer Keys in Astra. WARNING: Deleting a key from Astra will result in an outage. Please see https://docs.datastax.com/en/astra-db-serverless/administration/delete-customer-keys.html for more information.
+
+## Example Usage
+
+```terraform
+# AWS example
+resource "astra_customer_key" "customerkey" {
+ cloud_provider = "aws"
+ region = "us-east-1"
+ key_id = "arn:aws:kms:us-east-1:123456789012:key/1a2b3c4d-5e6f-1a2b-3c4d-5e6f1a2b3c4d"
+}
+
+# GCP example
+resource "astra_customer_key" "customerKey" {
+ cloud_provider = "gcp"
+ region = "us-east1"
+ key_id = "projects/my-project/locations/us-east1/keyRings/my-key-ring/cryptoKeys/my-key"
+}
+```
+
+
+## Schema
+
+### Required
+
+- `cloud_provider` (String) The cloud provider where the Customer Key exists (Currently supported: aws, gcp)
+- `key_id` (String) Customer Key ID. This is cloud provider specific.
+- `region` (String) Region in which the Customer Key exists.
+
+### Read-Only
+
+- `id` (String) The ID of this resource.
+- `organization_id` (String) The Astra organization ID (this is derived from the token used to create the Customer Key).
+
+## Import
+
+Import is supported using the following syntax:
+
+```shell
+# the import id is in the fomrat of /cloudProvider//region//keyId/
+terraform import astra_customer_key.customerkey 4d3c2b1a-5e6f-1a2b-3c4d-5e6f1a2b3c4d/cloudProvider/aws/region/us-east-1/keyId/arn:aws:kms:us-east-1:123456789012:key/1a2b3c4d-5e6f-1a2b-3c4d-5e6f1a2b3c4d
+```
diff --git a/examples/data-sources/astra_cloud_accounts/data-source.tf b/examples/data-sources/astra_cloud_accounts/data-source.tf
new file mode 100644
index 0000000..f0a702c
--- /dev/null
+++ b/examples/data-sources/astra_cloud_accounts/data-source.tf
@@ -0,0 +1,11 @@
+# AWS example
+data "astra_cloud_accounts" "awsaccounts" {
+ cloud_provider = "aws"
+ region = "us-east-1"
+}
+
+# GCP example
+data "astra_cloud_accounts" "gcpaccounts" {
+ cloud_provider = "gcp"
+ region = "us-east1"
+}
diff --git a/examples/data-sources/astra_customer_key/data-source.tf b/examples/data-sources/astra_customer_key/data-source.tf
new file mode 100644
index 0000000..098e7a0
--- /dev/null
+++ b/examples/data-sources/astra_customer_key/data-source.tf
@@ -0,0 +1,5 @@
+# Read in a customer key for a given cloud provider and region
+data "astra_customer_key" "key" {
+ cloud_provider = "aws"
+ region = "us-east-1"
+}
diff --git a/examples/data-sources/astra_customer_keys/data-source.tf b/examples/data-sources/astra_customer_keys/data-source.tf
new file mode 100644
index 0000000..b386596
--- /dev/null
+++ b/examples/data-sources/astra_customer_keys/data-source.tf
@@ -0,0 +1,3 @@
+# Read in all customer keys for the given Organization
+data "astra_customer_keys" "keys" {
+}
diff --git a/examples/resources/astra_customer_key/import.sh b/examples/resources/astra_customer_key/import.sh
new file mode 100644
index 0000000..dfcdb58
--- /dev/null
+++ b/examples/resources/astra_customer_key/import.sh
@@ -0,0 +1,2 @@
+# the import id is in the fomrat of /cloudProvider//region//keyId/
+terraform import astra_customer_key.customerkey 4d3c2b1a-5e6f-1a2b-3c4d-5e6f1a2b3c4d/cloudProvider/aws/region/us-east-1/keyId/arn:aws:kms:us-east-1:123456789012:key/1a2b3c4d-5e6f-1a2b-3c4d-5e6f1a2b3c4d
\ No newline at end of file
diff --git a/examples/resources/astra_customer_key/resource.tf b/examples/resources/astra_customer_key/resource.tf
new file mode 100644
index 0000000..a2affc0
--- /dev/null
+++ b/examples/resources/astra_customer_key/resource.tf
@@ -0,0 +1,13 @@
+# AWS example
+resource "astra_customer_key" "customerkey" {
+ cloud_provider = "aws"
+ region = "us-east-1"
+ key_id = "arn:aws:kms:us-east-1:123456789012:key/1a2b3c4d-5e6f-1a2b-3c4d-5e6f1a2b3c4d"
+}
+
+# GCP example
+resource "astra_customer_key" "customerKey" {
+ cloud_provider = "gcp"
+ region = "us-east1"
+ key_id = "projects/my-project/locations/us-east1/keyRings/my-key-ring/cryptoKeys/my-key"
+}
\ No newline at end of file
diff --git a/go.mod b/go.mod
index a5edf31..29c369d 100644
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,7 @@ go 1.21
toolchain go1.22.0
require (
- github.com/datastax/astra-client-go/v2 v2.2.53
+ github.com/datastax/astra-client-go/v2 v2.2.54
github.com/datastax/pulsar-admin-client-go v0.0.0-20230707040954-1a4745e07587
github.com/google/uuid v1.6.0
github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320
diff --git a/go.sum b/go.sum
index d85265e..c786d30 100644
--- a/go.sum
+++ b/go.sum
@@ -101,8 +101,8 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
-github.com/datastax/astra-client-go/v2 v2.2.53 h1:qWCBksV9rWi9WmSBW71IGhy3mL/QwkEw1BMG42ph540=
-github.com/datastax/astra-client-go/v2 v2.2.53/go.mod h1:zxXWuqDkYia7PzFIL3T7RmjChc9LN81UnfI2yB4kE7M=
+github.com/datastax/astra-client-go/v2 v2.2.54 h1:R2k9ek9zaU15cLD96np5gsj12oZhK3Z5/tSytjQagO8=
+github.com/datastax/astra-client-go/v2 v2.2.54/go.mod h1:zxXWuqDkYia7PzFIL3T7RmjChc9LN81UnfI2yB4kE7M=
github.com/datastax/pulsar-admin-client-go v0.0.0-20230707040954-1a4745e07587 h1:3jv+O0hWcz3oj3sZ9/Ov9/m1Vaqx8Ql8jp5ZeA13O5A=
github.com/datastax/pulsar-admin-client-go v0.0.0-20230707040954-1a4745e07587/go.mod h1:guL8YZ5gJINN+h5Kmja1AnuzhxLU3sHQL8o/8HYLtqk=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
diff --git a/internal/provider/data_source_cloud_accounts.go b/internal/provider/data_source_cloud_accounts.go
new file mode 100644
index 0000000..3be19ef
--- /dev/null
+++ b/internal/provider/data_source_cloud_accounts.go
@@ -0,0 +1,101 @@
+package provider
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+
+ "github.com/datastax/astra-client-go/v2/astra"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
+)
+
+func dataSourceCloudAccounts() *schema.Resource {
+ return &schema.Resource{
+ Description: "Retrieve a list of Cloud Accounts within an Organization",
+
+ ReadContext: dataSourceCloudAccountsRead,
+
+ Schema: map[string]*schema.Schema{
+ // Required inputs
+ "cloud_provider": {
+ Description: "The cloud provider where the Customer Key exists (Currently supported: aws, gcp)",
+ Type: schema.TypeString,
+ Required: true,
+ ForceNew: true,
+ ValidateFunc: validation.StringInSlice(availableBYOKCloudProviders, true),
+ DiffSuppressFunc: ignoreCase,
+ },
+ "region": {
+ Description: "Cloud provider region",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ // Computed outputs
+ "results": {
+ Type: schema.TypeList,
+ Description: "The list of Cloud Accounts for the given Organization.",
+ Computed: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "organization_id": {
+ Description: "Organization ID",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "provider": {
+ Description: "The cloud provider",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "provider_id": {
+ Description: "The provider account ID",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
+func dataSourceCloudAccountsRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ client := meta.(astraClients).astraClient.(*astra.ClientWithResponses)
+ provider := d.Get("cloud_provider").(string)
+ region := d.Get("region").(string)
+
+ cloudAccounts, err := listCloudAccounts(ctx, client, provider, region)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ if err := d.Set("results", cloudAccounts); err != nil {
+ return diag.FromErr(err)
+ }
+
+ d.SetId(id.UniqueId())
+ return nil
+}
+
+func listCloudAccounts(ctx context.Context, client *astra.ClientWithResponses, cloudProvider, region string) ([]map[string]interface{}, error) {
+ resp, err := client.GetCloudAccountsWithResponse(ctx, cloudProvider, region)
+ if err != nil {
+ return nil, err
+ }
+ if resp.StatusCode() != http.StatusOK {
+ return nil, fmt.Errorf("Error fetching Customer Keys. Status: %d, Message: %s", resp.StatusCode(), (resp.Body))
+ }
+ cloudAccounts := resp.JSON200
+ result := make([]map[string]interface{}, 0, len(*cloudAccounts))
+ for _, account := range *cloudAccounts {
+ result = append(result, map[string]interface{}{
+ "organization_id" : account.OrganizationId,
+ "provider" : account.Provider,
+ "provider_id" : account.ProviderId,
+ })
+ }
+ return result, nil
+}
\ No newline at end of file
diff --git a/internal/provider/data_source_customer_key.go b/internal/provider/data_source_customer_key.go
new file mode 100644
index 0000000..9bf75d0
--- /dev/null
+++ b/internal/provider/data_source_customer_key.go
@@ -0,0 +1,72 @@
+package provider
+
+import (
+ "context"
+ "fmt"
+ "strings"
+
+ "github.com/datastax/astra-client-go/v2/astra"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
+)
+
+func dataSourceCustomerKey() *schema.Resource {
+ return &schema.Resource{
+ Description: "Retrieve a Customer Key for a given cloud provider and region",
+
+ ReadContext: dataSourceCustomerKeyRead,
+
+ Schema: map[string]*schema.Schema{
+ // Required inputs
+ "cloud_provider": {
+ Description: "The cloud provider where the Customer Key exists (Currently supported: aws, gcp)",
+ Type: schema.TypeString,
+ Required: true,
+ ForceNew: true,
+ ValidateFunc: validation.StringInSlice(availableBYOKCloudProviders, true),
+ DiffSuppressFunc: ignoreCase,
+ },
+ "region": {
+ Description: "Cloud provider region",
+ Type: schema.TypeString,
+ Required: true,
+ },
+ // Computed outputs
+ "organization_id": {
+ Description: "Organization ID",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "key_id": {
+ Description: "The Customer Key ID",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ }
+}
+
+func dataSourceCustomerKeyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ client := meta.(astraClients).astraClient.(*astra.ClientWithResponses)
+ cloudProvider := d.Get("cloud_provider").(string)
+ region := d.Get("region").(string)
+
+ customerKeys, err := listCustomerKeys(ctx, client)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+ for _, key := range customerKeys {
+ if strings.EqualFold(cloudProvider, key["cloud_provider"].(string)) &&
+ region == key["region"].(string) {
+ orgId := key["organization_id"].(string)
+ keyId := key["key_id"].(string)
+ d.Set("organization_id", orgId)
+ d.Set("key_id", keyId)
+ d.SetId(fmt.Sprintf("%s/cloudProvider/%s/region/%s/keyId/%s", orgId, cloudProvider, region, keyId))
+ return nil
+ }
+ }
+ // key not found
+ return diag.Errorf("No Customer Key found for provider: %s, region: %s", cloudProvider, region)
+}
diff --git a/internal/provider/data_source_customer_keys.go b/internal/provider/data_source_customer_keys.go
new file mode 100644
index 0000000..ddfe862
--- /dev/null
+++ b/internal/provider/data_source_customer_keys.go
@@ -0,0 +1,89 @@
+package provider
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+
+ "github.com/datastax/astra-client-go/v2/astra"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/id"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+)
+
+func dataSourceCustomerKeys() *schema.Resource {
+ return &schema.Resource{
+ Description: "Retrieve a list of Customer Keys within an Organization",
+
+ ReadContext: dataSourceCustomerKeysRead,
+
+ Schema: map[string]*schema.Schema{
+ "results": {
+ Type: schema.TypeList,
+ Description: "The list of Customer Keys for the given Organization.",
+ Computed: true,
+ Elem: &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "organization_id": {
+ Description: "Organization ID",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "cloud_provider": {
+ Description: "The cloud provider",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "key_id": {
+ Description: "The Customer Key ID",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "region": {
+ Description: "The cloud provider region",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
+func dataSourceCustomerKeysRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ client := meta.(astraClients).astraClient.(*astra.ClientWithResponses)
+
+ customerKeys, err := listCustomerKeys(ctx, client)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ if err := d.Set("results", customerKeys); err != nil {
+ return diag.FromErr(err)
+ }
+
+ d.SetId(id.UniqueId())
+ return nil
+}
+
+func listCustomerKeys(ctx context.Context, client *astra.ClientWithResponses) ([]map[string]interface{}, error) {
+ resp, err := client.ListKeysWithResponse(ctx)
+ if err != nil {
+ return nil, err
+ }
+ if resp.StatusCode() != http.StatusOK {
+ return nil, fmt.Errorf("Error fetching Customer Keys: %s", string(resp.Body))
+ }
+ customerKeys := resp.JSON200
+ result := make([]map[string]interface{}, 0, len(*customerKeys))
+ for _, key := range *customerKeys {
+ result = append(result, map[string]interface{}{
+ "organization_id" : *key.OrganizationID,
+ "cloud_provider" : *key.CloudProvider,
+ "region" : *key.Region,
+ "key_id" : *key.KeyID,
+ })
+ }
+ return result, nil
+}
diff --git a/internal/provider/data_source_database.go b/internal/provider/data_source_database.go
index dcb462b..4d6ead5 100644
--- a/internal/provider/data_source_database.go
+++ b/internal/provider/data_source_database.go
@@ -36,7 +36,7 @@ func dataSourceDatabase() *schema.Resource {
Computed: true,
},
"organization_id": {
- Description: "Ordg id (system generated)",
+ Description: "Organization id (system generated)",
Type: schema.TypeString,
Computed: true,
},
diff --git a/internal/provider/data_source_databases.go b/internal/provider/data_source_databases.go
index 75a439f..ecd7890 100644
--- a/internal/provider/data_source_databases.go
+++ b/internal/provider/data_source_databases.go
@@ -19,7 +19,7 @@ func dataSourceDatabases() *schema.Resource {
// Optional
"status": {
Type: schema.TypeString,
- Description: "Status flter. Only return databases with matching status, if supplied. Otherwise return all databases matching other requirements",
+ Description: "Status filter. Only return databases with matching status, if supplied. Otherwise return all databases matching other requirements",
Optional: true,
},
"cloud_provider": {
diff --git a/internal/provider/provider.go b/internal/provider/provider.go
index 3539b38..83e5c61 100644
--- a/internal/provider/provider.go
+++ b/internal/provider/provider.go
@@ -72,6 +72,9 @@ func NewSDKProvider(version string) func() *schema.Provider {
"astra_roles": dataSourceRoles(),
"astra_users": dataSourceUsers(),
"astra_streaming_tenant_tokens": dataSourceStreamingTenantTokens(),
+ "astra_customer_keys": dataSourceCustomerKeys(),
+ "astra_customer_key": dataSourceCustomerKey(),
+ "astra_cloud_accounts": dataSourceCloudAccounts(),
},
ResourcesMap: map[string]*schema.Resource{
"astra_database": resourceDatabase(),
@@ -85,6 +88,7 @@ func NewSDKProvider(version string) func() *schema.Provider {
"astra_streaming_tenant": resourceStreamingTenant(),
"astra_streaming_sink": resourceStreamingSink(),
"astra_table": resourceTable(),
+ "astra_customer_key": resourceCustomerKey(),
},
Schema: map[string]*schema.Schema{
"token": {
diff --git a/internal/provider/resource_customer_key.go b/internal/provider/resource_customer_key.go
new file mode 100644
index 0000000..3f3955e
--- /dev/null
+++ b/internal/provider/resource_customer_key.go
@@ -0,0 +1,183 @@
+package provider
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "net/http"
+ "regexp"
+ "strings"
+
+ "github.com/datastax/astra-client-go/v2/astra"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
+)
+
+var availableBYOKCloudProviders = []string{
+ "aws",
+ "gcp",
+}
+
+func resourceCustomerKey() *schema.Resource {
+ return &schema.Resource{
+ Description: "`astra_customer_key` provides a Customer Key resource for Astra's Bring Your Own Key (BYOK). " +
+ "Note that DELETE is not supported through Terraform currently. " +
+ "A support ticket must be created to delete Customer Keys in Astra. " +
+ "WARNING: Deleting a key from Astra will result in an outage. " +
+ "Please see https://docs.datastax.com/en/astra-db-serverless/administration/delete-customer-keys.html for more information.",
+ CreateContext: resourceCustomerKeyCreate,
+ ReadContext: resourceCustomerKeyRead,
+ DeleteContext: resourceCustomerKeyDelete,
+
+ Importer: &schema.ResourceImporter{
+ StateContext: schema.ImportStatePassthroughContext,
+ },
+
+ Schema: map[string]*schema.Schema{
+ // Required
+ "cloud_provider": {
+ Description: "The cloud provider where the Customer Key exists (Currently supported: aws, gcp)",
+ Type: schema.TypeString,
+ Required: true,
+ ForceNew: true,
+ ValidateFunc: validation.StringInSlice(availableBYOKCloudProviders, true),
+ DiffSuppressFunc: ignoreCase,
+ },
+ "key_id": {
+ Description: "Customer Key ID. This is cloud provider specific.",
+ Type: schema.TypeString,
+ Required: true,
+ ForceNew: true,
+ },
+ "region": {
+ Description: "Region in which the Customer Key exists.",
+ Type: schema.TypeString,
+ Required: true,
+ ForceNew: true,
+ },
+ // Computed
+ "organization_id": {
+ Description: "The Astra organization ID (this is derived from the token used to create the Customer Key).",
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ }
+}
+
+func resourceCustomerKeyCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ client := meta.(astraClients).astraClient.(*astra.ClientWithResponses)
+ cloudProvider := d.Get("cloud_provider").(string)
+ keyId := d.Get("key_id").(string)
+ region := d.Get("region").(string)
+ // Determine the orgId from the current context
+ orgId, err := getOrgId(ctx, client)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+
+ // build the create Key request
+ createKeyReq := &astra.ExternalKMS{
+ OrgId: &orgId,
+ }
+ if strings.EqualFold("aws", cloudProvider) {
+ createKeyReq.Aws = buildAWSKms(region, keyId)
+ } else if strings.EqualFold("gcp", cloudProvider) {
+ createKeyReq.Gcp = buildGCPKms(region, keyId)
+ }
+ // create the Customer Key
+ resp, err := client.CreateKeyWithResponse(ctx, *createKeyReq)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+ if resp.StatusCode() != http.StatusCreated {
+ return diag.Errorf("Unexpected error creating Customer Key. Status: %d, Message: %s", resp.StatusCode(), string(resp.Body))
+ }
+ // set the data
+ if err := setCustomerKeyData(d, orgId, cloudProvider, region, keyId); err != nil {
+ return diag.FromErr(err)
+ }
+ return nil
+}
+
+func resourceCustomerKeyRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ id := d.Id()
+
+ orgId, cloudProvider, region, keyId, err := parseCustomerKeyId(id)
+ if err != nil {
+ return diag.FromErr(err)
+ }
+ setCustomerKeyData(d, orgId, cloudProvider, region, keyId)
+ return nil
+}
+
+func resourceCustomerKeyDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ // Delete not yet supported via DevOps API
+ return diag.Diagnostics{
+ diag.Diagnostic{
+ Severity: diag.Warning,
+ Summary: "Delete of Customer Key resource not supported",
+ Detail: "Please open a Support ticket to delete Customer Keys in Astra.",
+ },
+ }
+}
+
+func buildAWSKms(region, keyId string) *astra.AWSKMS {
+ return &astra.AWSKMS{
+ KeyID: &keyId,
+ Region: ®ion,
+ }
+}
+
+func buildGCPKms(region, keyId string) *astra.GCPKMS {
+ return &astra.GCPKMS{
+ KeyID: &keyId,
+ Region: ®ion,
+ }
+}
+
+func setCustomerKeyData(d *schema.ResourceData, orgId, cloudProvider, region, keyId string) error {
+ if err := d.Set("organization_id", orgId); err != nil {
+ return err
+ }
+ if err:= d.Set("cloud_provider", cloudProvider); err != nil {
+ return err
+ }
+ if err := d.Set("region", region); err != nil {
+ return err
+ }
+ if err := d.Set("key_id", keyId); err != nil {
+ return err
+ }
+
+ // generate the resource ID
+ // format: /cloudProvider//region//keyId/
+ d.SetId(fmt.Sprintf("%s/cloudProvider/%s/region/%s/keyId/%s", orgId, cloudProvider, region, keyId))
+ return nil
+}
+
+func getOrgId(ctx context.Context, client *astra.ClientWithResponses) (string, error) {
+ // get the current Org ID
+ resp, err := client.GetCurrentOrganizationWithResponse(ctx)
+ if err != nil {
+ return "", err
+ }
+ if resp.StatusCode() != http.StatusOK {
+ return "", fmt.Errorf("Error fetching current organization. Status: %d, Message: %s", resp.StatusCode(), string(resp.Body))
+ }
+ return resp.JSON200.Id, nil
+}
+
+func parseCustomerKeyId(id string) (string, string, string, string, error) {
+ re := regexp.MustCompile(`(?P.*)/cloudProvider/(?P.*)/region/(?P.*)/keyId/(?P.*)`)
+ if !re.MatchString(id) {
+ return "", "", "", "", errors.New("invalid customer key id format: expected /cloudProvider//region//keyId/")
+ }
+ matches := re.FindStringSubmatch(id)
+ orgIdIndex := re.SubexpIndex("orgid")
+ cloudProviderIndex := re.SubexpIndex("cloudprovider")
+ regionIndex := re.SubexpIndex("region")
+ keyIdIndex := re.SubexpIndex("keyid")
+ return matches[orgIdIndex], matches[cloudProviderIndex], matches[regionIndex], matches[keyIdIndex], nil
+}
\ No newline at end of file
diff --git a/internal/provider/resource_customer_key_test.go b/internal/provider/resource_customer_key_test.go
new file mode 100644
index 0000000..91114f7
--- /dev/null
+++ b/internal/provider/resource_customer_key_test.go
@@ -0,0 +1,30 @@
+package provider
+
+import (
+ "testing"
+)
+
+func TestCustomerKeyIdParser(t *testing.T) {
+ testId := "28b7d281-a2ae-4e5b-bc3f-9f80df5f5223/cloudProvider/aws/region/us-east-1/keyId/arn:aws:kms:us-east-1:388533891461:key/85e37e2b-d897-49f0-9d18-3c0daf4a7ff5"
+ orgId, cloudProvider, region, keyId, err := parseCustomerKeyId(testId)
+ if err != nil {
+ t.Logf("Customer Key ID failed to parse: \"%s\", %s", testId, err)
+ t.Fail()
+ }
+ if orgId != "28b7d281-a2ae-4e5b-bc3f-9f80df5f5223" {
+ t.Logf("Organization ID parsed from Customer Key ID: \"%s\", expected \"%s\"", orgId, "28b7d281-a2ae-4e5b-bc3f-9f80df5f5223")
+ t.Fail()
+ }
+ if cloudProvider != "aws" {
+ t.Logf("Cloud Provider parsed from Customer Key ID: \"%s\", expected \"%s\"", cloudProvider, "aws")
+ t.Fail()
+ }
+ if region != "us-east-1" {
+ t.Logf("Region parsed from Customer Key ID: \"%s\", expected \"%s\"", region, "us-east-1")
+ t.Fail()
+ }
+ if keyId != "arn:aws:kms:us-east-1:388533891461:key/85e37e2b-d897-49f0-9d18-3c0daf4a7ff5" {
+ t.Logf("Key ID parsed from Customer Key ID: \"%s\", expected \"%s\"", keyId, "arn:aws:kms:us-east-1:388533891461:key/85e37e2b-d897-49f0-9d18-3c0daf4a7ff5")
+ t.Fail()
+ }
+}
\ No newline at end of file