Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.24.9

require (
github.com/confluentinc/ccloud-sdk-go-v2/apikeys v0.4.0
github.com/confluentinc/ccloud-sdk-go-v2/byok v0.0.2
github.com/confluentinc/ccloud-sdk-go-v2/byok v0.0.9
github.com/confluentinc/ccloud-sdk-go-v2/cam v0.3.0
github.com/confluentinc/ccloud-sdk-go-v2/ccpm v0.0.1
github.com/confluentinc/ccloud-sdk-go-v2/certificate-authority v0.0.2
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBS
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/confluentinc/ccloud-sdk-go-v2/apikeys v0.4.0 h1:8fWyLwMuy8ec0MVF5Avd54UvbIxhDFhZzanHBVwgxdw=
github.com/confluentinc/ccloud-sdk-go-v2/apikeys v0.4.0/go.mod h1:wNa9Qg2e2v/+PQsUyKh+qB22hhLkPR6Ahy6rP+1jmGI=
github.com/confluentinc/ccloud-sdk-go-v2/byok v0.0.2 h1:BV/dPTFVovJPylrcr9lcSmukCVxBxyUeHBr6hp7zUNk=
github.com/confluentinc/ccloud-sdk-go-v2/byok v0.0.2/go.mod h1:MMtRTfg1g32bQrRwfqvGpe+grS5pQzeq9V+L5GKydV4=
github.com/confluentinc/ccloud-sdk-go-v2/byok v0.0.9 h1:TFz/gJ0tPnTsEE56xJwLtU5ykTpfPv0vZwI1jS1QUMg=
github.com/confluentinc/ccloud-sdk-go-v2/byok v0.0.9/go.mod h1:evP8lAu09jGJMQX54bUaJxmT17eQcN4GsmzjTAZst6w=
github.com/confluentinc/ccloud-sdk-go-v2/cam v0.3.0 h1:9MMm9VeJ8ZVg9A6ofplPPvLZH4OGP56PtrJ/WP18EtY=
github.com/confluentinc/ccloud-sdk-go-v2/cam v0.3.0/go.mod h1:vderFceIQXBEdhkulkwTZ0SPxHz4yZ3uDMne/FA4dJ8=
github.com/confluentinc/ccloud-sdk-go-v2/ccpm v0.0.1 h1:q++EceNVxARLSE5J9FO3Vbp9/dqPPep0z3AJpVC4HW8=
Expand Down
12 changes: 9 additions & 3 deletions internal/provider/data_source_byok_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,15 @@ func byokDataSource() *schema.Resource {
Description: "The id of the BYOK key",
Required: true,
},
paramAws: awsKeyDataSourceSchema(),
paramAzure: azureKeyDataSourceSchema(),
paramGcp: gcpKeyDataSourceSchema(),
paramDisplayName: {
Type: schema.TypeString,
Description: "A human-readable name for the BYOK key.",
Computed: true,
},
paramValidation: validationSchema(),
paramAws: awsKeyDataSourceSchema(),
paramAzure: azureKeyDataSourceSchema(),
paramGcp: gcpKeyDataSourceSchema(),
},
}
}
Expand Down
3 changes: 3 additions & 0 deletions internal/provider/data_source_byok_key_aws_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ func TestAccDataSourceAwsBYOKKey(t *testing.T) {
resource.TestCheckResourceAttr(fullAwsKeyResourceName, "aws.0.roles.0", "arn:aws:iam::111111111111:role/testRoleId1"),
resource.TestCheckResourceAttr(fullAwsKeyResourceName, "aws.0.roles.1", "arn:aws:iam::111111111111:role/testRoleId2"),
resource.TestCheckResourceAttr(fullAwsKeyResourceName, "aws.0.roles.2", "arn:aws:iam::111111111111:role/testRoleId3"),
resource.TestCheckResourceAttr(fullAwsKeyResourceName, "validation.0.phase", "VALID"),
resource.TestCheckResourceAttr(fullAwsKeyResourceName, "validation.0.since", "2023-01-20T10:18:30.000Z"),
resource.TestCheckResourceAttr(fullAwsKeyResourceName, "validation.0.region", "us-west-2"),
),
},
},
Expand Down
2 changes: 2 additions & 0 deletions internal/provider/data_source_byok_key_azure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ func TestAccDataSourceAzureBYOKKey(t *testing.T) {
resource.TestCheckResourceAttr(fullAzureKeyResourceName, "azure.0.key_vault_id", "/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/test-vault/providers/Microsoft.KeyVault/vaults/test-vault"),
resource.TestCheckResourceAttr(fullAzureKeyResourceName, "azure.0.key_identifier", "https://test-vault.vault.azure.net/keys/test-key/dd554e3117e74ed8bbcd43390e1e3824"),
resource.TestCheckResourceAttr(fullAzureKeyResourceName, "azure.0.application_id", "11111111-1111-1111-1111-111111111111"),
resource.TestCheckResourceAttr(fullAzureKeyResourceName, "validation.0.phase", "INITIALIZING"),
resource.TestCheckResourceAttr(fullAzureKeyResourceName, "validation.0.since", "2023-01-19T17:02:15.000Z"),
),
},
},
Expand Down
3 changes: 3 additions & 0 deletions internal/provider/data_source_byok_key_byok_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ func TestAccDataSourceGcpBYOKKey(t *testing.T) {
resource.TestCheckResourceAttr(fullGcpKeyResourceName, "id", "cck-abcde"),
resource.TestCheckResourceAttr(fullGcpKeyResourceName, "gcp.0.key_id", testGcpByokKeyId),
resource.TestCheckResourceAttr(fullGcpKeyResourceName, "gcp.0.security_group", testGcpByokSecurityGroup),
resource.TestCheckResourceAttr(fullGcpKeyResourceName, "validation.0.phase", "INVALID"),
resource.TestCheckResourceAttr(fullGcpKeyResourceName, "validation.0.since", "2023-11-23T23:37:00.000Z"),
resource.TestCheckResourceAttr(fullGcpKeyResourceName, "validation.0.message", "Key access permissions not configured correctly"),
),
},
},
Expand Down
111 changes: 108 additions & 3 deletions internal/provider/resource_byok_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ const (
paramGcpKeyId = "key_id"
paramAwsRoles = "roles"

paramValidation = "validation"
paramPhase = "phase"
paramSince = "since"
paramMessage = "message"

kindAws = "AwsKey"
kindAzure = "AzureKey"
kindGcp = "GcpKey"
Expand All @@ -35,14 +40,21 @@ func byokResource() *schema.Resource {
return &schema.Resource{
CreateContext: byokCreate,
ReadContext: byokRead,
UpdateContext: byokUpdate,
DeleteContext: byokDelete,
Importer: &schema.ResourceImporter{
StateContext: byokImport,
},
Schema: map[string]*schema.Schema{
paramAws: awsKeySchema(),
paramAzure: azureKeySchema(),
paramGcp: gcpKeySchema(),
paramDisplayName: {
Type: schema.TypeString,
Description: "A human-readable name for the BYOK key.",
Optional: true,
Copy link
Contributor

@linouk23 Kostya Linou (linouk23) Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to have both computed and optional here? What's the default value if user doesn't provide display_name in the request data?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The backend does not compute a default value for this. It's fully optional.

},
paramValidation: validationSchema(),
paramAws: awsKeySchema(),
paramAzure: azureKeySchema(),
paramGcp: gcpKeySchema(),
},
}
}
Expand Down Expand Up @@ -96,6 +108,38 @@ func gcpKeySchema() *schema.Schema {
}
}

