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

r/aws_s3_bucket_server_side_encryption_configuration: S3 directory buckets now support SSE-KMS #39366

Merged
merged 11 commits into from
Sep 18, 2024
Merged
3 changes: 3 additions & 0 deletions .changelog/39366.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:enhancement
resource/aws_s3_bucket_server_side_encryption_configuration: S3 directory buckets now support SSE-KMS
```
Original file line number Diff line number Diff line change
Expand Up @@ -1061,7 +1061,7 @@ func TestAccS3BucketLifecycleConfiguration_directoryBucket(t *testing.T) {
Steps: []resource.TestStep{
{
Config: testAccBucketLifecycleConfigurationConfig_directoryBucket(rName),
ExpectError: regexache.MustCompile(`directory buckets are not supported`),
ExpectError: regexache.MustCompile(`MethodNotAllowed: The specified method is not allowed against this resource`),
},
},
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,6 @@ func resourceBucketServerSideEncryptionConfigurationCreate(ctx context.Context,
return conn.PutBucketEncryption(ctx, input)
}, errCodeNoSuchBucket, errCodeOperationAborted)

if tfawserr.ErrMessageContains(err, errCodeInvalidArgument, "ServerSideEncryptionConfiguration is not valid, expected CreateBucketConfiguration") {
err = errDirectoryBucket(err)
}

if err != nil {
return sdkdiag.AppendErrorf(diags, "creating S3 Bucket (%s) Server-side Encryption Configuration: %s", bucket, err)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"fmt"
"testing"

"github.com/YakDriver/regexache"
"github.com/aws/aws-sdk-go-v2/service/s3/types"
sdkacctest "github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
Expand All @@ -32,7 +31,7 @@ func TestAccS3BucketServerSideEncryptionConfiguration_basic(t *testing.T) {
Steps: []resource.TestStep{
{
Config: testAccBucketServerSideEncryptionConfigurationConfig_basic(rName),
Check: resource.ComposeTestCheckFunc(
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckBucketServerSideEncryptionConfigurationExists(ctx, resourceName),
resource.TestCheckResourceAttrPair(resourceName, names.AttrBucket, "aws_s3_bucket.test", names.AttrBucket),
resource.TestCheckResourceAttr(resourceName, acctest.CtRulePound, acctest.Ct1),
Expand Down Expand Up @@ -446,6 +445,7 @@ func TestAccS3BucketServerSideEncryptionConfiguration_migrate_withChange(t *test
func TestAccS3BucketServerSideEncryptionConfiguration_directoryBucket(t *testing.T) {
ctx := acctest.Context(t)
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)
resourceName := "aws_s3_bucket_server_side_encryption_configuration.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
Expand All @@ -454,8 +454,24 @@ func TestAccS3BucketServerSideEncryptionConfiguration_directoryBucket(t *testing
CheckDestroy: acctest.CheckDestroyNoop,
Steps: []resource.TestStep{
{
Config: testAccBucketServerSideEncryptionConfigurationConfig_directoryBucket(rName),
ExpectError: regexache.MustCompile(`directory buckets are not supported`),
Config: testAccBucketServerSideEncryptionConfigurationConfig_directoryBucket(rName),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckBucketServerSideEncryptionConfigurationExists(ctx, resourceName),
resource.TestCheckResourceAttrPair(resourceName, names.AttrBucket, "aws_s3_directory_bucket.test", names.AttrBucket),
resource.TestCheckResourceAttr(resourceName, acctest.CtRulePound, acctest.Ct1),
resource.TestCheckResourceAttr(resourceName, "rule.0.apply_server_side_encryption_by_default.#", acctest.Ct1),
resource.TestCheckResourceAttrPair(resourceName, "rule.0.apply_server_side_encryption_by_default.0.kms_master_key_id", "aws_kms_key.test", names.AttrARN),
resource.TestCheckResourceAttr(resourceName, "rule.0.apply_server_side_encryption_by_default.0.sse_algorithm", string(types.ServerSideEncryptionAwsKms)),
resource.TestCheckResourceAttr(resourceName, "rule.0.bucket_key_enabled", acctest.CtTrue),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{
"rule.0.bucket_key_enabled",
},
},
},
})
Expand Down Expand Up @@ -631,7 +647,7 @@ resource "aws_s3_bucket_server_side_encryption_configuration" "test" {
}

func testAccBucketServerSideEncryptionConfigurationConfig_directoryBucket(rName string) string {
return acctest.ConfigCompose(testAccDirectoryBucketConfig_base(rName), `
return acctest.ConfigCompose(testAccDirectoryBucketConfig_base(rName), fmt.Sprintf(`
resource "aws_s3_directory_bucket" "test" {
bucket = local.bucket

Expand All @@ -640,15 +656,21 @@ resource "aws_s3_directory_bucket" "test" {
}
}

resource "aws_kms_key" "test" {
description = %[1]q
deletion_window_in_days = 7
}

resource "aws_s3_bucket_server_side_encryption_configuration" "test" {
bucket = aws_s3_directory_bucket.test.bucket

rule {
# This is Amazon S3 bucket default encryption.
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
kms_master_key_id = aws_kms_key.test.arn
sse_algorithm = "aws:kms"
}
bucket_key_enabled = true
}
}
`)
`, rName))
}
50 changes: 50 additions & 0 deletions internal/service/s3/object_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1769,6 +1769,40 @@ func TestAccS3Object_DirectoryBucket_DefaultTags_providerOnly(t *testing.T) {
})
}

func TestAccS3Object_DirectoryBucket_kmsSSE(t *testing.T) {
ctx := acctest.Context(t)
var obj s3.GetObjectOutput
resourceName := "aws_s3_object.object"
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)

source := testAccObjectCreateTempFile(t, "{anything will do }")
defer os.Remove(source)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(ctx, t) },
ErrorCheck: acctest.ErrorCheck(t, names.S3ServiceID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckObjectDestroy(ctx),
Steps: []resource.TestStep{
{
Config: testAccObjectConfig_directoryBucketKMSSSE(rName, source),
Check: resource.ComposeAggregateTestCheckFunc(
testAccCheckObjectExists(ctx, resourceName, &obj),
testAccCheckObjectSSE(ctx, resourceName, "aws:kms"),
testAccCheckObjectBody(&obj, "{anything will do }"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{names.AttrForceDestroy, "override_provider", names.AttrSource},
ImportStateIdFunc: testAccObjectImportStateIdFunc(resourceName),
},
},
})
}

// https://github.com/hashicorp/terraform-provider-aws/issues/32385.
func TestAccS3Object_prefix(t *testing.T) {
ctx := acctest.Context(t)
Expand Down Expand Up @@ -2945,6 +2979,22 @@ resource "aws_s3_object" "object" {
`)
}

