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

Feature: Add KMS Support to Verified Access Group #34055

Merged
merged 13 commits into from
Feb 8, 2024
3 changes: 3 additions & 0 deletions .changelog/34055.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_verifiedaccess_group: Add `sse_configuration` argument
```
88 changes: 85 additions & 3 deletions internal/service/ec2/verifiedaccess_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,25 @@ func ResourceVerifiedAccessGroup() *schema.Resource {
Type: schema.TypeString,
Optional: true,
},
"sse_configuration": {
Type: schema.TypeList,
MaxItems: 1,
Optional: true,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"customer_managed_key_enabled": {
Type: schema.TypeBool,
Optional: true,
},
"kms_key_arn": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: verify.ValidARN,
},
},
},
},
names.AttrTags: tftags.TagsSchema(),
names.AttrTagsAll: tftags.TagsSchemaComputed(),
"verifiedaccess_group_arn": {
Expand Down Expand Up @@ -99,6 +118,10 @@ func resourceVerifiedAccessGroupCreate(ctx context.Context, d *schema.ResourceDa
input.PolicyDocument = aws.String(v.(string))
}

if v, ok := d.GetOk("sse_configuration"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil {
input.SseSpecification = expandVerifiedAccessSseSpecificationRequest(v.([]interface{})[0].(map[string]interface{}))
}

output, err := conn.CreateVerifiedAccessGroup(ctx, input)

if err != nil {
Expand Down Expand Up @@ -131,6 +154,13 @@ func resourceVerifiedAccessGroupRead(ctx context.Context, d *schema.ResourceData
d.Set("description", group.Description)
d.Set("last_updated_time", group.LastUpdatedTime)
d.Set("owner", group.Owner)
if v := group.SseSpecification; v != nil {
if err := d.Set("sse_configuration", flattenVerifiedAccessSseSpecificationResponse(v)); err != nil {
return sdkdiag.AppendErrorf(diags, "setting sse_configuration: %s", err)
}
} else {
d.Set("sse_configuration", nil)
}
d.Set("verifiedaccess_group_arn", group.VerifiedAccessGroupArn)
d.Set("verifiedaccess_group_id", group.VerifiedAccessGroupId)
d.Set("verifiedaccess_instance_id", group.VerifiedAccessInstanceId)
Expand All @@ -152,7 +182,7 @@ func resourceVerifiedAccessGroupUpdate(ctx context.Context, d *schema.ResourceDa
var diags diag.Diagnostics
conn := meta.(*conns.AWSClient).EC2Client(ctx)

if d.HasChangesExcept("policy_document", "tags", "tags_all") {
if d.HasChangesExcept("policy_document", "tags", "tags_all", "sse_configuration") {
input := &ec2.ModifyVerifiedAccessGroupInput{
ClientToken: aws.String(id.UniqueId()),
VerifiedAccessGroupId: aws.String(d.Id()),
Expand All @@ -174,19 +204,35 @@ func resourceVerifiedAccessGroupUpdate(ctx context.Context, d *schema.ResourceDa
}

if d.HasChange("policy_document") {
input := &ec2.ModifyVerifiedAccessGroupPolicyInput{
in := &ec2.ModifyVerifiedAccessGroupPolicyInput{
PolicyDocument: aws.String(d.Get("policy_document").(string)),
VerifiedAccessGroupId: aws.String(d.Id()),
PolicyEnabled: aws.Bool(true),
}

_, err := conn.ModifyVerifiedAccessGroupPolicy(ctx, input)
_, err := conn.ModifyVerifiedAccessGroupPolicy(ctx, in)

if err != nil {
return sdkdiag.AppendErrorf(diags, "updating Verified Access Group (%s) policy: %s", d.Id(), err)
}
}

if d.HasChange("sse_configuration") {
in := &ec2.ModifyVerifiedAccessGroupPolicyInput{
VerifiedAccessGroupId: aws.String(d.Id()),
}

if v, ok := d.GetOk("sse_configuration"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil {
in.SseSpecification = expandVerifiedAccessSseSpecificationRequest(v.([]interface{})[0].(map[string]interface{}))
}

_, err := conn.ModifyVerifiedAccessGroupPolicy(ctx, in)

if err != nil {
return sdkdiag.AppendErrorf(diags, "updating SSE on Verified Access Group (%s) policy: %s", d.Id(), err)
}
}

return append(diags, resourceVerifiedAccessGroupRead(ctx, d, meta)...)
}

Expand All @@ -210,3 +256,39 @@ func resourceVerifiedAccessGroupDelete(ctx context.Context, d *schema.ResourceDa

return diags
}

func expandVerifiedAccessSseSpecificationRequest(tfMap map[string]interface{}) *types.VerifiedAccessSseSpecificationRequest {
if tfMap == nil {
return nil
}

apiObject := &types.VerifiedAccessSseSpecificationRequest{}

if v, ok := tfMap["kms_key_arn"].(string); ok && v != "" {
apiObject.KmsKeyArn = aws.String(v)
}

if v, ok := tfMap["customer_managed_key_enabled"].(bool); ok {
apiObject.CustomerManagedKeyEnabled = aws.Bool(v)
}

return apiObject
}

func flattenVerifiedAccessSseSpecificationResponse(apiObject *types.VerifiedAccessSseSpecificationResponse) []interface{} {
if apiObject == nil {
return nil
}

tfMap := map[string]interface{}{}

if v := apiObject.CustomerManagedKeyEnabled; v != nil {
tfMap["customer_managed_key_enabled"] = aws.ToBool(v)
}

if v := apiObject.KmsKeyArn; v != nil {
tfMap["kms_key_arn"] = aws.ToString(v)
}

return []interface{}{tfMap}
}
114 changes: 114 additions & 0 deletions internal/service/ec2/verifiedaccess_group_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,102 @@ func TestAccVerifiedAccessGroup_basic(t *testing.T) {
resource.TestCheckResourceAttrSet(resourceName, "verifiedaccess_group_arn"),
resource.TestCheckResourceAttrSet(resourceName, "verifiedaccess_group_id"),
resource.TestCheckResourceAttrSet(resourceName, "verifiedaccess_instance_id"),
resource.TestCheckResourceAttr(resourceName, "sse_configuration.0.customer_managed_key_enabled", "false"),
resource.TestCheckResourceAttr(resourceName, "sse_configuration.0.kms_key_arn", ""),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccVerifiedAccessGroup_kms(t *testing.T) {
ctx := acctest.Context(t)
var v types.VerifiedAccessGroup
resourceName := "aws_verifiedaccess_group.test"
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
policyDoc := "permit(principal, action, resource) \nwhen {\ncontext.http_request.method == \"GET\"\n};"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() {
acctest.PreCheck(ctx, t)
testAccPreCheckVerifiedAccess(ctx, t)
},
ErrorCheck: acctest.ErrorCheck(t, names.EC2),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckVerifiedAccessGroupDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccVerifiedAccessGroupConfig_kms(rName, policyDoc),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckVerifiedAccessGroupExists(ctx, resourceName, &v),
resource.TestCheckResourceAttrSet(resourceName, "creation_time"),
resource.TestCheckResourceAttr(resourceName, "sse_configuration.#", "1"),
resource.TestCheckResourceAttrSet(resourceName, "sse_configuration.0.customer_managed_key_enabled"),
resource.TestCheckResourceAttrSet(resourceName, "sse_configuration.0.kms_key_arn"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccVerifiedAccessGroup_updateKMS(t *testing.T) {
ctx := acctest.Context(t)
var v types.VerifiedAccessGroup
resourceName := "aws_verifiedaccess_group.test"
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
policyDoc := "permit(principal, action, resource) \nwhen {\ncontext.http_request.method == \"GET\"\n};"
description := sdkacctest.RandString(100)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() {
acctest.PreCheck(ctx, t)
testAccPreCheckVerifiedAccess(ctx, t)
},
ErrorCheck: acctest.ErrorCheck(t, names.EC2),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckVerifiedAccessGroupDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccVerifiedAccessGroupConfig_policy(rName, description, policyDoc),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckVerifiedAccessGroupExists(ctx, resourceName, &v),
resource.TestCheckResourceAttrSet(resourceName, "creation_time"),
resource.TestCheckResourceAttr(resourceName, "deletion_time", ""),
resource.TestCheckResourceAttr(resourceName, "description", description),
resource.TestCheckResourceAttrSet(resourceName, "last_updated_time"),
acctest.CheckResourceAttrAccountID(resourceName, "owner"),
resource.TestCheckResourceAttr(resourceName, "policy_document", policyDoc),
resource.TestCheckResourceAttr(resourceName, "tags.%", "1"),
resource.TestCheckResourceAttrSet(resourceName, "verifiedaccess_group_arn"),
resource.TestCheckResourceAttrSet(resourceName, "verifiedaccess_group_id"),
resource.TestCheckResourceAttrSet(resourceName, "verifiedaccess_instance_id"),
resource.TestCheckResourceAttr(resourceName, "sse_configuration.0.customer_managed_key_enabled", "false"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
{
Config: testAccVerifiedAccessGroupConfig_kms(rName, policyDoc),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckVerifiedAccessGroupExists(ctx, resourceName, &v),
resource.TestCheckResourceAttrSet(resourceName, "creation_time"),
resource.TestCheckResourceAttr(resourceName, "sse_configuration.#", "1"),
resource.TestCheckResourceAttrSet(resourceName, "sse_configuration.0.customer_managed_key_enabled"),
resource.TestCheckResourceAttr(resourceName, "sse_configuration.0.customer_managed_key_enabled", "true"),
resource.TestCheckResourceAttrSet(resourceName, "sse_configuration.0.kms_key_arn"),
),
},
{
Expand Down Expand Up @@ -366,6 +462,24 @@ resource "aws_verifiedaccess_group" "test" {
`)
}

