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: Add support for S3 Bucket Keys #16581

Merged
merged 5 commits into from
Apr 2, 2021
Merged
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
11 changes: 11 additions & 0 deletions .changelog/16581.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
```release-note:enhancement
data-source/aws_s3_bucket_object: Add `bucket_key_enabled` attribute (Support S3 Bucket Keys)
```

```release-note:enhancement
resource/aws_s3_bucket: Add `bucket_key_enabled` argument to `server_side_encryption_configuration` `rule` configuration block (Support S3 Bucket Keys)
```

```release-note:enhancement
resource/aws_s3_bucket_object: Add `bucket_key_enabled` attribute (Support S3 Bucket Keys)
```
5 changes: 5 additions & 0 deletions aws/data_source_aws_s3_bucket_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ func dataSourceAwsS3BucketObject() *schema.Resource {
Type: schema.TypeString,
Required: true,
},
"bucket_key_enabled": {
mattburgess marked this conversation as resolved.
Show resolved Hide resolved
Type: schema.TypeBool,
Computed: true,
},
"cache_control": {
Type: schema.TypeString,
Computed: true,
Expand Down Expand Up @@ -157,6 +161,7 @@ func dataSourceAwsS3BucketObjectRead(d *schema.ResourceData, meta interface{}) e

d.SetId(uniqueId)

d.Set("bucket_key_enabled", out.BucketKeyEnabled)
d.Set("cache_control", out.CacheControl)
d.Set("content_disposition", out.ContentDisposition)
d.Set("content_encoding", out.ContentEncoding)
Expand Down
65 changes: 65 additions & 0 deletions aws/data_source_aws_s3_bucket_object_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,43 @@ func TestAccDataSourceAWSS3BucketObject_kmsEncrypted(t *testing.T) {
})
}

func TestAccDataSourceAWSS3BucketObject_bucketKeyEnabled(t *testing.T) {
rInt := acctest.RandInt()

var rObj s3.GetObjectOutput
var dsObj s3.GetObjectOutput

resourceName := "aws_s3_bucket_object.object"
dataSourceName := "data.aws_s3_bucket_object.obj"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ErrorCheck: testAccErrorCheck(t, s3.EndpointsID),
Providers: testAccProviders,
PreventPostDestroyRefresh: true,
Steps: []resource.TestStep{
{
Config: testAccAWSDataSourceS3ObjectConfig_bucketKeyEnabled(rInt),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSS3BucketObjectExists(resourceName, &rObj),
testAccCheckAwsS3ObjectDataSourceExists(dataSourceName, &dsObj),
resource.TestCheckResourceAttr(dataSourceName, "content_length", "22"),
resource.TestCheckResourceAttrPair(dataSourceName, "content_type", resourceName, "content_type"),
resource.TestCheckResourceAttrPair(dataSourceName, "etag", resourceName, "etag"),
resource.TestCheckResourceAttrPair(dataSourceName, "server_side_encryption", resourceName, "server_side_encryption"),
resource.TestCheckResourceAttrPair(dataSourceName, "sse_kms_key_id", resourceName, "kms_key_id"),
resource.TestCheckResourceAttrPair(dataSourceName, "bucket_key_enabled", resourceName, "bucket_key_enabled"),
resource.TestMatchResourceAttr(dataSourceName, "last_modified", regexp.MustCompile(rfc1123RegexPattern)),
resource.TestCheckResourceAttrPair(dataSourceName, "object_lock_legal_hold_status", resourceName, "object_lock_legal_hold_status"),
resource.TestCheckResourceAttrPair(dataSourceName, "object_lock_mode", resourceName, "object_lock_mode"),
resource.TestCheckResourceAttrPair(dataSourceName, "object_lock_retain_until_date", resourceName, "object_lock_retain_until_date"),
resource.TestCheckResourceAttr(dataSourceName, "body", "Keep Calm and Carry On"),
),
},
},
})
}