func validationSchema() *schema.Schema {
return &schema.Schema{
Type: schema.TypeList,
Description: "Validation information for the BYOK key.",
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
paramPhase: {
Type: schema.TypeString,
Description: "The validation phase of the key (INITIALIZING, VALID, INVALID).",
Computed: true,
},
paramSince: {
Type: schema.TypeString,
Description: "Timestamp when the key entered the current validation phase.",
Computed: true,
},
paramMessage: {
Type: schema.TypeString,
Description: "Optional validation message providing additional details.",
Computed: true,
},
paramRegion: {
Type: schema.TypeString,
Description: "Region information for successfully validated keys.",
Computed: true,
},
},
},
}
}

func azureKeySchema() *schema.Schema {
return &schema.Schema{
Type: schema.TypeList,
Expand Down Expand Up @@ -134,6 +178,11 @@ func byokCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) d
c := meta.(*Client)

createByokKeyRequest := byok.NewByokV1Key()

// Set display name
displayName := d.Get(paramDisplayName).(string)
Copy link
Contributor

@linouk23 Kostya Linou (linouk23) Jan 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recall that some resources only assign this attribute if it is not empty. Will the server still process the request if name is an empty string ("")?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, an patching with empty works. Tried an example with the provider to be sure:

Changes to Outputs:
  ~ byok_key_display_name = "My AWS BYOK Encryption Key" -> ""
  
  
  Apply complete! Resources: 0 added, 2 changed, 0 destroyed.

Outputs:

...
byok_key_display_name = ""

createByokKeyRequest.SetDisplayName(displayName)

_, isAwsKey := d.GetOk(paramAws)
_, isAzureKey := d.GetOk(paramAzure)
_, isGcpKey := d.GetOk(paramGcp)
Expand Down Expand Up @@ -246,6 +295,28 @@ func readKeyAndSetAttributes(ctx context.Context, d *schema.ResourceData, meta i
}

func setKeyAttributes(d *schema.ResourceData, byokKey byok.ByokV1Key) (*schema.ResourceData, error) {
// Set display name
if err := d.Set(paramDisplayName, byokKey.GetDisplayName()); err != nil {
return nil, err
}

// Set validation information if available
if validation, ok := byokKey.GetValidationOk(); ok {
validationMap := map[string]interface{}{
paramPhase: validation.GetPhase(),
paramSince: validation.GetSince().Format("2006-01-02T15:04:05.000Z"),
}
if message, messageOk := validation.GetMessageOk(); messageOk {
validationMap[paramMessage] = *message
}
if region, regionOk := validation.GetRegionOk(); regionOk {
validationMap[paramRegion] = *region
}
if err := d.Set(paramValidation, []interface{}{validationMap}); err != nil {
return nil, err
}
}

oneOfKeys := byokKey.GetKey()

switch {
Expand Down Expand Up @@ -278,6 +349,40 @@ func setKeyAttributes(d *schema.ResourceData, byokKey byok.ByokV1Key) (*schema.R
return d, nil
}

func byokUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
if d.HasChangeExcept(paramDisplayName) {
return diag.Errorf("error updating BYOK Key %q: only %q attribute can be updated for BYOK Key", d.Id(), paramDisplayName)
}

tflog.Debug(ctx, fmt.Sprintf("Updating BYOK Key %q", d.Id()), map[string]interface{}{byokKeyLoggingKey: d.Id()})

c := meta.(*Client)
updateByokKeyRequest := byok.NewByokV1KeyUpdate()

displayName := d.Get(paramDisplayName).(string)
updateByokKeyRequest.SetDisplayName(displayName)

updateByokKeyRequestJson, err := json.Marshal(updateByokKeyRequest)
if err != nil {
return diag.Errorf("error updating BYOK Key: error marshaling %#v to json: %s", updateByokKeyRequest, createDescriptiveError(err))
}
tflog.Debug(ctx, fmt.Sprintf("Updating BYOK Key %q: %s", d.Id(), updateByokKeyRequestJson))

_, _, err = executeKeyUpdate(ctx, c, d.Id(), *updateByokKeyRequest)
if err != nil {
return diag.Errorf("error updating BYOK Key %q: %s", d.Id(), createDescriptiveError(err))
}

tflog.Debug(ctx, fmt.Sprintf("Finished updating BYOK Key %q", d.Id()), map[string]interface{}{byokKeyLoggingKey: d.Id()})

return byokRead(ctx, d, meta)
}

func executeKeyUpdate(ctx context.Context, c *Client, id string, keyUpdate byok.ByokV1KeyUpdate) (byok.ByokV1Key, *http.Response, error) {
req := c.byokClient.KeysByokV1Api.UpdateByokV1Key(c.byokApiContext(ctx), id).ByokV1KeyUpdate(keyUpdate)
return req.Execute()
}

func byokImport(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
tflog.Debug(ctx, fmt.Sprintf("Importing BYOK Key %q", d.Id()), map[string]interface{}{byokKeyLoggingKey: d.Id()})

Expand Down
58 changes: 57 additions & 1 deletion internal/provider/resource_byok_key_aws_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,29 @@ func TestAccAwsBYOKKey(t *testing.T) {
http.StatusOK,
)

deleteAwsKeyStub := wiremock.Delete(wiremock.URLPathEqualTo(fmt.Sprintf("%s/cck-abcde", byokV1Path))).
updateAwsKeyResponse, _ := ioutil.ReadFile("../testdata/byok/aws_key_updated.json")
updateAwsKeyStub := wiremock.Patch(wiremock.URLPathEqualTo(fmt.Sprintf("%s/cck-abcde", byokV1Path))).
InScenario(awsKeyScenarioName).
WhenScenarioStateIs(wiremock.ScenarioStateStarted).
WillSetStateTo("KeyUpdated").
WillReturn(
string(updateAwsKeyResponse),
contentTypeJSONHeader,
http.StatusOK,
)

readUpdatedAwsKeyResponse, _ := ioutil.ReadFile("../testdata/byok/aws_key_updated.json")
readUpdatedAwsKeyStub := wiremock.Get(wiremock.URLPathEqualTo(fmt.Sprintf("%s/cck-abcde", byokV1Path))).
InScenario(awsKeyScenarioName).
WhenScenarioStateIs("KeyUpdated").
WillReturn(
string(readUpdatedAwsKeyResponse),
contentTypeJSONHeader,
http.StatusOK,
)

deleteAwsKeyStub := wiremock.Delete(wiremock.URLPathEqualTo(fmt.Sprintf("%s/cck-abcde", byokV1Path))).
InScenario(awsKeyScenarioName).
WillSetStateTo(scenarioStateAwsKeyHasBeenDeleted).
WillReturn(
"",
Expand All @@ -68,6 +88,8 @@ func TestAccAwsBYOKKey(t *testing.T) {

_ = wiremockClient.StubFor(createAwsKeyStub)
_ = wiremockClient.StubFor(readAwsKeyStub)
_ = wiremockClient.StubFor(updateAwsKeyStub)
_ = wiremockClient.StubFor(readUpdatedAwsKeyStub)
_ = wiremockClient.StubFor(deleteAwsKeyStub)

awsKeyResourceName := "aws_key"
Expand All @@ -83,10 +105,29 @@ func TestAccAwsBYOKKey(t *testing.T) {
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsKeyExists(fullAwsKeyResourceName),
resource.TestCheckResourceAttr(fullAwsKeyResourceName, "id", "cck-abcde"),
resource.TestCheckResourceAttr(fullAwsKeyResourceName, "display_name", "My AWS BYOK Key"),
resource.TestCheckResourceAttr(fullAwsKeyResourceName, "aws.0.key_arn", keyArn),
resource.TestCheckResourceAttr(fullAwsKeyResourceName, "aws.0.roles.0", "arn:aws:iam::111111111111:role/testRoleId1"),
resource.TestCheckResourceAttr(fullAwsKeyResourceName, "aws.0.roles.1", "arn:aws:iam::111111111111:role/testRoleId2"),
resource.TestCheckResourceAttr(fullAwsKeyResourceName, "aws.0.roles.2", "arn:aws:iam::111111111111:role/testRoleId3"),
resource.TestCheckResourceAttr(fullAwsKeyResourceName, "validation.0.phase", "VALID"),
resource.TestCheckResourceAttr(fullAwsKeyResourceName, "validation.0.since", "2023-01-20T10:18:30.000Z"),
resource.TestCheckResourceAttr(fullAwsKeyResourceName, "validation.0.region", "us-west-2"),
),
},
{
Config: testAccCheckAwsByokKeyConfigUpdated(mockServerUrl, awsKeyResourceName, keyArn),
Check: resource.ComposeTestCheckFunc(
testAccCheckAwsKeyExists(fullAwsKeyResourceName),
resource.TestCheckResourceAttr(fullAwsKeyResourceName, "id", "cck-abcde"),
resource.TestCheckResourceAttr(fullAwsKeyResourceName, "display_name", "Updated AWS BYOK Key"),
resource.TestCheckResourceAttr(fullAwsKeyResourceName, "aws.0.key_arn", keyArn),
resource.TestCheckResourceAttr(fullAwsKeyResourceName, "aws.0.roles.0", "arn:aws:iam::111111111111:role/testRoleId1"),
resource.TestCheckResourceAttr(fullAwsKeyResourceName, "aws.0.roles.1", "arn:aws:iam::111111111111:role/testRoleId2"),
resource.TestCheckResourceAttr(fullAwsKeyResourceName, "aws.0.roles.2", "arn:aws:iam::111111111111:role/testRoleId3"),
resource.TestCheckResourceAttr(fullAwsKeyResourceName, "validation.0.phase", "VALID"),
resource.TestCheckResourceAttr(fullAwsKeyResourceName, "validation.0.since", "2023-01-20T10:18:30.000Z"),
resource.TestCheckResourceAttr(fullAwsKeyResourceName, "validation.0.region", "us-west-2"),
),
},
{
Expand All @@ -108,6 +149,21 @@ func testAccCheckAwsByokKeyConfig(mockServerUrl, resourceName, keyArn string) st
endpoint = "%s"
}
resource "confluent_byok_key" "%s" {
display_name = "My AWS BYOK Key"
aws {
key_arn = "%s"
}
}
`, mockServerUrl, resourceName, keyArn)
}

func testAccCheckAwsByokKeyConfigUpdated(mockServerUrl, resourceName, keyArn string) string {
return fmt.Sprintf(`
provider "confluent" {
endpoint = "%s"
}
resource "confluent_byok_key" "%s" {
display_name = "Updated AWS BYOK Key"
aws {
key_arn = "%s"
}
Expand Down
Loading