func testAccVerifiedAccessGroupConfig_kms(rName, policyDoc string) string {
return acctest.ConfigCompose(testAccVerifiedAccessGroupConfig_base(rName), fmt.Sprintf(`
resource "aws_kms_key" "test_key" {
description = "KMS key for Verified Access Group test"
}

resource "aws_verifiedaccess_group" "test" {
verifiedaccess_instance_id = aws_verifiedaccess_instance_trust_provider_attachment.test.verifiedaccess_instance_id
policy_document = %[1]q

sse_configuration {
kms_key_arn = aws_kms_key.test_key.arn
customer_managed_key_enabled = true
}
}
`, policyDoc))
}

func testAccVerifiedAccessGroupConfig_tags1(rName, tagKey1, tagValue1 string) string {
return acctest.ConfigCompose(testAccVerifiedAccessGroupConfig_base(rName), fmt.Sprintf(`
resource "aws_verifiedaccess_group" "test" {
Expand Down
21 changes: 20 additions & 1 deletion website/docs/r/verifiedaccess_group.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,22 @@ resource "aws_verifiedaccess_group" "example" {
}
```

### Usage with KMS Key

```terraform
resource "aws_kms_key" "test_key" {
description = "KMS key for Verified Access Group test"
}

resource "aws_verifiedaccess_group" "test" {
verifiedaccess_instance_id = aws_verifiedaccess_instance_trust_provider_attachment.test.verifiedaccess_instance_id

server_side_encryption_configuration {
kms_key_arn = aws_kms_key.test_key.arn
}
}
```

## Argument Reference

The following arguments are required:
Expand All @@ -29,8 +45,11 @@ The following arguments are required:
The following arguments are optional:

* `description` - (Optional) Description of the verified access group.
* `tags` - (Optional) Key-value mapping of resource tags. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level.
* `policy_document` - (Optional) The policy document that is associated with this resource.
* `sse_configuration` - (Optional) Configuration block to use KMS keys for server-side encryption.
* `cmk_enabled` - (Optional) Boolean flag to indicate that the CMK should be used.
* `kms_key_arn` - (Optional) ARN of the KMS key to use.
* `tags` - (Optional) Key-value mapping of resource tags. If configured with a provider [`default_tags` configuration block](https://registry.terraform.io/providers/hashicorp/aws/latest/docs#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level.

## Attribute Reference

Expand Down
Loading