func TestAccDataSourceAWSS3BucketObject_allParams(t *testing.T) {
rInt := acctest.RandInt()

Expand All @@ -172,6 +209,7 @@ func TestAccDataSourceAWSS3BucketObject_allParams(t *testing.T) {
resource.TestMatchResourceAttr(dataSourceName, "last_modified", regexp.MustCompile(rfc1123RegexPattern)),
resource.TestCheckResourceAttrPair(dataSourceName, "version_id", resourceName, "version_id"),
resource.TestCheckNoResourceAttr(dataSourceName, "body"),
resource.TestCheckResourceAttrPair(dataSourceName, "bucket_key_enabled", resourceName, "bucket_key_enabled"),
resource.TestCheckResourceAttrPair(dataSourceName, "cache_control", resourceName, "cache_control"),
resource.TestCheckResourceAttrPair(dataSourceName, "content_disposition", resourceName, "content_disposition"),
resource.TestCheckResourceAttrPair(dataSourceName, "content_encoding", resourceName, "content_encoding"),
Expand Down Expand Up @@ -506,6 +544,33 @@ data "aws_s3_bucket_object" "obj" {
`, randInt)
}

func testAccAWSDataSourceS3ObjectConfig_bucketKeyEnabled(randInt int) string {
return fmt.Sprintf(`
resource "aws_s3_bucket" "object_bucket" {
bucket = "tf-object-test-bucket-%[1]d"
}

resource "aws_kms_key" "example" {
description = "TF Acceptance Test KMS key"
deletion_window_in_days = 7
}

resource "aws_s3_bucket_object" "object" {
bucket = aws_s3_bucket.object_bucket.bucket
key = "tf-testing-obj-%[1]d-encrypted"
content = "Keep Calm and Carry On"
content_type = "text/plain"
kms_key_id = aws_kms_key.example.arn
bucket_key_enabled = true
}

data "aws_s3_bucket_object" "obj" {
bucket = aws_s3_bucket.object_bucket.bucket
key = aws_s3_bucket_object.object.key
}
`, randInt)
}

func testAccAWSDataSourceS3ObjectConfig_allParams(randInt int) string {
return fmt.Sprintf(`
resource "aws_s3_bucket" "object_bucket" {
Expand Down
9 changes: 9 additions & 0 deletions aws/resource_aws_s3_bucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,10 @@ func resourceAwsS3Bucket() *schema.Resource {
},
},
},
"bucket_key_enabled": {
Type: schema.TypeBool,
Optional: true,
},
},
},
},
Expand Down Expand Up @@ -1931,6 +1935,10 @@ func resourceAwsS3BucketServerSideEncryptionConfigurationUpdate(s3conn *s3.S3, d
ApplyServerSideEncryptionByDefault: rcDefaultRule,
}

if val, ok := rr["bucket_key_enabled"].(bool); ok {
rcRule.BucketKeyEnabled = aws.Bool(val)
}

rules = append(rules, rcRule)
}

Expand Down Expand Up @@ -2291,6 +2299,7 @@ func flattenAwsS3ServerSideEncryptionConfiguration(c *s3.ServerSideEncryptionCon
d["kms_master_key_id"] = aws.StringValue(v.ApplyServerSideEncryptionByDefault.KMSMasterKeyID)
d["sse_algorithm"] = aws.StringValue(v.ApplyServerSideEncryptionByDefault.SSEAlgorithm)
r["apply_server_side_encryption_by_default"] = []map[string]interface{}{d}
r["bucket_key_enabled"] = aws.BoolValue(v.BucketKeyEnabled)
rules = append(rules, r)
}
}
Expand Down
12 changes: 12 additions & 0 deletions aws/resource_aws_s3_bucket_object.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ func resourceAwsS3BucketObject() *schema.Resource {
ValidateFunc: validation.StringInSlice(s3.ObjectCannedACL_Values(), false),
},

"bucket_key_enabled": {
Type: schema.TypeBool,
Optional: true,
Computed: true,
},

"cache_control": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -259,6 +265,10 @@ func resourceAwsS3BucketObjectPut(d *schema.ResourceData, meta interface{}) erro
putInput.ContentDisposition = aws.String(v.(string))
}

if v, ok := d.GetOk("bucket_key_enabled"); ok {
putInput.BucketKeyEnabled = aws.Bool(v.(bool))
}

if v, ok := d.GetOk("server_side_encryption"); ok {
putInput.ServerSideEncryption = aws.String(v.(string))
}
Expand Down Expand Up @@ -347,6 +357,7 @@ func resourceAwsS3BucketObjectRead(d *schema.ResourceData, meta interface{}) err

log.Printf("[DEBUG] Reading S3 Bucket Object meta: %s", resp)