func testAccObjectConfig_directoryBucketKMSSSE(rName, source string) string {
return acctest.ConfigCompose(testAccBucketServerSideEncryptionConfigurationConfig_directoryBucket(rName), fmt.Sprintf(`
resource "aws_s3_object" "object" {
bucket = aws_s3_directory_bucket.test.bucket
key = "test-key"
source = %[1]q

override_provider {
default_tags {
tags = {}
}
}
}
`, source))
}

func testAccObjectConfig_prefix(rName string) string {
return fmt.Sprintf(`
resource "aws_s3_bucket" "test" {
Expand Down
2 changes: 1 addition & 1 deletion internal/service/s3control/access_grant.go
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ func (r *accessGrantResource) Create(ctx context.Context, request resource.Creat
input.Tags = getTagsIn(ctx)

// "InvalidRequest: Invalid Grantee in the request".
outputRaw, err := tfresource.RetryWhenAWSErrMessageContains(ctx, propagationTimeout, func() (interface{}, error) {
outputRaw, err := tfresource.RetryWhenAWSErrMessageContains(ctx, s3PropagationTimeout, func() (interface{}, error) {
return conn.CreateAccessGrant(ctx, input)
}, errCodeInvalidRequest, "Invalid Grantee in the request")

Expand Down
6 changes: 3 additions & 3 deletions internal/service/s3control/access_grants_location.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ func (r *accessGrantsLocationResource) Create(ctx context.Context, request resou

input.Tags = getTagsIn(ctx)

outputRaw, err := tfresource.RetryWhenAWSErrCodeEquals(ctx, propagationTimeout, func() (interface{}, error) {
outputRaw, err := tfresource.RetryWhenAWSErrCodeEquals(ctx, s3PropagationTimeout, func() (interface{}, error) {
return conn.CreateAccessGrantsLocation(ctx, input)
}, errCodeInvalidIAMRole)

Expand Down Expand Up @@ -205,7 +205,7 @@ func (r *accessGrantsLocationResource) Update(ctx context.Context, request resou
return
}

_, err := tfresource.RetryWhenAWSErrCodeEquals(ctx, propagationTimeout, func() (interface{}, error) {
_, err := tfresource.RetryWhenAWSErrCodeEquals(ctx, s3PropagationTimeout, func() (interface{}, error) {
return conn.UpdateAccessGrantsLocation(ctx, input)
}, errCodeInvalidIAMRole)

Expand Down Expand Up @@ -244,7 +244,7 @@ func (r *accessGrantsLocationResource) Delete(ctx context.Context, request resou
}

// "AccessGrantsLocationNotEmptyError: Please delete access grants before deleting access grants location".
_, err := tfresource.RetryWhenAWSErrCodeEquals(ctx, propagationTimeout, func() (interface{}, error) {
_, err := tfresource.RetryWhenAWSErrCodeEquals(ctx, s3PropagationTimeout, func() (interface{}, error) {
return conn.DeleteAccessGrantsLocation(ctx, input)
}, errCodeAccessGrantsLocationNotEmptyError)

Expand Down
12 changes: 10 additions & 2 deletions internal/service/s3control/account_public_access_block.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func resourceAccountPublicAccessBlockCreate(ctx context.Context, d *schema.Resou

d.SetId(accountID)

_, err = tfresource.RetryWhenNotFound(ctx, propagationTimeout, func() (interface{}, error) {
_, err = tfresource.RetryWhenNotFound(ctx, s3PropagationTimeout, func() (interface{}, error) {
return findPublicAccessBlockByAccountID(ctx, conn, d.Id())
})

Expand Down Expand Up @@ -176,6 +176,14 @@ func resourceAccountPublicAccessBlockDelete(ctx context.Context, d *schema.Resou
return sdkdiag.AppendErrorf(diags, "deleting S3 Account Public Access Block (%s): %s", d.Id(), err)
}

_, err = tfresource.RetryUntilNotFound(ctx, s3PropagationTimeout, func() (interface{}, error) {
return findPublicAccessBlockByAccountID(ctx, conn, d.Id())
})

if err != nil {
return sdkdiag.AppendErrorf(diags, "waiting for S3 Account Public Access Block (%s) delete: %s", d.Id(), err)
}

return diags
}

Expand Down Expand Up @@ -225,7 +233,7 @@ func waitPublicAccessBlockEqual(ctx context.Context, conn *s3control.Client, acc
Pending: []string{strconv.FormatBool(false)},
Target: []string{strconv.FormatBool(true)},
Refresh: statusPublicAccessBlockEqual(ctx, conn, accountID, target),
Timeout: propagationTimeout,
Timeout: s3PropagationTimeout,
MinTimeout: 5 * time.Second,
ContinuousTargetOccurence: 2,
}
Expand Down
4 changes: 3 additions & 1 deletion internal/service/s3control/consts.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@ import (
)

const (
propagationTimeout = 2 * time.Minute
// General timeout for S3 changes to propagate.
// See https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html#ConsistencyModel.
s3PropagationTimeout = 2 * time.Minute
)
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ func testAccCheckObjectLambdaAccessPointPolicyExists(ctx context.Context, n stri
}

func testAccObjectLambdaAccessPointPolicyConfig_basic(rName string) string {
return acctest.ConfigCompose(testAccObjectLambdaAccessPointBaseConfig(rName), fmt.Sprintf(`
return acctest.ConfigCompose(testAccObjectLambdaAccessPointConfig_base(rName), fmt.Sprintf(`
data "aws_caller_identity" "current" {}

resource "aws_s3_bucket" "test" {
Expand Down Expand Up @@ -236,7 +236,7 @@ resource "aws_s3control_object_lambda_access_point_policy" "test" {
}

func testAccObjectLambdaAccessPointPolicyConfig_updated(rName string) string {
return acctest.ConfigCompose(testAccObjectLambdaAccessPointBaseConfig(rName), fmt.Sprintf(`
return acctest.ConfigCompose(testAccObjectLambdaAccessPointConfig_base(rName), fmt.Sprintf(`
data "aws_caller_identity" "current" {}

resource "aws_s3_bucket" "test" {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,20 +213,20 @@ func testAccCheckObjectLambdaAccessPointExists(ctx context.Context, n string, v
}
}

func testAccObjectLambdaAccessPointBaseConfig(rName string) string {
func testAccObjectLambdaAccessPointConfig_base(rName string) string {
return acctest.ConfigCompose(acctest.ConfigLambdaBase(rName, rName, rName), fmt.Sprintf(`
resource "aws_lambda_function" "test" {
filename = "test-fixtures/lambdatest.zip"
function_name = %[1]q
role = aws_iam_role.iam_for_lambda.arn
handler = "index.handler"
runtime = "nodejs14.x"
runtime = "nodejs20.x"
}
`, rName))
}

func testAccObjectLambdaAccessPointConfig_basic(rName string) string {
return acctest.ConfigCompose(testAccObjectLambdaAccessPointBaseConfig(rName), fmt.Sprintf(`
return acctest.ConfigCompose(testAccObjectLambdaAccessPointConfig_base(rName), fmt.Sprintf(`
resource "aws_s3_bucket" "test" {
bucket = %[1]q
}
Expand Down Expand Up @@ -257,7 +257,7 @@ resource "aws_s3control_object_lambda_access_point" "test" {
}

func testAccObjectLambdaAccessPointConfig_optionals(rName string) string {
return acctest.ConfigCompose(testAccObjectLambdaAccessPointBaseConfig(rName), fmt.Sprintf(`
return acctest.ConfigCompose(testAccObjectLambdaAccessPointConfig_base(rName), fmt.Sprintf(`
resource "aws_s3_bucket" "test" {
bucket = %[1]q
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ Provides a S3 bucket server-side encryption configuration resource.

~> **NOTE:** Destroying an `aws_s3_bucket_server_side_encryption_configuration` resource resets the bucket to [Amazon S3 bucket default encryption](https://docs.aws.amazon.com/AmazonS3/latest/userguide/default-encryption-faq.html).

-> This resource cannot be used with S3 directory buckets.

## Example Usage

```terraform
Expand Down
Loading