diff --git a/builtin/providers/aws/resource_aws_s3_bucket_object.go b/builtin/providers/aws/resource_aws_s3_bucket_object.go index f1a4d24aa560..c7ae47d75646 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket_object.go +++ b/builtin/providers/aws/resource_aws_s3_bucket_object.go @@ -74,6 +74,11 @@ func resourceAwsS3BucketObject() *schema.Resource { ConflictsWith: []string{"source"}, }, + "kms_key_id": &schema.Schema{ + Type: schema.TypeString, + Optional: true, + }, + "etag": &schema.Schema{ Type: schema.TypeString, // This will conflict with SSE-C and SSE-KMS encryption and multi-part upload @@ -117,6 +122,12 @@ func resourceAwsS3BucketObjectPut(d *schema.ResourceData, meta interface{}) erro return fmt.Errorf("Must specify \"source\" or \"content\" field") } + if _, ok := d.GetOk("kms_key_id"); ok { + if _, ok := d.GetOk("etag"); ok { + return fmt.Errorf("Unable to specify 'kms_key_id' and 'etag' together because 'etag' wouldn't equal the MD5 digest of the raw object data") + } + } + putInput := &s3.PutObjectInput{ Bucket: aws.String(bucket), Key: aws.String(key), @@ -143,6 +154,11 @@ func resourceAwsS3BucketObjectPut(d *schema.ResourceData, meta interface{}) erro putInput.ContentDisposition = aws.String(v.(string)) } + if v, ok := d.GetOk("kms_key_id"); ok { + putInput.SSEKMSKeyId = aws.String(v.(string)) + putInput.ServerSideEncryption = aws.String("aws:kms") + } + resp, err := s3conn.PutObject(putInput) if err != nil { return fmt.Errorf("Error putting object in S3 bucket (%s): %s", bucket, err) @@ -186,6 +202,7 @@ func resourceAwsS3BucketObjectRead(d *schema.ResourceData, meta interface{}) err d.Set("content_language", resp.ContentLanguage) d.Set("content_type", resp.ContentType) d.Set("version_id", resp.VersionId) + d.Set("kms_key_id", resp.SSEKMSKeyId) log.Printf("[DEBUG] Reading S3 Bucket Object meta: %s", resp) return nil diff --git a/builtin/providers/aws/resource_aws_s3_bucket_object_test.go b/builtin/providers/aws/resource_aws_s3_bucket_object_test.go index 0ba226cd0043..60ca49081698 100644 --- a/builtin/providers/aws/resource_aws_s3_bucket_object_test.go +++ b/builtin/providers/aws/resource_aws_s3_bucket_object_test.go @@ -247,6 +247,24 @@ func testAccCheckAWSS3BucketObjectExists(n string, obj *s3.GetObjectOutput) reso } } +func TestAccAWSS3BucketObject_kms(t *testing.T) { + rInt := acctest.RandInt() + var obj s3.GetObjectOutput + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSS3BucketObjectDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + PreConfig: func() {}, + Config: testAccAWSS3BucketObjectConfig_withKMSId(rInt), + Check: testAccCheckAWSS3BucketObjectExists("aws_s3_bucket_object.object", &obj), + }, + }, + }) +} + func testAccAWSS3BucketObjectConfigSource(randInt int, source string) string { return fmt.Sprintf(` resource "aws_s3_bucket" "object_bucket" { @@ -322,3 +340,21 @@ resource "aws_s3_bucket_object" "object" { } `, randInt, source, source) } + +func testAccAWSS3BucketObjectConfig_withKMSId(randInt int) string { + return fmt.Sprintf(` +resource "aws_kms_key" "kms_key_1" { +} + +resource "aws_s3_bucket" "object_bucket_2" { + bucket = "tf-object-test-bucket-%d" +} + +resource "aws_s3_bucket_object" "object" { + bucket = "${aws_s3_bucket.object_bucket_2.bucket}" + key = "test-key" + content = "stuff" + kms_key_id = "${aws_kms_key.kms_key_1.key_id}" +} +`, randInt) +} diff --git a/website/source/docs/providers/aws/r/s3_bucket_object.html.markdown b/website/source/docs/providers/aws/r/s3_bucket_object.html.markdown index 646e9a5dec64..89d9797b6c72 100644 --- a/website/source/docs/providers/aws/r/s3_bucket_object.html.markdown +++ b/website/source/docs/providers/aws/r/s3_bucket_object.html.markdown @@ -37,6 +37,7 @@ The following arguments are supported: * `content_language` - (Optional) The language the content is in e.g. en-US or en-GB. * `content_type` - (Optional) A standard MIME type describing the format of the object data, e.g. application/octet-stream. All Valid MIME Types are valid for this input. * `etag` - (Optional) Used to trigger updates. The only meaningful value is `${md5(file("path/to/file"))}` +* `kms_key_id` - (Optional) Specifies the AWS KMS key ID to use for object encryption. Either `source` or `content` must be provided to specify the bucket content. These two arguments are mutually-exclusive.