From e1ea427649c12f6a0c2141d9e451187236fb18f0 Mon Sep 17 00:00:00 2001 From: Paul Hinze Date: Fri, 15 May 2015 15:18:05 -0400 Subject: [PATCH] provider/aws: support ec2 termination protection closes #1233 --- .../providers/aws/resource_aws_instance.go | 33 +++++++--- .../aws/resource_aws_instance_test.go | 66 +++++++++++++++++++ .../providers/aws/r/instance.html.markdown | 2 + 3 files changed, 93 insertions(+), 8 deletions(-) diff --git a/builtin/providers/aws/resource_aws_instance.go b/builtin/providers/aws/resource_aws_instance.go index 956a73970..e51892475 100644 --- a/builtin/providers/aws/resource_aws_instance.go +++ b/builtin/providers/aws/resource_aws_instance.go @@ -140,6 +140,11 @@ func resourceAwsInstance() *schema.Resource { Optional: true, }, + "disable_api_termination": &schema.Schema{ + Type: schema.TypeBool, + Optional: true, + }, + "iam_instance_profile": &schema.Schema{ Type: schema.TypeString, ForceNew: true, @@ -336,14 +341,15 @@ func resourceAwsInstanceCreate(d *schema.ResourceData, meta interface{}) error { // Build the creation struct runOpts := &ec2.RunInstancesInput{ - ImageID: aws.String(d.Get("ami").(string)), - Placement: placement, - InstanceType: aws.String(d.Get("instance_type").(string)), - MaxCount: aws.Long(int64(1)), - MinCount: aws.Long(int64(1)), - UserData: aws.String(userData), - EBSOptimized: aws.Boolean(d.Get("ebs_optimized").(bool)), - IAMInstanceProfile: iam, + ImageID: aws.String(d.Get("ami").(string)), + Placement: placement, + InstanceType: aws.String(d.Get("instance_type").(string)), + MaxCount: aws.Long(int64(1)), + MinCount: aws.Long(int64(1)), + UserData: aws.String(userData), + EBSOptimized: aws.Boolean(d.Get("ebs_optimized").(bool)), + DisableAPITermination: aws.Boolean(d.Get("disable_api_termination").(bool)), + IAMInstanceProfile: iam, } associatePublicIPAddress := false @@ -700,7 +706,18 @@ func resourceAwsInstanceUpdate(d *schema.ResourceData, meta interface{}) error { if err != nil { return err } + } + if d.HasChange("disable_api_termination") { + _, err := conn.ModifyInstanceAttribute(&ec2.ModifyInstanceAttributeInput{ + InstanceID: aws.String(d.Id()), + DisableAPITermination: &ec2.AttributeBooleanValue{ + Value: aws.Boolean(d.Get("disable_api_termination").(bool)), + }, + }) + if err != nil { + return err + } } // TODO(mitchellh): wait for the attributes we modified to diff --git a/builtin/providers/aws/resource_aws_instance_test.go b/builtin/providers/aws/resource_aws_instance_test.go index f591e3335..b976b836f 100644 --- a/builtin/providers/aws/resource_aws_instance_test.go +++ b/builtin/providers/aws/resource_aws_instance_test.go @@ -229,6 +229,51 @@ func TestAccAWSInstance_sourceDestCheck(t *testing.T) { }) } +func TestAccAWSInstance_disableApiTermination(t *testing.T) { + var v ec2.Instance + + checkDisableApiTermination := func(expected bool) resource.TestCheckFunc { + return func(*terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).ec2conn + r, err := conn.DescribeInstanceAttribute(&ec2.DescribeInstanceAttributeInput{ + InstanceID: v.InstanceID, + Attribute: aws.String("disableApiTermination"), + }) + if err != nil { + return err + } + got := *r.DisableAPITermination.Value + if got != expected { + return fmt.Errorf("expected: %t, got: %t", expected, got) + } + return nil + } + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckInstanceDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccInstanceConfigDisableAPITermination(true), + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists("aws_instance.foo", &v), + checkDisableApiTermination(true), + ), + }, + + resource.TestStep{ + Config: testAccInstanceConfigDisableAPITermination(false), + Check: resource.ComposeTestCheckFunc( + testAccCheckInstanceExists("aws_instance.foo", &v), + checkDisableApiTermination(false), + ), + }, + }, + }) +} + func TestAccAWSInstance_vpc(t *testing.T) { var v ec2.Instance @@ -629,6 +674,27 @@ resource "aws_instance" "foo" { } ` +func testAccInstanceConfigDisableAPITermination(val bool) string { + return fmt.Sprintf(` + resource "aws_vpc" "foo" { + cidr_block = "10.1.0.0/16" + } + + resource "aws_subnet" "foo" { + cidr_block = "10.1.1.0/24" + vpc_id = "${aws_vpc.foo.id}" + } + + resource "aws_instance" "foo" { + # us-west-2 + ami = "ami-4fccb37f" + instance_type = "m1.small" + subnet_id = "${aws_subnet.foo.id}" + disable_api_termination = %t + } + `, val) +} + const testAccInstanceConfigVPC = ` resource "aws_vpc" "foo" { cidr_block = "10.1.0.0/16" diff --git a/website/source/docs/providers/aws/r/instance.html.markdown b/website/source/docs/providers/aws/r/instance.html.markdown index a03fce102..080ccb871 100644 --- a/website/source/docs/providers/aws/r/instance.html.markdown +++ b/website/source/docs/providers/aws/r/instance.html.markdown @@ -34,6 +34,8 @@ The following arguments are supported: * `placement_group` - (Optional) The Placement Group to start the instance in. * `ebs_optimized` - (Optional) If true, the launched EC2 instance will be EBS-optimized. +* `disable_api_termination` - (Optional) If true, enables [EC2 Instance + Termination Protection](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/terminating-instances.html#Using_ChangingDisableAPITermination) * `instance_type` - (Required) The type of instance to start * `key_name` - (Optional) The key name to use for the instance. * `security_groups` - (Optional) A list of security group names to associate with.