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

delete_on_termination on ENI has to be optional #8612

Merged
merged 5 commits into from
Jul 15, 2020
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
37 changes: 25 additions & 12 deletions aws/resource_aws_launch_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -430,8 +430,14 @@ func resourceAwsLaunchTemplate() *schema.Resource {
ValidateFunc: validateTypeStringNullableBoolean,
},
"delete_on_termination": {
Type: schema.TypeBool,
Optional: true,
// Use TypeString to allow an "unspecified" value,
// since TypeBool only has true/false with false default.
// The conversion from bare true/false values in
// configurations to TypeString value is currently safe.
Type: schema.TypeString,
Optional: true,
DiffSuppressFunc: suppressEquivalentTypeStringBoolean,
ValidateFunc: validateTypeStringNullableBoolean,
},
"description": {
Type: schema.TypeString,
Expand Down Expand Up @@ -1119,19 +1125,22 @@ func getNetworkInterfaces(n []*ec2.LaunchTemplateInstanceNetworkInterfaceSpecifi
var ipv4Addresses []string

networkInterface := map[string]interface{}{
"delete_on_termination": aws.BoolValue(v.DeleteOnTermination),
"description": aws.StringValue(v.Description),
"device_index": aws.Int64Value(v.DeviceIndex),
"ipv4_address_count": aws.Int64Value(v.SecondaryPrivateIpAddressCount),
"ipv6_address_count": aws.Int64Value(v.Ipv6AddressCount),
"network_interface_id": aws.StringValue(v.NetworkInterfaceId),
"private_ip_address": aws.StringValue(v.PrivateIpAddress),
"subnet_id": aws.StringValue(v.SubnetId),
"description": aws.StringValue(v.Description),
"device_index": aws.Int64Value(v.DeviceIndex),
"ipv4_address_count": aws.Int64Value(v.SecondaryPrivateIpAddressCount),
"ipv6_address_count": aws.Int64Value(v.Ipv6AddressCount),
"network_interface_id": aws.StringValue(v.NetworkInterfaceId),
"private_ip_address": aws.StringValue(v.PrivateIpAddress),
"subnet_id": aws.StringValue(v.SubnetId),
}
if v.AssociatePublicIpAddress != nil {
networkInterface["associate_public_ip_address"] = strconv.FormatBool(aws.BoolValue(v.AssociatePublicIpAddress))
}

if v.DeleteOnTermination != nil {
networkInterface["delete_on_termination"] = strconv.FormatBool(aws.BoolValue(v.DeleteOnTermination))
}

if len(v.Ipv6Addresses) > 0 {
raw, ok := networkInterface["ipv6_addresses"]
if !ok {
Expand Down Expand Up @@ -1507,8 +1516,12 @@ func readNetworkInterfacesFromConfig(ni map[string]interface{}) (*ec2.LaunchTemp
var privateIpAddress string
networkInterface := &ec2.LaunchTemplateInstanceNetworkInterfaceSpecificationRequest{}

if v, ok := ni["delete_on_termination"]; ok {
networkInterface.DeleteOnTermination = aws.Bool(v.(bool))
if v, ok := ni["delete_on_termination"]; ok && v.(string) != "" {
vBool, err := strconv.ParseBool(v.(string))
if err != nil {
return nil, fmt.Errorf("error converting delete_on_termination %q from string to boolean: %w", v.(string), err)
}
networkInterface.DeleteOnTermination = aws.Bool(vBool)
}

if v, ok := ni["description"].(string); ok && v != "" {
Expand Down
71 changes: 71 additions & 0 deletions aws/resource_aws_launch_template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,61 @@ func TestAccAWSLaunchTemplate_ElasticInferenceAccelerator(t *testing.T) {
})
}

func TestAccAWSLaunchTemplate_NetworkInterfaces_DeleteOnTermination(t *testing.T) {
savar marked this conversation as resolved.
Show resolved Hide resolved
var template ec2.LaunchTemplate
rName := acctest.RandomWithPrefix("tf-acc-test")
resourceName := "aws_launch_template.test"

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckAWSLaunchTemplateDestroy,
Steps: []resource.TestStep{
{
Config: testAccAWSLaunchTemplateConfig_NetworkInterfaces_DeleteOnTermination(rName, "true"),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSLaunchTemplateExists(resourceName, &template),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.#", "1"),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.security_groups.#", "1"),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.delete_on_termination", "true"),
),
},
{
Config: testAccAWSLaunchTemplateConfig_NetworkInterfaces_DeleteOnTermination(rName, "false"),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSLaunchTemplateExists(resourceName, &template),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.#", "1"),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.security_groups.#", "1"),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.delete_on_termination", "false"),
),
},
{
Config: testAccAWSLaunchTemplateConfig_NetworkInterfaces_DeleteOnTermination(rName, "\"\""),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSLaunchTemplateExists(resourceName, &template),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.#", "1"),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.security_groups.#", "1"),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.delete_on_termination", ""),
),
},
{
Config: testAccAWSLaunchTemplateConfig_NetworkInterfaces_DeleteOnTermination(rName, "null"),
Check: resource.ComposeTestCheckFunc(
testAccCheckAWSLaunchTemplateExists(resourceName, &template),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.#", "1"),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.security_groups.#", "1"),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.delete_on_termination", ""),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccAWSLaunchTemplate_data(t *testing.T) {
var template ec2.LaunchTemplate
resourceName := "aws_launch_template.test"
Expand Down Expand Up @@ -313,6 +368,7 @@ func TestAccAWSLaunchTemplate_data(t *testing.T) {
resource.TestCheckResourceAttr(resourceName, "monitoring.#", "1"),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.#", "1"),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.security_groups.#", "1"),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.delete_on_termination", ""),
resource.TestCheckResourceAttr(resourceName, "placement.#", "1"),
resource.TestCheckResourceAttrSet(resourceName, "ram_disk_id"),
resource.TestCheckResourceAttr(resourceName, "vpc_security_group_ids.#", "1"),
Expand Down Expand Up @@ -625,6 +681,7 @@ func TestAccAWSLaunchTemplate_networkInterface(t *testing.T) {
resource.TestCheckResourceAttr(resourceName, "network_interfaces.#", "1"),
resource.TestCheckResourceAttrSet(resourceName, "network_interfaces.0.network_interface_id"),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.associate_public_ip_address", ""),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.delete_on_termination", ""),
resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.ipv4_address_count", "2"),
),
},
Expand Down Expand Up @@ -1253,6 +1310,20 @@ resource "aws_autoscaling_group" "test" {
`, rName, deleteOnTermination, rName)
}

func testAccAWSLaunchTemplateConfig_NetworkInterfaces_DeleteOnTermination(rName string, deleteOnTermination string) string {
return fmt.Sprintf(`
resource "aws_launch_template" "test" {
name = %q

network_interfaces {
network_interface_id = "eni-123456ab"
security_groups = ["sg-1a23bc45"]
delete_on_termination = %s
}
}
`, rName, deleteOnTermination)
}

func testAccAWSLaunchTemplateConfig_EbsOptimized(rName, ebsOptimized string) string {
return fmt.Sprintf(`
resource "aws_launch_template" "test" {
Expand Down
35 changes: 35 additions & 0 deletions website/docs/guides/version-3-upgrade.html.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Upgrade topics:
- [Resource: aws_dx_gateway](#resource-aws_dx_gateway)
- [Resource: aws_elastic_transcoder_preset](#resource-aws_elastic_transcoder_preset)
- [Resource: aws_emr_cluster](#resource-aws_emr_cluster)
- [Resource: aws_launch_template](#resource-aws_launch_template)
- [Resource: aws_lb_listener_rule](#resource-aws_lb_listener_rule)
- [Resource: aws_msk_cluster](#resource-aws_msk_cluster)
- [Resource: aws_s3_bucket](#resource-aws_s3_bucket)
Expand Down Expand Up @@ -376,6 +377,40 @@ resource "aws_emr_cluster" "example" {
}
```

## Resource: aws_launch_template

### network_interfaces.delete_on_termination Argument type change

The `network_interfaces.delete_on_termination` argument is now of type `string`, allowing an unspecified value for the argument since the previous `bool` type only allowed for `true/false` and defaulted to `false` when no value was set. Now to enforce `delete_on_termination` to `false`, the string `"false"` or bare `false` value must be used.

For example, given this previous configuration:

```hcl
resource "aws_launch_template" "example" {
# ... other configuration ...

network_interfaces {
# ... other configuration ...

delete_on_termination = null
}
}
```

An updated configuration:

```hcl
resource "aws_launch_template" "example" {
# ... other configuration ...

network_interfaces {
# ... other configuration ...

delete_on_termination = false
}
}
```

## Resource: aws_lb_listener_rule

### condition.field and condition.values Arguments Removal
Expand Down