diff --git a/aws/provider.go b/aws/provider.go index 410b8844d15..ae65dda9aa4 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -426,6 +426,8 @@ func Provider() terraform.ResourceProvider { "aws_dynamodb_table": resourceAwsDynamoDbTable(), "aws_dynamodb_table_item": resourceAwsDynamoDbTableItem(), "aws_dynamodb_global_table": resourceAwsDynamoDbGlobalTable(), + "aws_ebs_default_kms_key": resourceAwsEbsDefaultKmsKey(), + "aws_ebs_encryption_by_default": resourceAwsEbsEncryptionByDefault(), "aws_ebs_snapshot": resourceAwsEbsSnapshot(), "aws_ebs_snapshot_copy": resourceAwsEbsSnapshotCopy(), "aws_ebs_volume": resourceAwsEbsVolume(), diff --git a/aws/resource_aws_ebs_default_kms_key.go b/aws/resource_aws_ebs_default_kms_key.go new file mode 100644 index 00000000000..10cc15ccd01 --- /dev/null +++ b/aws/resource_aws_ebs_default_kms_key.go @@ -0,0 +1,69 @@ +package aws + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsEbsDefaultKmsKey() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsEbsDefaultKmsKeyCreate, + Read: resourceAwsEbsDefaultKmsKeyRead, + Delete: resourceAwsEbsDefaultKmsKeyDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "key_arn": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateArn, + }, + }, + } +} + +func resourceAwsEbsDefaultKmsKeyCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + resp, err := conn.ModifyEbsDefaultKmsKeyId(&ec2.ModifyEbsDefaultKmsKeyIdInput{ + KmsKeyId: aws.String(d.Get("key_arn").(string)), + }) + if err != nil { + return fmt.Errorf("error creating EBS default KMS key: %s", err) + } + + d.SetId(aws.StringValue(resp.KmsKeyId)) + + return resourceAwsEbsDefaultKmsKeyRead(d, meta) +} + +func resourceAwsEbsDefaultKmsKeyRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + resp, err := conn.GetEbsDefaultKmsKeyId(&ec2.GetEbsDefaultKmsKeyIdInput{}) + if err != nil { + return fmt.Errorf("error reading EBS default KMS key: %s", err) + } + + d.Set("key_arn", resp.KmsKeyId) + + return nil +} + +func resourceAwsEbsDefaultKmsKeyDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + _, err := conn.ResetEbsDefaultKmsKeyId(&ec2.ResetEbsDefaultKmsKeyIdInput{}) + if err != nil { + return fmt.Errorf("error deleting EBS default KMS key: %s", err) + } + + return nil +} diff --git a/aws/resource_aws_ebs_default_kms_key_test.go b/aws/resource_aws_ebs_default_kms_key_test.go new file mode 100644 index 00000000000..8eb3cf65ec0 --- /dev/null +++ b/aws/resource_aws_ebs_default_kms_key_test.go @@ -0,0 +1,123 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSEBSDefaultKmsKey_basic(t *testing.T) { + resourceName := "aws_ebs_default_kms_key.test" + resourceNameKey := "aws_kms_key.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsEbsDefaultKmsKeyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsEbsDefaultKmsKeyConfig_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckEbsDefaultKmsKey(resourceName), + resource.TestCheckResourceAttrPair(resourceName, "key_arn", resourceNameKey, "arn"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccCheckAwsEbsDefaultKmsKeyDestroy(s *terraform.State) error { + arn, err := testAccAwsEbsDefaultKmsKeyAwsManagedDefaultKey() + if err != nil { + return err + } + + conn := testAccProvider.Meta().(*AWSClient).ec2conn + + resp, err := conn.GetEbsDefaultKmsKeyId(&ec2.GetEbsDefaultKmsKeyIdInput{}) + if err != nil { + return err + } + + // Verify that the default key is now the account's AWS-managed default CMK. + if aws.StringValue(resp.KmsKeyId) != arn.String() { + return fmt.Errorf("Default CMK (%s) is not the account's AWS-managed default CMK (%s)", aws.StringValue(resp.KmsKeyId), arn.String()) + } + + return nil +} + +func testAccCheckEbsDefaultKmsKey(name string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[name] + if !ok { + return fmt.Errorf("Not found: %s", name) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + arn, err := testAccAwsEbsDefaultKmsKeyAwsManagedDefaultKey() + if err != nil { + return err + } + + conn := testAccProvider.Meta().(*AWSClient).ec2conn + + resp, err := conn.GetEbsDefaultKmsKeyId(&ec2.GetEbsDefaultKmsKeyIdInput{}) + if err != nil { + return err + } + + // Verify that the default key is not the account's AWS-managed default CMK. + if aws.StringValue(resp.KmsKeyId) == arn.String() { + return fmt.Errorf("Default CMK (%s) is the account's AWS-managed default CMK (%s)", aws.StringValue(resp.KmsKeyId), arn.String()) + } + + return nil + } +} + +// testAccAwsEbsDefaultKmsKeyAwsManagedDefaultKey returns' the account's AWS-managed default CMK. +func testAccAwsEbsDefaultKmsKeyAwsManagedDefaultKey() (*arn.ARN, error) { + conn := testAccProvider.Meta().(*AWSClient).kmsconn + + alias, err := findKmsAliasByName(conn, "alias/aws/ebs", nil) + if err != nil { + return nil, err + } + + aliasARN, err := arn.Parse(aws.StringValue(alias.AliasArn)) + if err != nil { + return nil, err + } + + arn := arn.ARN{ + Partition: aliasARN.Partition, + Service: aliasARN.Service, + Region: aliasARN.Region, + AccountID: aliasARN.AccountID, + Resource: fmt.Sprintf("key/%s", aws.StringValue(alias.TargetKeyId)), + } + + return &arn, nil +} + +const testAccAwsEbsDefaultKmsKeyConfig_basic = ` +resource "aws_kms_key" "test" {} + +resource "aws_ebs_default_kms_key" "test" { + key_arn = "${aws_kms_key.test.arn}" +} +` diff --git a/aws/resource_aws_ebs_encryption_by_default.go b/aws/resource_aws_ebs_encryption_by_default.go new file mode 100644 index 00000000000..a34b10ccd9d --- /dev/null +++ b/aws/resource_aws_ebs_encryption_by_default.go @@ -0,0 +1,88 @@ +package aws + +import ( + "fmt" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsEbsEncryptionByDefault() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsEbsEncryptionByDefaultCreate, + Read: resourceAwsEbsEncryptionByDefaultRead, + Update: resourceAwsEbsEncryptionByDefaultUpdate, + Delete: resourceAwsEbsEncryptionByDefaultDelete, + + Schema: map[string]*schema.Schema{ + "enabled": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + }, + } +} + +func resourceAwsEbsEncryptionByDefaultCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + enabled := d.Get("enabled").(bool) + if err := setEbsEncryptionByDefault(conn, enabled); err != nil { + return fmt.Errorf("error creating EBS encryption by default (%t): %s", enabled, err) + } + + d.SetId(resource.UniqueId()) + + return resourceAwsEbsEncryptionByDefaultRead(d, meta) +} + +func resourceAwsEbsEncryptionByDefaultRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + resp, err := conn.GetEbsEncryptionByDefault(&ec2.GetEbsEncryptionByDefaultInput{}) + if err != nil { + return fmt.Errorf("error reading EBS encryption by default: %s", err) + } + + d.Set("enabled", aws.BoolValue(resp.EbsEncryptionByDefault)) + + return nil +} + +func resourceAwsEbsEncryptionByDefaultUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + enabled := d.Get("enabled").(bool) + if err := setEbsEncryptionByDefault(conn, enabled); err != nil { + return fmt.Errorf("error updating EBS encryption by default (%t): %s", enabled, err) + } + + return resourceAwsEbsEncryptionByDefaultRead(d, meta) +} + +func resourceAwsEbsEncryptionByDefaultDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + // Removing the resource disables default encryption. + if err := setEbsEncryptionByDefault(conn, false); err != nil { + return fmt.Errorf("error disabling EBS encryption by default: %s", err) + } + + return nil +} + +func setEbsEncryptionByDefault(conn *ec2.EC2, enabled bool) error { + var err error + + if enabled { + _, err = conn.EnableEbsEncryptionByDefault(&ec2.EnableEbsEncryptionByDefaultInput{}) + } else { + _, err = conn.DisableEbsEncryptionByDefault(&ec2.DisableEbsEncryptionByDefaultInput{}) + } + + return err +} diff --git a/aws/resource_aws_ebs_encryption_by_default_test.go b/aws/resource_aws_ebs_encryption_by_default_test.go new file mode 100644 index 00000000000..bc72b737dae --- /dev/null +++ b/aws/resource_aws_ebs_encryption_by_default_test.go @@ -0,0 +1,86 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSEBSEncryptionByDefault_basic(t *testing.T) { + resourceName := "aws_ebs_encryption_by_default.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsEncryptionByDefaultDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsEbsEncryptionByDefaultConfig(false), + Check: resource.ComposeTestCheckFunc( + testAccCheckEbsEncryptionByDefault(resourceName, false), + resource.TestCheckResourceAttr(resourceName, "enabled", "false"), + ), + }, + { + Config: testAccAwsEbsEncryptionByDefaultConfig(true), + Check: resource.ComposeTestCheckFunc( + testAccCheckEbsEncryptionByDefault(resourceName, true), + resource.TestCheckResourceAttr(resourceName, "enabled", "true"), + ), + }, + }, + }) +} + +func testAccCheckAwsEncryptionByDefaultDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).ec2conn + + response, err := conn.GetEbsEncryptionByDefault(&ec2.GetEbsEncryptionByDefaultInput{}) + if err != nil { + return err + } + + if aws.BoolValue(response.EbsEncryptionByDefault) != false { + return fmt.Errorf("EBS encryption by default not disabled on resource removal") + } + + return nil +} + +func testAccCheckEbsEncryptionByDefault(n string, enabled bool) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + conn := testAccProvider.Meta().(*AWSClient).ec2conn + + response, err := conn.GetEbsEncryptionByDefault(&ec2.GetEbsEncryptionByDefaultInput{}) + if err != nil { + return err + } + + if aws.BoolValue(response.EbsEncryptionByDefault) != enabled { + return fmt.Errorf("EBS encryption by default is not in expected state (%t)", enabled) + } + + return nil + } +} + +func testAccAwsEbsEncryptionByDefaultConfig(enabled bool) string { + return fmt.Sprintf(` +resource "aws_ebs_encryption_by_default" "test" { + enabled = %[1]t +} +`, enabled) +} diff --git a/website/aws.erb b/website/aws.erb index fa74a7c5916..4dd6e5ef635 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -1135,6 +1135,14 @@ aws_ami_launch_permission +