d.Set("bucket_key_enabled", resp.BucketKeyEnabled)
d.Set("cache_control", resp.CacheControl)
d.Set("content_disposition", resp.ContentDisposition)
d.Set("content_encoding", resp.ContentEncoding)
Expand Down Expand Up @@ -537,6 +548,7 @@ func resourceAwsS3BucketObjectCustomizeDiff(_ context.Context, d *schema.Resourc

func hasS3BucketObjectContentChanges(d resourceDiffer) bool {
for _, key := range []string{
"bucket_key_enabled",
"cache_control",
"content_base64",
"content_disposition",
Expand Down
96 changes: 96 additions & 0 deletions aws/resource_aws_s3_bucket_object_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,52 @@ func TestAccAWSS3BucketObject_ObjectLockRetentionStartWithSet(t *testing.T) {
})
}

func TestAccAWSS3BucketObject_objectBucketKeyEnabled(t *testing.T) {
var obj s3.GetObjectOutput
resourceName := "aws_s3_bucket_object.object"
rInt := acctest.RandInt()

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ErrorCheck: testAccErrorCheck(t, s3.EndpointsID),
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSS3BucketObjectDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSS3BucketObjectConfig_objectBucketKeyEnabled(rInt, "stuff"),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSS3BucketObjectExists(resourceName, &obj),
testAccCheckAWSS3BucketObjectBody(&obj, "stuff"),
resource.TestCheckResourceAttr(resourceName, "bucket_key_enabled", "true"),
),
},
},
})
}

func TestAccAWSS3BucketObject_bucketBucketKeyEnabled(t *testing.T) {
var obj s3.GetObjectOutput
resourceName := "aws_s3_bucket_object.object"
rInt := acctest.RandInt()

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ErrorCheck: testAccErrorCheck(t, s3.EndpointsID),
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSS3BucketObjectDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSS3BucketObjectConfig_bucketBucketKeyEnabled(rInt, "stuff"),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSS3BucketObjectExists(resourceName, &obj),
testAccCheckAWSS3BucketObjectBody(&obj, "stuff"),
resource.TestCheckResourceAttr(resourceName, "bucket_key_enabled", "true"),
),
},
},
})
}

func TestAccAWSS3BucketObject_defaultBucketSSE(t *testing.T) {
var obj1 s3.GetObjectOutput
resourceName := "aws_s3_bucket_object.object"
Expand Down Expand Up @@ -1861,6 +1907,56 @@ resource "aws_s3_bucket_object" "object" {
`, randInt, source)
}

func testAccAWSS3BucketObjectConfig_objectBucketKeyEnabled(randInt int, content string) string {
return fmt.Sprintf(`
resource "aws_kms_key" "test" {
description = "Encrypts test bucket objects"
deletion_window_in_days = 7
}

resource "aws_s3_bucket" "object_bucket" {
bucket = "tf-object-test-bucket-%[1]d"
}

resource "aws_s3_bucket_object" "object" {
bucket = aws_s3_bucket.object_bucket.bucket
key = "test-key"
content = %q
kms_key_id = aws_kms_key.test.arn
bucket_key_enabled = true
}
`, randInt, content)
}

func testAccAWSS3BucketObjectConfig_bucketBucketKeyEnabled(randInt int, content string) string {
return fmt.Sprintf(`
resource "aws_kms_key" "test" {
description = "Encrypts test bucket objects"
deletion_window_in_days = 7
}

resource "aws_s3_bucket" "object_bucket" {
bucket = "tf-object-test-bucket-%[1]d"

server_side_encryption_configuration {
rule {
apply_server_side_encryption_by_default {
kms_master_key_id = aws_kms_key.test.arn
sse_algorithm = "aws:kms"
}
bucket_key_enabled = true
}
}
}

resource "aws_s3_bucket_object" "object" {
bucket = aws_s3_bucket.object_bucket.bucket
key = "test-key"
content = %q
}
`, randInt, content)
}

func testAccAWSS3BucketObjectConfig_defaultBucketSSE(randInt int, content string) string {
return fmt.Sprintf(`
resource "aws_kms_key" "test" {
Expand Down
55 changes: 55 additions & 0 deletions aws/resource_aws_s3_bucket_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1004,6 +1004,38 @@ func TestAccAWSS3Bucket_disableDefaultEncryption_whenDefaultEncryptionIsEnabled(
})
}

func TestAccAWSS3Bucket_bucketKeyEnabled(t *testing.T) {
bucketName := acctest.RandomWithPrefix("tf-test-bucket")
resourceName := "aws_s3_bucket.arbitrary"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ErrorCheck: testAccErrorCheck(t, s3.EndpointsID),
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSS3BucketDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSS3BucketKeyEnabled(bucketName),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSS3BucketExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "server_side_encryption_configuration.#", "1"),
resource.TestCheckResourceAttr(resourceName, "server_side_encryption_configuration.0.rule.#", "1"),
resource.TestCheckResourceAttr(resourceName, "server_side_encryption_configuration.0.rule.0.apply_server_side_encryption_by_default.#", "1"),
resource.TestCheckResourceAttr(resourceName, "server_side_encryption_configuration.0.rule.0.apply_server_side_encryption_by_default.0.sse_algorithm", "aws:kms"),
resource.TestMatchResourceAttr(resourceName, "server_side_encryption_configuration.0.rule.0.apply_server_side_encryption_by_default.0.kms_master_key_id", regexp.MustCompile("^arn")),
resource.TestCheckResourceAttr(resourceName, "server_side_encryption_configuration.0.rule.0.bucket_key_enabled", "true"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"force_destroy", "acl"},
},
},
})
}

