Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

kms: add support for external key store #40557

Merged
merged 22 commits into from
Jan 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
e0db82a
kms: add support for external key store
bschaatsbergen Dec 13, 2024
9f8bf0f
kms: refactor to remove 'cloud_hsm_cluster_id' from required UpdateIn…
bschaatsbergen Dec 13, 2024
0a5bcaf
kms: refactor to remove `cloud_hsm_cluster_id` from required UpdateIn…
bschaatsbergen Dec 13, 2024
59e8dbf
kms: update custom key store documentation to reflect XKS arguments
bschaatsbergen Dec 13, 2024
e4d1d28
Merge branch 'f/external-kms-store' of github.com:bschaatsbergen/terr…
bschaatsbergen Dec 13, 2024
16f6e04
kms: add XKS changelog
bschaatsbergen Dec 13, 2024
e0a0781
kms: remove unnecessary enum wrapper
bschaatsbergen Dec 13, 2024
268f087
kms: terrafmt docs
bschaatsbergen Dec 13, 2024
f21dbc9
kms: refactor flatten to read the `access_key_id`
bschaatsbergen Dec 13, 2024
dc45631
kms: add `xks_proxy_authentication_credential` block in docs
bschaatsbergen Dec 13, 2024
69302e7
kms: add note to changelog
bschaatsbergen Dec 16, 2024
94ff4b1
Merge branch 'main' into f/external-kms-store
jar-b Jan 10, 2025
68c1499
r/aws_kms_custom_key_store: rm default for `custom_key_store_type`
jar-b Jan 10, 2025
0385103
chore: tidy changelog
jar-b Jan 10, 2025
549ff18
docs: document missing acceptance testing env vars
jar-b Jan 10, 2025
6a9b8f0
r/aws_kms_custom_key_store: tidy flex functions
jar-b Jan 10, 2025
e722168
r/aws_kms_custom_key_store(doc): adjust optional argument format
jar-b Jan 10, 2025
069e6ea
r/aws_kms_custom_key_store: `trust_anchor_certificate` is now optional
jar-b Jan 10, 2025
fa301ab
r/aws_kms_custom_key_store: remove `xks_proxy_authentication_credenti…
jar-b Jan 13, 2025
a80b55e
r/aws_kms_custom_key_store(test): tidy serial test structure
jar-b Jan 13, 2025
2f3d6a8
Merge branch 'main' into f/external-kms-store
jar-b Jan 13, 2025
53bce24
chore: make fix-constants
jar-b Jan 13, 2025
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
6 changes: 6 additions & 0 deletions .changelog/40557.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
```release-note:enhancement
resource/aws_kms_custom_key_store: Add support for external key stores
```
```release-note:note
resource/aws_kms_custom_key_store: We cannot acceptance test the support for external key stores added in this release. The impementation is best effort and we ask for community help in testing.
```
2 changes: 2 additions & 0 deletions docs/acc-test-environment-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ Environment variables (beyond standard AWS Go SDK ones) used by acceptance testi
| `CHATBOT_TEAMS_CHANNEL_ID` | ID of the Microsoft Teams channel. |
| `CHATBOT_TEAMS_TEAM_ID` | ID of the Microsoft Teams workspace authorized with AWS Chatbot. |
| `CHATBOT_TEAMS_TENANT_ID` | ID of the Microsoft Teams tenant. |
| `CLOUD_HSM_CLUSTER_ID` | Cloud HSM cluster identifier for KMS custom key store acceptance tests. |
| `DX_CONNECTION_ID` | Identifier for Direct Connect Connection testing. |
| `DX_VIRTUAL_INTERFACE_ID` | Identifier for Direct Connect Virtual Interface testing. |
| `EC2_SECURITY_GROUP_RULES_PER_GROUP_LIMIT` | EC2 Quota for Rules per Security Group. Defaults to 50. **DEPRECATED:** Can be augmented or replaced with Service Quotas lookup. |
Expand Down Expand Up @@ -97,3 +98,4 @@ Environment variables (beyond standard AWS Go SDK ones) used by acceptance testi
| `TF_AWS_LICENSE_MANAGER_GRANT_PRINCIPAL` | ARN of a principal to share the License Manager license with. Either a root user, Organization, or Organizational Unit. |
| `TF_TEST_CLOUDFRONT_RETAIN` | Flag to disable but dangle CloudFront Distributions during testing to reduce feedback time (must be manually destroyed afterwards) |
| `TF_TEST_ELASTICACHE_RESERVED_CACHE_NODE` | Flag to enable resource tests for ElastiCache reserved nodes. Set to `1` to run tests |
| `TRUST_ANCHOR_CERTIFICATE` | Trust anchor certificate for KMS custom key store acceptance tests. |
147 changes: 137 additions & 10 deletions internal/service/kms/custom_key_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/hashicorp/terraform-provider-aws/internal/conns"
"github.com/hashicorp/terraform-provider-aws/internal/enum"
"github.com/hashicorp/terraform-provider-aws/internal/errs"
"github.com/hashicorp/terraform-provider-aws/internal/errs/sdkdiag"
tfslices "github.com/hashicorp/terraform-provider-aws/internal/slices"
Expand Down Expand Up @@ -43,23 +44,64 @@ func resourceCustomKeyStore() *schema.Resource {
Schema: map[string]*schema.Schema{
"cloud_hsm_cluster_id": {
Type: schema.TypeString,
Required: true,
Optional: true,
ForceNew: true,
},
"custom_key_store_name": {
Type: schema.TypeString,
Required: true,
},
"custom_key_store_type": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ValidateDiagFunc: enum.Validate[awstypes.CustomKeyStoreType](),
},
"key_store_password": {
Type: schema.TypeString,
Required: true,
Optional: true,
ValidateDiagFunc: validation.ToDiagFunc(validation.StringLenBetween(7, 32)),
},
"trust_anchor_certificate": {
Type: schema.TypeString,
Required: true,
Optional: true,
ForceNew: true,
},
"xks_proxy_authentication_credential": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"access_key_id": {
Type: schema.TypeString,
Required: true,
},
"raw_secret_access_key": {
Type: schema.TypeString,
Required: true,
},
},
},
},
"xks_proxy_connectivity": {
Type: schema.TypeString,
Optional: true,
ValidateDiagFunc: enum.Validate[awstypes.XksProxyConnectivityType](),
},
"xks_proxy_uri_endpoint": {
Type: schema.TypeString,
Optional: true,
},
"xks_proxy_uri_path": {
Type: schema.TypeString,
Optional: true,
},
"xks_proxy_vpc_endpoint_service_name": {
Type: schema.TypeString,
Optional: true,
},
},
}
}
Expand All @@ -70,10 +112,43 @@ func resourceCustomKeyStoreCreate(ctx context.Context, d *schema.ResourceData, m

name := d.Get("custom_key_store_name").(string)
input := &kms.CreateCustomKeyStoreInput{
CloudHsmClusterId: aws.String(d.Get("cloud_hsm_cluster_id").(string)),
CustomKeyStoreName: aws.String(name),
KeyStorePassword: aws.String(d.Get("key_store_password").(string)),
TrustAnchorCertificate: aws.String(d.Get("trust_anchor_certificate").(string)),
CustomKeyStoreName: aws.String(name),
}

if v, ok := d.GetOk("custom_key_store_type"); ok {
input.CustomKeyStoreType = awstypes.CustomKeyStoreType(v.(string))
}

if v, ok := d.GetOk("cloud_hsm_cluster_id"); ok {
input.CloudHsmClusterId = aws.String(v.(string))
}

if v, ok := d.GetOk("key_store_password"); ok {
input.KeyStorePassword = aws.String(v.(string))
}

if v, ok := d.GetOk("trust_anchor_certificate"); ok {
input.TrustAnchorCertificate = aws.String(v.(string))
}

if v, ok := d.GetOk("xks_proxy_authentication_credential"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil {
input.XksProxyAuthenticationCredential = expandXksProxyAuthenticationCredential(v.([]interface{}))
}

if v, ok := d.GetOk("xks_proxy_connectivity"); ok {
input.XksProxyConnectivity = awstypes.XksProxyConnectivityType(v.(string))
}

if v, ok := d.GetOk("xks_proxy_uri_endpoint"); ok {
input.XksProxyUriEndpoint = aws.String(v.(string))
}

if v, ok := d.GetOk("xks_proxy_uri_path"); ok {
input.XksProxyUriPath = aws.String(v.(string))
}

if v, ok := d.GetOk("xks_proxy_vpc_endpoint_service_name"); ok {
input.XksProxyVpcEndpointServiceName = aws.String(v.(string))
}

output, err := conn.CreateCustomKeyStore(ctx, input)
Expand Down Expand Up @@ -103,11 +178,17 @@ func resourceCustomKeyStoreRead(ctx context.Context, d *schema.ResourceData, met
return sdkdiag.AppendErrorf(diags, "reading KMS Custom Key Store (%s): %s", d.Id(), err)
}

d.Set("cloud_hsm_cluster_id", output.CloudHsmClusterId)
d.Set("custom_key_store_name", output.CustomKeyStoreName)
d.Set("cloud_hsm_cluster_id", output.CloudHsmClusterId)
d.Set("custom_key_store_type", output.CustomKeyStoreType)
d.Set("key_store_password", d.Get("key_store_password"))
d.Set("trust_anchor_certificate", output.TrustAnchorCertificate)

d.Set("xks_proxy_connectivity", output.XksProxyConfiguration.Connectivity)
d.Set("xks_proxy_uri_endpoint", output.XksProxyConfiguration.UriEndpoint)
d.Set("xks_proxy_uri_path", output.XksProxyConfiguration.UriPath)
d.Set("xks_proxy_vpc_endpoint_service_name", output.XksProxyConfiguration.VpcEndpointServiceName)

return diags
}

Expand All @@ -116,8 +197,11 @@ func resourceCustomKeyStoreUpdate(ctx context.Context, d *schema.ResourceData, m
conn := meta.(*conns.AWSClient).KMSClient(ctx)

input := &kms.UpdateCustomKeyStoreInput{
CloudHsmClusterId: aws.String(d.Get("cloud_hsm_cluster_id").(string)),
CustomKeyStoreId: aws.String(d.Id()),
CustomKeyStoreId: aws.String(d.Id()),
}

if d.HasChange("cloud_hsm_cluster_id") {
input.CloudHsmClusterId = aws.String(d.Get("cloud_hsm_cluster_id").(string))
}

if d.HasChange("custom_key_store_name") {
Expand All @@ -128,6 +212,26 @@ func resourceCustomKeyStoreUpdate(ctx context.Context, d *schema.ResourceData, m
input.KeyStorePassword = aws.String(d.Get("key_store_password").(string))
}

if d.HasChange("xks_proxy_authentication_credential") {
input.XksProxyAuthenticationCredential = expandXksProxyAuthenticationCredential(d.Get("xks_proxy_authentication_credential").(*schema.Set).List())
}

if d.HasChange("xks_proxy_connectivity") {
input.XksProxyConnectivity = awstypes.XksProxyConnectivityType(d.Get("xks_proxy_connectivity").(string))
}

if d.HasChange("xks_proxy_uri_endpoint") {
input.XksProxyUriEndpoint = aws.String(d.Get("xks_proxy_uri_endpoint").(string))
}

if d.HasChange("xks_proxy_uri_path") {
input.XksProxyUriPath = aws.String(d.Get("xks_proxy_uri_path").(string))
}

if d.HasChange("xks_proxy_vpc_endpoint_service_name") {
input.XksProxyVpcEndpointServiceName = aws.String(d.Get("xks_proxy_vpc_endpoint_service_name").(string))
}

_, err := conn.UpdateCustomKeyStore(ctx, input)

if err != nil {
Expand Down Expand Up @@ -198,3 +302,26 @@ func findCustomKeyStores(ctx context.Context, conn *kms.Client, input *kms.Descr

return output, nil
}

func expandXksProxyAuthenticationCredential(tfList []interface{}) *awstypes.XksProxyAuthenticationCredentialType {
if len(tfList) == 0 || tfList[0] == nil {
return nil
}

tfMap, ok := tfList[0].(map[string]interface{})
if !ok {
return nil
}

apiObject := &awstypes.XksProxyAuthenticationCredentialType{}

if v, ok := tfMap["access_key_id"].(string); ok {
apiObject.AccessKeyId = aws.String(v)
}

if v, ok := tfMap["raw_secret_access_key"].(string); ok {
apiObject.RawSecretAccessKey = aws.String(v)
}

return apiObject
}
4 changes: 3 additions & 1 deletion internal/service/kms/kms_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ func TestAccKMS_serial(t *testing.T) {
acctest.CtBasic: testAccCustomKeyStore_basic,
"update": testAccCustomKeyStore_update,
acctest.CtDisappears: testAccCustomKeyStore_disappears,
"DataSource_basic": testAccCustomKeyStoreDataSource_basic,
},
"CustomKeyStoreDataSource": {
acctest.CtBasic: testAccCustomKeyStoreDataSource_basic,
},
}

Expand Down
63 changes: 59 additions & 4 deletions website/docs/r/kms_custom_key_store.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Terraform resource for managing an AWS KMS (Key Management) Custom Key Store.

## Example Usage

### Basic Usage
### CloudHSM

```terraform
resource "aws_kms_custom_key_store" "test" {
Expand All @@ -24,14 +24,69 @@ resource "aws_kms_custom_key_store" "test" {
}
```

### External Key Store (VPC)

```terraform
resource "aws_kms_custom_key_store" "example" {
custom_key_store_name = "example-vpc-xks"
custom_key_store_type = "EXTERNAL_KEY_STORE"

xks_proxy_authentication_credential {
access_key_id = var.ephemeral_access_key_id
raw_secret_access_key = var.ephemeral_secret_access_key
}
xks_proxy_connectivity = "VPC_ENDPOINT_SERVICE"
xks_proxy_uri_endpoint = "https://myproxy-private.xks.example.com"
xks_proxy_uri_path = "/kms/xks/v1"
xks_proxy_vpc_endpoint_service_name = "com.amazonaws.vpce.us-east-1.vpce-svc-example"
}
```

### External Key Store (Public)

```terraform
resource "aws_kms_custom_key_store" "example" {
custom_key_store_name = "example-public-xks"
custom_key_store_type = "EXTERNAL_KEY_STORE"

xks_proxy_authentication_credential {
access_key_id = var.ephemeral_access_key_id
raw_secret_access_key = var.ephemeral_secret_access_key
}
xks_proxy_connectivity = "PUBLIC_ENDPOINT"
xks_proxy_uri_endpoint = "https://myproxy.xks.example.com"
xks_proxy_uri_path = "/kms/xks/v1"
}
```

## Argument Reference

The following arguments are required:

* `cloud_hsm_cluster_id` - (Required) Cluster ID of CloudHSM.
* `custom_key_store_name` - (Required) Unique name for Custom Key Store.
* `key_store_password` - (Required) Password for `kmsuser` on CloudHSM.
* `trust_anchor_certificate` - (Required) Customer certificate used for signing on CloudHSM.

The following arguments are optional:

* `custom_key_store_type` - (Optional, ForceNew) Specifies the type of key store to create. Valid values are `AWS_CLOUDHSM` and `EXTERNAL_KEY_STORE`. If omitted, AWS will default the value to `AWS_CLOUDHSM`.

If `custom_key_store_type` is `AWS_CLOUDHSM`, the following optional arguments must be set:

* `cloud_hsm_cluster_id` - (Optional) Cluster ID of CloudHSM.
* `key_store_password` - (Optional) Specifies the `kmsuser` password for an AWS CloudHSM key store.
* `trust_anchor_certificate` - (Optional) Specifies the certificate for an AWS CloudHSM key store.

If `custom_key_store_type` is `EXTERNAL_KEY_STORE`, the following optional arguments must be set:

* `xks_proxy_authentication_credential` - (Optional) Specifies an authentication credential for the external key store proxy (XKS proxy). See [`xks_proxy_authentication_credential` attribute reference](#xks_proxy_authentication_credential-argument-reference) below.
* `xks_proxy_connectivity` - (Optional) Indicates how AWS KMS communicates with the external key store proxy.
* `xks_proxy_uri_endpoint` - (Optional) Specifies the endpoint that AWS KMS uses to send requests to the external key store proxy (XKS proxy).
* `xks_proxy_uri_path` - (Optional) Specifies the base path to the proxy APIs for this external key store. To find this value, see the documentation for your external key store proxy.
* `xks_proxy_vpc_endpoint_service_name` - (Optional) Specifies the name of the Amazon VPC endpoint service for interface endpoints that is used to communicate with your external key store proxy (XKS proxy). This argument is required when the value of `xks_proxy_connectivity` is `VPC_ENDPOINT_SERVICE`.

### `xks_proxy_authentication_credential` Argument Reference

* `access_key_id` - (Required) A unique identifier for the raw secret access key.
* `raw_secret_access_key` - (Required) A secret string of 43-64 characters.

## Attribute Reference

Expand Down
Loading