// Test TestAccAWSS3Bucket_shouldFailNotFound is designed to fail with a "plan
// not empty" error in Terraform, to check against regresssions.
// See https:/hashicorp/terraform/pull/2925
Expand Down Expand Up @@ -3543,6 +3575,29 @@ resource "aws_s3_bucket" "arbitrary" {
`, bucketName)
}

func testAccAWSS3BucketKeyEnabled(bucketName string) string {
return fmt.Sprintf(`
resource "aws_kms_key" "arbitrary" {
description = "KMS Key for Bucket %[1]s"
deletion_window_in_days = 7
}

resource "aws_s3_bucket" "arbitrary" {
bucket = %[1]q

server_side_encryption_configuration {
rule {
apply_server_side_encryption_by_default {
kms_master_key_id = aws_kms_key.arbitrary.arn
sse_algorithm = "aws:kms"
}
bucket_key_enabled = true
}
}
}
`, bucketName)
}

func testAccAWSS3BucketEnableDefaultEncryptionWithAES256(bucketName string) string {
return fmt.Sprintf(`
resource "aws_s3_bucket" "arbitrary" {
Expand Down
1 change: 1 addition & 0 deletions website/docs/d/s3_bucket_object.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ The following arguments are supported:
In addition to all arguments above, the following attributes are exported:

* `body` - Object data (see **limitations above** to understand cases in which this field is actually available)
* `bucket_key_enabled` - (Optional) Whether or not to use [Amazon S3 Bucket Keys](https://docs.aws.amazon.com/AmazonS3/latest/dev/bucket-key.html) for SSE-KMS.
* `cache_control` - Specifies caching behavior along the request/reply chain.
* `content_disposition` - Specifies presentational information for the object.
* `content_encoding` - Specifies what content encodings have been applied to the object and thus what decoding mechanisms must be applied to obtain the media-type referenced by the Content-Type header field.
Expand Down
1 change: 1 addition & 0 deletions website/docs/r/s3_bucket.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,7 @@ The `server_side_encryption_configuration` object supports the following:
The `rule` object supports the following:

* `apply_server_side_encryption_by_default` - (required) A single object for setting server-side encryption by default. (documented below)
* `bucket_key_enabled` - (Optional) Whether or not to use [Amazon S3 Bucket Keys](https://docs.aws.amazon.com/AmazonS3/latest/dev/bucket-key.html) for SSE-KMS.

The `apply_server_side_encryption_by_default` object supports the following:

Expand Down
1 change: 1 addition & 0 deletions website/docs/r/s3_bucket_object.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ This attribute is not compatible with KMS encryption, `kms_key_id` or `server_si
* `kms_key_id` - (Optional) Amazon Resource Name (ARN) of the KMS Key to use for object encryption. If the S3 Bucket has server-side encryption enabled, that value will automatically be used. If referencing the
`aws_kms_key` resource, use the `arn` attribute. If referencing the `aws_kms_alias` data source or resource, use the `target_key_arn` attribute. Terraform will only perform drift detection if a configuration value
is provided.
* `bucket_key_enabled` - (Optional) Whether or not to use [Amazon S3 Bucket Keys](https://docs.aws.amazon.com/AmazonS3/latest/dev/bucket-key.html) for SSE-KMS.
* `metadata` - (Optional) A map of keys/values to provision metadata (will be automatically prefixed by `x-amz-meta-`, note that only lowercase label are currently supported by the AWS Go API).
* `tags` - (Optional) A map of tags to assign to the object.
* `force_destroy` - (Optional) Allow the object to be deleted by removing any legal hold on any object version.
Expand Down