diff --git a/.changelog/18841.txt b/.changelog/18841.txt new file mode 100644 index 00000000000..d8613477781 --- /dev/null +++ b/.changelog/18841.txt @@ -0,0 +1,7 @@ +```release-note:enhancement +resource/aws_network_interface: Add `interface_type` argument +``` + +```release-note:enhancement +resource/aws_launch_template: Add `interface_type` argument to `network_interfaces` configuration block +``` \ No newline at end of file diff --git a/aws/provider_test.go b/aws/provider_test.go index 7f9495aa6f6..de8c5142252 100644 --- a/aws/provider_test.go +++ b/aws/provider_test.go @@ -365,6 +365,17 @@ func testAccCheckResourceAttrHostnameWithPort(resourceName, attributeName, servi } } +// testAccCheckResourceAttrPrivateDnsName ensures the Terraform state exactly matches a private DNS name +// +// For example: ip-172-16-10-100.us-west-2.compute.internal +func testAccCheckResourceAttrPrivateDnsName(resourceName, attributeName string, privateIpAddress **string) resource.TestCheckFunc { + return func(s *terraform.State) error { + privateDnsName := fmt.Sprintf("ip-%s.%s", resourceAwsEc2DashIP(**privateIpAddress), resourceAwsEc2RegionalPrivateDnsSuffix(testAccGetRegion())) + + return resource.TestCheckResourceAttr(resourceName, attributeName, privateDnsName)(s) + } +} + // testAccMatchResourceAttrAccountID ensures the Terraform state regexp matches an account ID func testAccMatchResourceAttrAccountID(resourceName, attributeName string) resource.TestCheckFunc { return func(s *terraform.State) error { diff --git a/aws/resource_aws_launch_template.go b/aws/resource_aws_launch_template.go index 6a917f83699..fa363ed210b 100644 --- a/aws/resource_aws_launch_template.go +++ b/aws/resource_aws_launch_template.go @@ -16,6 +16,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/naming" ) func resourceAwsLaunchTemplate() *schema.Resource { @@ -41,6 +42,7 @@ func resourceAwsLaunchTemplate() *schema.Resource { "name_prefix": { Type: schema.TypeString, Optional: true, + Computed: true, ForceNew: true, ConflictsWith: []string{"name"}, ValidateFunc: validateLaunchTemplateName, @@ -510,6 +512,11 @@ func resourceAwsLaunchTemplate() *schema.Resource { Type: schema.TypeString, Optional: true, }, + "interface_type": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"efa", "interface"}, false), + }, }, }, }, @@ -658,14 +665,7 @@ func resourceAwsLaunchTemplateCreate(d *schema.ResourceData, meta interface{}) e defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig tags := defaultTagsConfig.MergeTags(keyvaluetags.New(d.Get("tags").(map[string]interface{}))) - var ltName string - if v, ok := d.GetOk("name"); ok { - ltName = v.(string) - } else if v, ok := d.GetOk("name_prefix"); ok { - ltName = resource.PrefixedUniqueId(v.(string)) - } else { - ltName = resource.UniqueId() - } + ltName := naming.Generate(d.Get("name").(string), d.Get("name_prefix").(string)) launchTemplateData, err := buildLaunchTemplateData(d) if err != nil { @@ -739,6 +739,7 @@ func resourceAwsLaunchTemplateRead(d *schema.ResourceData, meta interface{}) err lt := dlt.LaunchTemplates[0] d.Set("name", lt.LaunchTemplateName) + d.Set("name_prefix", naming.NamePrefixFromName(aws.StringValue(lt.LaunchTemplateName))) d.Set("latest_version", lt.LatestVersionNumber) d.Set("default_version", lt.DefaultVersionNumber) tags := keyvaluetags.Ec2KeyValueTags(lt.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig) @@ -1185,6 +1186,7 @@ func getNetworkInterfaces(n []*ec2.LaunchTemplateInstanceNetworkInterfaceSpecifi networkInterface := map[string]interface{}{ "description": aws.StringValue(v.Description), "device_index": aws.Int64Value(v.DeviceIndex), + "interface_type": aws.StringValue(v.InterfaceType), "ipv4_address_count": aws.Int64Value(v.SecondaryPrivateIpAddressCount), "ipv6_address_count": aws.Int64Value(v.Ipv6AddressCount), "network_interface_id": aws.StringValue(v.NetworkInterfaceId), @@ -1615,6 +1617,10 @@ func readNetworkInterfacesFromConfig(ni map[string]interface{}) (*ec2.LaunchTemp networkInterface.NetworkInterfaceId = aws.String(v) } + if v, ok := ni["interface_type"].(string); ok && v != "" { + networkInterface.InterfaceType = aws.String(v) + } + if v, ok := ni["associate_carrier_ip_address"]; ok && v.(string) != "" { vBool, err := strconv.ParseBool(v.(string)) if err != nil { diff --git a/aws/resource_aws_launch_template_test.go b/aws/resource_aws_launch_template_test.go index 5ab9d0c330d..22a436c7358 100644 --- a/aws/resource_aws_launch_template_test.go +++ b/aws/resource_aws_launch_template_test.go @@ -13,6 +13,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/naming" ) func init() { @@ -86,14 +87,70 @@ func TestAccAWSLaunchTemplate_basic(t *testing.T) { CheckDestroy: testAccCheckAWSLaunchTemplateDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLaunchTemplateConfig_basic(rName), + Config: testAccAWSLaunchTemplateConfigName(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSLaunchTemplateExists(resourceName, &template), - resource.TestCheckResourceAttr(resourceName, "default_version", "1"), - resource.TestCheckResourceAttr(resourceName, "latest_version", "1"), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "ec2", regexp.MustCompile(`launch-template/.+`)), + resource.TestCheckResourceAttr(resourceName, "default_version", "1"), resource.TestCheckResourceAttr(resourceName, "ebs_optimized", ""), resource.TestCheckResourceAttr(resourceName, "elastic_inference_accelerator.#", "0"), + resource.TestCheckResourceAttr(resourceName, "latest_version", "1"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "name_prefix", ""), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSLaunchTemplate_Name_Generated(t *testing.T) { + var template ec2.LaunchTemplate + resourceName := "aws_launch_template.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, autoscaling.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLaunchTemplateDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLaunchTemplateConfigNameGenerated(), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLaunchTemplateExists(resourceName, &template), + naming.TestCheckResourceAttrNameGenerated(resourceName, "name"), + resource.TestCheckResourceAttr(resourceName, "name_prefix", "terraform-"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSLaunchTemplate_Name_Prefix(t *testing.T) { + var template ec2.LaunchTemplate + resourceName := "aws_launch_template.test" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, autoscaling.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLaunchTemplateDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLaunchTemplateConfigNamePrefix("tf-acc-test-prefix-"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLaunchTemplateExists(resourceName, &template), + naming.TestCheckResourceAttrNameFromPrefix(resourceName, "name", "tf-acc-test-prefix-"), + resource.TestCheckResourceAttr(resourceName, "name_prefix", "tf-acc-test-prefix-"), ), }, { @@ -117,7 +174,7 @@ func TestAccAWSLaunchTemplate_disappears(t *testing.T) { CheckDestroy: testAccCheckAWSLaunchTemplateDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLaunchTemplateConfig_basic(rName), + Config: testAccAWSLaunchTemplateConfigName(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSLaunchTemplateExists(resourceName, &launchTemplate), testAccCheckAWSLaunchTemplateDisappears(&launchTemplate), @@ -501,7 +558,7 @@ func TestAccAWSLaunchTemplate_update(t *testing.T) { }) } -func TestAccAWSLaunchTemplate_tags(t *testing.T) { +func TestAccAWSLaunchTemplate_Tags(t *testing.T) { var template ec2.LaunchTemplate resourceName := "aws_launch_template.test" rName := acctest.RandomWithPrefix("tf-acc-test") @@ -513,11 +570,11 @@ func TestAccAWSLaunchTemplate_tags(t *testing.T) { CheckDestroy: testAccCheckAWSLaunchTemplateDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSLaunchTemplateConfig_basic(rName), + Config: testAccAWSLaunchTemplateConfigTags1(rName, "key1", "value1"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSLaunchTemplateExists(resourceName, &template), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.test", "bar"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1"), ), }, { @@ -526,11 +583,20 @@ func TestAccAWSLaunchTemplate_tags(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccAWSLaunchTemplateConfig_tagsUpdate(rName), + Config: testAccAWSLaunchTemplateConfigTags2(rName, "key1", "value1updated", "key2", "value2"), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLaunchTemplateExists(resourceName, &template), + resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), + resource.TestCheckResourceAttr(resourceName, "tags.key1", "value1updated"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), + ), + }, + { + Config: testAccAWSLaunchTemplateConfigTags1(rName, "key2", "value2"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSLaunchTemplateExists(resourceName, &template), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceName, "tags.bar", "baz"), + resource.TestCheckResourceAttr(resourceName, "tags.key2", "value2"), ), }, }, @@ -591,7 +657,7 @@ func TestAccAWSLaunchTemplate_capacityReservation_target(t *testing.T) { func TestAccAWSLaunchTemplate_cpuOptions(t *testing.T) { var template ec2.LaunchTemplate - resName := "aws_launch_template.foo" + resName := "aws_launch_template.test" rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ @@ -777,6 +843,34 @@ func TestAccAWSLaunchTemplate_networkInterfaceAddresses(t *testing.T) { }) } +func TestAccAWSLaunchTemplate_networkInterfaceType(t *testing.T) { + var template ec2.LaunchTemplate + resourceName := "aws_launch_template.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, autoscaling.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSLaunchTemplateDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSLaunchTemplateConfig_networkInterfaceType_efa(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckAWSLaunchTemplateExists(resourceName, &template), + resource.TestCheckResourceAttr(resourceName, "network_interfaces.#", "1"), + resource.TestCheckResourceAttr(resourceName, "network_interfaces.0.interface_type", "efa"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccAWSLaunchTemplate_associatePublicIPAddress(t *testing.T) { var template ec2.LaunchTemplate rName := acctest.RandomWithPrefix("tf-acc-test") @@ -1040,7 +1134,7 @@ func TestAccAWSLaunchTemplate_instanceMarketOptions(t *testing.T) { func TestAccAWSLaunchTemplate_licenseSpecification(t *testing.T) { var template ec2.LaunchTemplate - resourceName := "aws_launch_template.example" + resourceName := "aws_launch_template.test" rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ @@ -1353,22 +1447,32 @@ func testAccCheckAWSLaunchTemplateDisappears(launchTemplate *ec2.LaunchTemplate) } } -func testAccAWSLaunchTemplateConfig_basic(rName string) string { +func testAccAWSLaunchTemplateConfigName(rName string) string { return fmt.Sprintf(` resource "aws_launch_template" "test" { - name = %q - - tags = { - test = "bar" - } + name = %[1]q } `, rName) } +func testAccAWSLaunchTemplateConfigNameGenerated() string { + return ` +resource "aws_launch_template" "test" {} +` +} + +func testAccAWSLaunchTemplateConfigNamePrefix(namePrefix string) string { + return fmt.Sprintf(` +resource "aws_launch_template" "test" { + name_prefix = %[1]q +} +`, namePrefix) +} + func testAccAWSLaunchTemplateConfig_ipv6_count(rName string) string { return fmt.Sprintf(` resource "aws_launch_template" "test" { - name = %q + name = %[1]q network_interfaces { ipv6_address_count = 1 @@ -1429,7 +1533,7 @@ resource "aws_launch_template" "test" { device_name = "/dev/xvda" ebs { - delete_on_termination = %t + delete_on_termination = %[2]t volume_size = 15 } } @@ -1495,12 +1599,12 @@ resource "aws_autoscaling_group" "test" { func testAccAWSLaunchTemplateConfig_NetworkInterfaces_DeleteOnTermination(rName string, deleteOnTermination string) string { return fmt.Sprintf(` resource "aws_launch_template" "test" { - name = %q + name = %[1]q network_interfaces { network_interface_id = "eni-123456ab" security_groups = ["sg-1a23bc45"] - delete_on_termination = %s + delete_on_termination = %[2]s } } `, rName, deleteOnTermination) @@ -1509,8 +1613,8 @@ resource "aws_launch_template" "test" { func testAccAWSLaunchTemplateConfig_EbsOptimized(rName, ebsOptimized string) string { return fmt.Sprintf(` resource "aws_launch_template" "test" { - ebs_optimized = %s - name = %q + ebs_optimized = %[1]s + name = %[2]q } `, ebsOptimized, rName) } @@ -1609,25 +1713,38 @@ resource "aws_launch_template" "test" { `, rName)) //lintignore:AWSAT002 } -func testAccAWSLaunchTemplateConfig_tagsUpdate(rName string) string { +func testAccAWSLaunchTemplateConfigTags1(rName, tagKey1, tagValue1 string) string { return fmt.Sprintf(` resource "aws_launch_template" "test" { - name = %q + name = %[1]q tags = { - bar = "baz" + %[2]q = %[3]q } } -`, rName) +`, rName, tagKey1, tagValue1) +} + +func testAccAWSLaunchTemplateConfigTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return fmt.Sprintf(` +resource "aws_launch_template" "test" { + name = %[1]q + + tags = { + %[2]q = %[3]q + %[4]q = %[5]q + } +} +`, rName, tagKey1, tagValue1, tagKey2, tagValue2) } func testAccAWSLaunchTemplateConfig_capacityReservation_preference(rName string, preference string) string { return fmt.Sprintf(` resource "aws_launch_template" "test" { - name = %q + name = %[1]q capacity_reservation_specification { - capacity_reservation_preference = %q + capacity_reservation_preference = %[2]q } } `, rName, preference) @@ -1652,7 +1769,7 @@ resource "aws_ec2_capacity_reservation" "test" { } resource "aws_launch_template" "test" { - name = %q + name = %[1]q capacity_reservation_specification { capacity_reservation_target { @@ -1665,12 +1782,12 @@ resource "aws_launch_template" "test" { func testAccAWSLaunchTemplateConfig_cpuOptions(rName string, coreCount, threadsPerCore int) string { return fmt.Sprintf(` -resource "aws_launch_template" "foo" { - name = %q +resource "aws_launch_template" "test" { + name = %[1]q cpu_options { - core_count = %d - threads_per_core = %d + core_count = %[2]d + threads_per_core = %[3]d } } `, rName, coreCount, threadsPerCore) @@ -1679,11 +1796,11 @@ resource "aws_launch_template" "foo" { func testAccAWSLaunchTemplateConfig_creditSpecification(rName, instanceType, cpuCredits string) string { return fmt.Sprintf(` resource "aws_launch_template" "test" { - instance_type = %q - name = %q + instance_type = %[1]q + name = %[2]q credit_specification { - cpu_credits = %q + cpu_credits = %[3]q } } `, instanceType, rName, cpuCredits) @@ -1692,7 +1809,7 @@ resource "aws_launch_template" "test" { func testAccAWSLaunchTemplateConfigIamInstanceProfileEmptyConfigurationBlock(rName string) string { return fmt.Sprintf(` resource "aws_launch_template" "test" { - name = %q + name = %[1]q iam_instance_profile {} } @@ -1701,16 +1818,16 @@ resource "aws_launch_template" "test" { func testAccAWSLaunchTemplateConfig_licenseSpecification(rName string) string { return fmt.Sprintf(` -resource "aws_licensemanager_license_configuration" "example" { - name = "Example" +resource "aws_licensemanager_license_configuration" "test" { + name = "Test" license_counting_type = "vCPU" } -resource "aws_launch_template" "example" { - name = %q +resource "aws_launch_template" "test" { + name = %[1]q license_specification { - license_configuration_arn = aws_licensemanager_license_configuration.example.id + license_configuration_arn = aws_licensemanager_license_configuration.test.id } } `, rName) @@ -1719,8 +1836,8 @@ resource "aws_launch_template" "example" { func testAccAWSLaunchTemplateConfig_description(rName, description string) string { return fmt.Sprintf(` resource "aws_launch_template" "test" { - name = %q - description = %q + name = %[1]q + description = %[2]q } `, rName, description) } @@ -1729,15 +1846,27 @@ func testAccAWSLaunchTemplateConfig_networkInterface(rName string) string { return fmt.Sprintf(` resource "aws_vpc" "test" { cidr_block = "10.1.0.0/16" + + tags = { + Name = %[1]q + } } resource "aws_subnet" "test" { vpc_id = aws_vpc.test.id cidr_block = "10.1.0.0/24" + + tags = { + Name = %[1]q + } } resource "aws_network_interface" "test" { subnet_id = aws_subnet.test.id + + tags = { + Name = %[1]q + } } resource "aws_launch_template" "test" { @@ -1805,19 +1934,31 @@ func testAccAWSLaunchTemplateConfig_networkInterfaceAddresses(rName string) stri return fmt.Sprintf(` resource "aws_vpc" "test" { cidr_block = "10.1.0.0/16" + + tags = { + Name = %[1]q + } } resource "aws_subnet" "test" { vpc_id = aws_vpc.test.id cidr_block = "10.1.0.0/24" + + tags = { + Name = %[1]q + } } resource "aws_network_interface" "test" { subnet_id = aws_subnet.test.id + + tags = { + Name = %[1]q + } } resource "aws_launch_template" "test" { - name = %q + name = %[1]q network_interfaces { network_interface_id = aws_network_interface.test.id @@ -1832,15 +1973,27 @@ func testAccAWSLaunchTemplateConfig_associatePublicIpAddress(rName, associatePub return fmt.Sprintf(` resource "aws_vpc" "test" { cidr_block = "10.1.0.0/16" + + tags = { + Name = %[1]q + } } resource "aws_subnet" "test" { vpc_id = aws_vpc.test.id cidr_block = "10.1.0.0/24" + + tags = { + Name = %[1]q + } } resource "aws_network_interface" "test" { subnet_id = aws_subnet.test.id + + tags = { + Name = %[1]q + } } resource "aws_launch_template" "test" { @@ -1859,15 +2012,27 @@ func testAccAWSLaunchTemplateConfig_associateCarrierIpAddress(rName, associateCa return fmt.Sprintf(` resource "aws_vpc" "test" { cidr_block = "10.1.0.0/16" + + tags = { + Name = %[1]q + } } resource "aws_subnet" "test" { vpc_id = aws_vpc.test.id cidr_block = "10.1.0.0/24" + + tags = { + Name = %[1]q + } } resource "aws_network_interface" "test" { subnet_id = aws_subnet.test.id + + tags = { + Name = %[1]q + } } resource "aws_launch_template" "test" { @@ -1897,6 +2062,18 @@ resource "aws_launch_template" "test" { `, rName) } +func testAccAWSLaunchTemplateConfig_networkInterfaceType_efa(rName string) string { + return fmt.Sprintf(` +resource "aws_launch_template" "test" { + name = %[1]q + + network_interfaces { + interface_type = "efa" + } +} +`, rName) +} + const testAccAWSLaunchTemplateConfig_asg_basic = ` data "aws_ami" "test_ami" { most_recent = true @@ -2111,13 +2288,9 @@ resource "aws_launch_template" "test" { func testAccAWSLaunchTemplateConfig_descriptionDefaultVersion(rName, description string, version int) string { return fmt.Sprintf(` resource "aws_launch_template" "test" { - name = %q - description = %q - default_version = %d - - tags = { - test = "baz" - } + name = %[1]q + description = %[2]q + default_version = %[3]d } `, rName, description, version) } @@ -2125,13 +2298,9 @@ resource "aws_launch_template" "test" { func testAccAWSLaunchTemplateconfig_descriptionUpdateDefaultVersion(rName, description string, update bool) string { return fmt.Sprintf(` resource "aws_launch_template" "test" { - name = %q - description = %q - update_default_version = %t - - tags = { - test = "baz" - } + name = %[1]q + description = %[2]q + update_default_version = %[3]t } `, rName, description, update) } diff --git a/aws/resource_aws_network_interface.go b/aws/resource_aws_network_interface.go index cb72ac81d46..a599eb94ef0 100644 --- a/aws/resource_aws_network_interface.go +++ b/aws/resource_aws_network_interface.go @@ -128,6 +128,14 @@ func resourceAwsNetworkInterface() *schema.Resource { }, ConflictsWith: []string{"ipv6_address_count"}, }, + + "interface_type": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice(ec2.NetworkInterfaceCreationType_Values(), false), + }, }, CustomizeDiff: SetTagsDiff, @@ -169,6 +177,10 @@ func resourceAwsNetworkInterfaceCreate(d *schema.ResourceData, meta interface{}) request.Ipv6Addresses = expandIP6Addresses(v.(*schema.Set).List()) } + if v, ok := d.GetOk("interface_type"); ok { + request.InterfaceType = aws.String(v.(string)) + } + log.Printf("[DEBUG] Creating network interface") resp, err := conn.CreateNetworkInterface(request) if err != nil { @@ -247,6 +259,7 @@ func resourceAwsNetworkInterfaceRead(d *schema.ResourceData, meta interface{}) e return fmt.Errorf("error setting attachment: %s", err) } + d.Set("interface_type", eni.InterfaceType) d.Set("description", eni.Description) d.Set("private_dns_name", eni.PrivateDnsName) d.Set("mac_address", eni.MacAddress) diff --git a/aws/resource_aws_network_interface_test.go b/aws/resource_aws_network_interface_test.go index 9af93453547..dc81aa50ca2 100644 --- a/aws/resource_aws_network_interface_test.go +++ b/aws/resource_aws_network_interface_test.go @@ -70,6 +70,8 @@ func testSweepEc2NetworkInterfaces(region string) error { func TestAccAWSENI_basic(t *testing.T) { var conf ec2.NetworkInterface resourceName := "aws_network_interface.test" + subnetResourceName := "aws_subnet.test" + rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -78,17 +80,23 @@ func TestAccAWSENI_basic(t *testing.T) { CheckDestroy: testAccCheckAWSENIDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSENIConfig(), + Config: testAccAWSENIConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSENIExists(resourceName, &conf), - testAccCheckAWSENIAttributes(&conf), - resource.TestCheckResourceAttr(resourceName, "private_ips.#", "1"), - resource.TestCheckResourceAttrSet(resourceName, "private_dns_name"), + resource.TestCheckResourceAttr(resourceName, "attachment.#", "0"), + resource.TestCheckResourceAttr(resourceName, "description", ""), + resource.TestCheckResourceAttr(resourceName, "interface_type", "interface"), + resource.TestCheckResourceAttr(resourceName, "ipv6_address_count", "0"), + resource.TestCheckResourceAttr(resourceName, "ipv6_addresses.#", "0"), resource.TestCheckResourceAttrSet(resourceName, "mac_address"), - resource.TestCheckResourceAttr(resourceName, "tags.#", "0"), - resource.TestCheckResourceAttr(resourceName, "description", "Managed by Terraform"), - testAccCheckAWSENIAvailabilityZone("data.aws_availability_zones.available", "names.0", &conf), resource.TestCheckResourceAttr(resourceName, "outpost_arn", ""), + testAccCheckResourceAttrPrivateDnsName(resourceName, "private_dns_name", &conf.PrivateIpAddress), + resource.TestCheckResourceAttrSet(resourceName, "private_ip"), + resource.TestCheckResourceAttr(resourceName, "private_ips.#", "1"), + resource.TestCheckResourceAttr(resourceName, "security_groups.#", "1"), + resource.TestCheckResourceAttr(resourceName, "source_dest_check", "true"), + resource.TestCheckResourceAttrPair(resourceName, "subnet_id", subnetResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), }, { @@ -100,7 +108,7 @@ func TestAccAWSENI_basic(t *testing.T) { }) } -func TestAccAWSENI_ipv6(t *testing.T) { +func TestAccAWSENI_IPv6(t *testing.T) { var conf ec2.NetworkInterface resourceName := "aws_network_interface.test" rName := acctest.RandomWithPrefix("tf-acc-test") @@ -112,10 +120,9 @@ func TestAccAWSENI_ipv6(t *testing.T) { CheckDestroy: testAccCheckAWSENIDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSENIIPV6Config(rName), + Config: testAccAWSENIConfigIPV6(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSENIExists(resourceName, &conf), - testAccCheckAWSENIAttributes(&conf), resource.TestCheckResourceAttr(resourceName, "ipv6_address_count", "1"), resource.TestCheckResourceAttr(resourceName, "ipv6_addresses.#", "1"), ), @@ -126,19 +133,17 @@ func TestAccAWSENI_ipv6(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccAWSENIIPV6MultipleConfig(rName), + Config: testAccAWSENIConfigIPV6Multiple(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSENIExists(resourceName, &conf), - testAccCheckAWSENIAttributes(&conf), resource.TestCheckResourceAttr(resourceName, "ipv6_address_count", "2"), resource.TestCheckResourceAttr(resourceName, "ipv6_addresses.#", "2"), ), }, { - Config: testAccAWSENIIPV6Config(rName), + Config: testAccAWSENIConfigIPV6(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSENIExists(resourceName, &conf), - testAccCheckAWSENIAttributes(&conf), resource.TestCheckResourceAttr(resourceName, "ipv6_address_count", "1"), resource.TestCheckResourceAttr(resourceName, "ipv6_addresses.#", "1"), ), @@ -147,7 +152,7 @@ func TestAccAWSENI_ipv6(t *testing.T) { }) } -func TestAccAWSENI_tags(t *testing.T) { +func TestAccAWSENI_Tags(t *testing.T) { resourceName := "aws_network_interface.test" rName := acctest.RandomWithPrefix("tf-acc-test") var conf ec2.NetworkInterface @@ -159,7 +164,7 @@ func TestAccAWSENI_tags(t *testing.T) { CheckDestroy: testAccCheckAWSENIDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSENITagsConfig1(rName, "key1", "value1"), + Config: testAccAWSENIConfigTags1(rName, "key1", "value1"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSENIExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), @@ -172,7 +177,7 @@ func TestAccAWSENI_tags(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccAWSENITagsConfig2(rName, "key1", "value1updated", "key2", "value2"), + Config: testAccAWSENIConfigTags2(rName, "key1", "value1updated", "key2", "value2"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSENIExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "tags.%", "2"), @@ -181,7 +186,7 @@ func TestAccAWSENI_tags(t *testing.T) { ), }, { - Config: testAccAWSENITagsConfig1(rName, "key2", "value2"), + Config: testAccAWSENIConfigTags1(rName, "key2", "value2"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSENIExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), @@ -192,7 +197,7 @@ func TestAccAWSENI_tags(t *testing.T) { }) } -func TestAccAWSENI_ipv6_count(t *testing.T) { +func TestAccAWSENI_IPv6Count(t *testing.T) { var conf ec2.NetworkInterface resourceName := "aws_network_interface.test" rName := acctest.RandomWithPrefix("tf-acc-test") @@ -204,7 +209,7 @@ func TestAccAWSENI_ipv6_count(t *testing.T) { CheckDestroy: testAccCheckAWSENIDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSENIIPV6CountConfig(1, rName), + Config: testAccAWSENIConfigIPV6Count(rName, 1), Check: resource.ComposeTestCheckFunc( testAccCheckAWSENIExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "ipv6_address_count", "1"), @@ -216,21 +221,21 @@ func TestAccAWSENI_ipv6_count(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccAWSENIIPV6CountConfig(2, rName), + Config: testAccAWSENIConfigIPV6Count(rName, 2), Check: resource.ComposeTestCheckFunc( testAccCheckAWSENIExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "ipv6_address_count", "2"), ), }, { - Config: testAccAWSENIIPV6CountConfig(0, rName), + Config: testAccAWSENIConfigIPV6Count(rName, 0), Check: resource.ComposeTestCheckFunc( testAccCheckAWSENIExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "ipv6_address_count", "0"), ), }, { - Config: testAccAWSENIIPV6CountConfig(1, rName), + Config: testAccAWSENIConfigIPV6Count(rName, 1), Check: resource.ComposeTestCheckFunc( testAccCheckAWSENIExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "ipv6_address_count", "1"), @@ -243,6 +248,7 @@ func TestAccAWSENI_ipv6_count(t *testing.T) { func TestAccAWSENI_disappears(t *testing.T) { var networkInterface ec2.NetworkInterface resourceName := "aws_network_interface.test" + rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -251,7 +257,7 @@ func TestAccAWSENI_disappears(t *testing.T) { CheckDestroy: testAccCheckAWSENIDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSENIConfig(), + Config: testAccAWSENIConfig(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSENIExists(resourceName, &networkInterface), testAccCheckResourceDisappears(testAccProvider, resourceAwsNetworkInterface(), resourceName), @@ -262,9 +268,12 @@ func TestAccAWSENI_disappears(t *testing.T) { }) } -func TestAccAWSENI_updatedDescription(t *testing.T) { +func TestAccAWSENI_Description(t *testing.T) { var conf ec2.NetworkInterface resourceName := "aws_network_interface.test" + subnetResourceName := "aws_subnet.test" + securityGroupResourceName := "aws_security_group.test" + rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -273,10 +282,26 @@ func TestAccAWSENI_updatedDescription(t *testing.T) { CheckDestroy: testAccCheckAWSENIDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSENIConfig(), + Config: testAccAWSENIConfigDescription(rName, "description 1"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSENIExists(resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "description", "Managed by Terraform"), + resource.TestCheckResourceAttr(resourceName, "attachment.#", "0"), + resource.TestCheckResourceAttr(resourceName, "description", "description 1"), + resource.TestCheckResourceAttr(resourceName, "interface_type", "interface"), + resource.TestCheckResourceAttr(resourceName, "ipv6_address_count", "0"), + resource.TestCheckResourceAttr(resourceName, "ipv6_addresses.#", "0"), + resource.TestCheckResourceAttrSet(resourceName, "mac_address"), + resource.TestCheckResourceAttr(resourceName, "outpost_arn", ""), + resource.TestCheckResourceAttrSet(resourceName, "private_dns_name"), + resource.TestCheckResourceAttr(resourceName, "private_ip", "172.16.10.100"), + resource.TestCheckResourceAttr(resourceName, "private_ips.#", "1"), + resource.TestCheckTypeSetElemAttr(resourceName, "private_ips.*", "172.16.10.100"), + resource.TestCheckResourceAttr(resourceName, "security_groups.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "security_groups.*", securityGroupResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "source_dest_check", "true"), + resource.TestCheckResourceAttrPair(resourceName, "subnet_id", subnetResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.Name", rName), ), }, { @@ -285,19 +310,36 @@ func TestAccAWSENI_updatedDescription(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccAWSENIConfigUpdatedDescription(), + Config: testAccAWSENIConfigDescription(rName, "description 2"), Check: resource.ComposeTestCheckFunc( testAccCheckAWSENIExists(resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "description", "Updated ENI Description"), + resource.TestCheckResourceAttr(resourceName, "attachment.#", "0"), + resource.TestCheckResourceAttr(resourceName, "description", "description 2"), + resource.TestCheckResourceAttr(resourceName, "interface_type", "interface"), + resource.TestCheckResourceAttr(resourceName, "ipv6_address_count", "0"), + resource.TestCheckResourceAttr(resourceName, "ipv6_addresses.#", "0"), + resource.TestCheckResourceAttrSet(resourceName, "mac_address"), + resource.TestCheckResourceAttr(resourceName, "outpost_arn", ""), + resource.TestCheckResourceAttrSet(resourceName, "private_dns_name"), + resource.TestCheckResourceAttr(resourceName, "private_ip", "172.16.10.100"), + resource.TestCheckResourceAttr(resourceName, "private_ips.#", "1"), + resource.TestCheckTypeSetElemAttr(resourceName, "private_ips.*", "172.16.10.100"), + resource.TestCheckResourceAttr(resourceName, "security_groups.#", "1"), + resource.TestCheckTypeSetElemAttrPair(resourceName, "security_groups.*", securityGroupResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "source_dest_check", "true"), + resource.TestCheckResourceAttrPair(resourceName, "subnet_id", subnetResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "1"), + resource.TestCheckResourceAttr(resourceName, "tags.Name", rName), ), }, }, }) } -func TestAccAWSENI_attached(t *testing.T) { +func TestAccAWSENI_Attachment(t *testing.T) { var conf ec2.NetworkInterface resourceName := "aws_network_interface.test" + rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -306,12 +348,16 @@ func TestAccAWSENI_attached(t *testing.T) { CheckDestroy: testAccCheckAWSENIDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSENIConfigWithAttachment(), + Config: testAccAWSENIConfigAttachment(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSENIExists(resourceName, &conf), - testAccCheckAWSENIAttributesWithAttachment(&conf), - testAccCheckAWSENIAvailabilityZone("data.aws_availability_zones.available", "names.0", &conf), + resource.TestCheckResourceAttr(resourceName, "attachment.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(resourceName, "attachment.*", map[string]string{ + "device_index": "1", + }), + resource.TestCheckResourceAttr(resourceName, "private_ip", "172.16.10.100"), resource.TestCheckResourceAttr(resourceName, "private_ips.#", "1"), + resource.TestCheckTypeSetElemAttr(resourceName, "private_ips.*", "172.16.10.100"), ), }, { @@ -323,9 +369,10 @@ func TestAccAWSENI_attached(t *testing.T) { }) } -func TestAccAWSENI_ignoreExternalAttachment(t *testing.T) { +func TestAccAWSENI_IgnoreExternalAttachment(t *testing.T) { var conf ec2.NetworkInterface resourceName := "aws_network_interface.test" + rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -334,11 +381,9 @@ func TestAccAWSENI_ignoreExternalAttachment(t *testing.T) { CheckDestroy: testAccCheckAWSENIDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSENIConfigExternalAttachment(), + Config: testAccAWSENIConfigExternalAttachment(rName), Check: resource.ComposeTestCheckFunc( testAccCheckAWSENIExists(resourceName, &conf), - testAccCheckAWSENIAttributes(&conf), - testAccCheckAWSENIAvailabilityZone("data.aws_availability_zones.available", "names.0", &conf), testAccCheckAWSENIMakeExternalAttachment("aws_instance.test", &conf), ), }, @@ -351,9 +396,10 @@ func TestAccAWSENI_ignoreExternalAttachment(t *testing.T) { }) } -func TestAccAWSENI_sourceDestCheck(t *testing.T) { +func TestAccAWSENI_SourceDestCheck(t *testing.T) { var conf ec2.NetworkInterface resourceName := "aws_network_interface.test" + rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -362,7 +408,7 @@ func TestAccAWSENI_sourceDestCheck(t *testing.T) { CheckDestroy: testAccCheckAWSENIDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSENIConfigWithSourceDestCheck(false), + Config: testAccAWSENIConfigSourceDestCheck(rName, false), Check: resource.ComposeTestCheckFunc( testAccCheckAWSENIExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "source_dest_check", "false"), @@ -374,14 +420,14 @@ func TestAccAWSENI_sourceDestCheck(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccAWSENIConfigWithSourceDestCheck(true), + Config: testAccAWSENIConfigSourceDestCheck(rName, true), Check: resource.ComposeTestCheckFunc( testAccCheckAWSENIExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "source_dest_check", "true"), ), }, { - Config: testAccAWSENIConfigWithSourceDestCheck(false), + Config: testAccAWSENIConfigSourceDestCheck(rName, false), Check: resource.ComposeTestCheckFunc( testAccCheckAWSENIExists(resourceName, &conf), resource.TestCheckResourceAttr(resourceName, "source_dest_check", "false"), @@ -391,9 +437,10 @@ func TestAccAWSENI_sourceDestCheck(t *testing.T) { }) } -func TestAccAWSENI_computedIPs(t *testing.T) { +func TestAccAWSENI_PrivateIpsCount(t *testing.T) { var conf ec2.NetworkInterface resourceName := "aws_network_interface.test" + rName := acctest.RandomWithPrefix("tf-acc-test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, @@ -402,10 +449,10 @@ func TestAccAWSENI_computedIPs(t *testing.T) { CheckDestroy: testAccCheckAWSENIDestroy, Steps: []resource.TestStep{ { - Config: testAccAWSENIConfigWithNoPrivateIPs(), + Config: testAccAWSENIConfigPrivateIpsCount(rName, 1), Check: resource.ComposeTestCheckFunc( testAccCheckAWSENIExists(resourceName, &conf), - resource.TestCheckResourceAttr(resourceName, "private_ips.#", "1"), + resource.TestCheckResourceAttr(resourceName, "private_ips_count", "1"), ), }, { @@ -413,25 +460,11 @@ func TestAccAWSENI_computedIPs(t *testing.T) { ImportState: true, ImportStateVerify: true, }, - }, - }) -} - -func TestAccAWSENI_PrivateIpsCount(t *testing.T) { - var networkInterface1, networkInterface2, networkInterface3, networkInterface4 ec2.NetworkInterface - resourceName := "aws_network_interface.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ErrorCheck: testAccErrorCheck(t, ec2.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckAWSENIDestroy, - Steps: []resource.TestStep{ { - Config: testAccAWSENIConfigPrivateIpsCount(1), + Config: testAccAWSENIConfigPrivateIpsCount(rName, 2), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSENIExists(resourceName, &networkInterface1), - resource.TestCheckResourceAttr(resourceName, "private_ips_count", "1"), + testAccCheckAWSENIExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "private_ips_count", "2"), ), }, { @@ -440,10 +473,10 @@ func TestAccAWSENI_PrivateIpsCount(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccAWSENIConfigPrivateIpsCount(2), + Config: testAccAWSENIConfigPrivateIpsCount(rName, 0), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSENIExists(resourceName, &networkInterface2), - resource.TestCheckResourceAttr(resourceName, "private_ips_count", "2"), + testAccCheckAWSENIExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "private_ips_count", "0"), ), }, { @@ -452,10 +485,10 @@ func TestAccAWSENI_PrivateIpsCount(t *testing.T) { ImportStateVerify: true, }, { - Config: testAccAWSENIConfigPrivateIpsCount(0), + Config: testAccAWSENIConfigPrivateIpsCount(rName, 1), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSENIExists(resourceName, &networkInterface3), - resource.TestCheckResourceAttr(resourceName, "private_ips_count", "0"), + testAccCheckAWSENIExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "private_ips_count", "1"), ), }, { @@ -463,11 +496,26 @@ func TestAccAWSENI_PrivateIpsCount(t *testing.T) { ImportState: true, ImportStateVerify: true, }, + }, + }) +} + +func TestAccAWSENI_InterfaceType_efa(t *testing.T) { + var conf ec2.NetworkInterface + resourceName := "aws_network_interface.test" + rName := acctest.RandomWithPrefix("tf-acc-test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + ErrorCheck: testAccErrorCheck(t, ec2.EndpointsID), + Providers: testAccProviders, + CheckDestroy: testAccCheckAWSENIDestroy, + Steps: []resource.TestStep{ { - Config: testAccAWSENIConfigPrivateIpsCount(1), + Config: testAccAWSENIConfigInterfaceType(rName, "efa"), Check: resource.ComposeTestCheckFunc( - testAccCheckAWSENIExists(resourceName, &networkInterface4), - resource.TestCheckResourceAttr(resourceName, "private_ips_count", "1"), + testAccCheckAWSENIExists(resourceName, &conf), + resource.TestCheckResourceAttr(resourceName, "interface_type", "efa"), ), }, { @@ -511,79 +559,6 @@ func testAccCheckAWSENIExists(n string, res *ec2.NetworkInterface) resource.Test } } -func testAccCheckAWSENIAttributes(conf *ec2.NetworkInterface) resource.TestCheckFunc { - return func(s *terraform.State) error { - if conf.Attachment != nil { - return fmt.Errorf("expected attachment to be nil") - } - - if len(conf.Groups) != 1 && *conf.Groups[0].GroupName != "foo" { - return fmt.Errorf("expected security group to be foo, but was %#v", conf.Groups) - } - - if *conf.PrivateIpAddress != "172.16.10.100" { - return fmt.Errorf("expected private ip to be 172.16.10.100, but was %s", *conf.PrivateIpAddress) - } - - expectedPrivateDnsName := fmt.Sprintf("ip-%s.%s", resourceAwsEc2DashIP(*conf.PrivateIpAddress), resourceAwsEc2RegionalPrivateDnsSuffix(testAccGetRegion())) - if *conf.PrivateDnsName != expectedPrivateDnsName { - return fmt.Errorf("expected private dns name to be %s, but was %s", expectedPrivateDnsName, *conf.PrivateDnsName) - } - - if len(*conf.MacAddress) == 0 { - return fmt.Errorf("expected mac_address to be set") - } - - if !*conf.SourceDestCheck { - return fmt.Errorf("expected source_dest_check to be true, but was %t", *conf.SourceDestCheck) - } - - return nil - } -} - -func testAccCheckAWSENIAvailabilityZone(name, attr string, conf *ec2.NetworkInterface) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[name] - if !ok || rs.Primary.ID == "" { - return fmt.Errorf("Not found: %s", name) - } - - if rs.Primary.Attributes[attr] != *conf.AvailabilityZone { - return fmt.Errorf("%s", fmt.Sprintf("expected %s, found %s", rs.Primary.Attributes[attr], *conf.AvailabilityZone)) - } - - return nil - } -} - -func testAccCheckAWSENIAttributesWithAttachment(conf *ec2.NetworkInterface) resource.TestCheckFunc { - return func(s *terraform.State) error { - if conf.Attachment == nil { - return fmt.Errorf("expected attachment to be set, but was nil") - } - - if *conf.Attachment.DeviceIndex != 1 { - return fmt.Errorf("expected attachment device index to be 1, but was %d", *conf.Attachment.DeviceIndex) - } - - if len(conf.Groups) != 1 && *conf.Groups[0].GroupName != "foo" { - return fmt.Errorf("expected security group to be foo, but was %#v", conf.Groups) - } - - if *conf.PrivateIpAddress != "172.16.10.100" { - return fmt.Errorf("expected private ip to be 172.16.10.100, but was %s", *conf.PrivateIpAddress) - } - - expectedPrivateDnsName := fmt.Sprintf("ip-%s.%s", resourceAwsEc2DashIP(*conf.PrivateIpAddress), resourceAwsEc2RegionalPrivateDnsSuffix(testAccGetRegion())) - if *conf.PrivateDnsName != expectedPrivateDnsName { - return fmt.Errorf("expected private dns name to be %s, but was %s", expectedPrivateDnsName, *conf.PrivateDnsName) - } - - return nil - } -} - func testAccCheckAWSENIDestroy(s *terraform.State) error { for _, rs := range s.RootModule().Resources { if rs.Type != "aws_network_interface" { @@ -614,28 +589,30 @@ func testAccCheckAWSENIMakeExternalAttachment(n string, conf *ec2.NetworkInterfa if !ok || rs.Primary.ID == "" { return fmt.Errorf("Not found: %s", n) } + input := &ec2.AttachNetworkInterfaceInput{ DeviceIndex: aws.Int64(1), InstanceId: aws.String(rs.Primary.ID), NetworkInterfaceId: conf.NetworkInterfaceId, } - conn := testAccProvider.Meta().(*AWSClient).ec2conn - _, err := conn.AttachNetworkInterface(input) + + _, err := testAccProvider.Meta().(*AWSClient).ec2conn.AttachNetworkInterface(input) + if err != nil { - return fmt.Errorf("Error attaching ENI: %s", err) + return fmt.Errorf("error attaching ENI: %w", err) } return nil } } -func testAccAWSENIConfig() string { - return composeConfig(testAccAvailableAZsNoOptInConfig(), ` +func testAccAWSENIIPV4ConfigBase(rName string) string { + return composeConfig(testAccAvailableAZsNoOptInConfig(), fmt.Sprintf(` resource "aws_vpc" "test" { cidr_block = "172.16.0.0/16" enable_dns_hostnames = true tags = { - Name = "tf-acc-network-interface" + Name = %[1]q } } @@ -643,15 +620,15 @@ resource "aws_subnet" "test" { vpc_id = aws_vpc.test.id cidr_block = "172.16.10.0/24" availability_zone = data.aws_availability_zones.available.names[0] + tags = { - Name = "tf-acc-network-interface" + Name = %[1]q } } resource "aws_security_group" "test" { - vpc_id = aws_vpc.test.id - description = "test" - name = "tf-acc-network-interface" + vpc_id = aws_vpc.test.id + name = %[1]q egress { from_port = 0 @@ -661,21 +638,14 @@ resource "aws_security_group" "test" { } tags = { - Name = "tf-acc-network-interface" + Name = %[1]q } } - -resource "aws_network_interface" "test" { - subnet_id = aws_subnet.test.id - private_ips = ["172.16.10.100"] - security_groups = [aws_security_group.test.id] - description = "Managed by Terraform" -} -`) +`, rName)) } func testAccAWSENIIPV6ConfigBase(rName string) string { - return testAccAvailableAZsNoOptInConfig() + fmt.Sprintf(` + return composeConfig(testAccAvailableAZsNoOptInConfig(), fmt.Sprintf(` resource "aws_vpc" "test" { cidr_block = "172.16.0.0/16" assign_generated_ipv6_cidr_block = true @@ -698,9 +668,8 @@ resource "aws_subnet" "test" { } resource "aws_security_group" "test" { - vpc_id = aws_vpc.test.id - description = "test" - name = %[1]q + vpc_id = aws_vpc.test.id + name = %[1]q egress { from_port = 0 @@ -713,187 +682,107 @@ resource "aws_security_group" "test" { Name = %[1]q } } -`, rName) +`, rName)) } -func testAccAWSENIIPV6Config(rName string) string { - return composeConfig(testAccAWSENIIPV6ConfigBase(rName), ` +func testAccAWSENIConfig(rName string) string { + return composeConfig(testAccAWSENIIPV4ConfigBase(rName), ` resource "aws_network_interface" "test" { - subnet_id = aws_subnet.test.id - private_ips = ["172.16.10.100"] - ipv6_addresses = [cidrhost(aws_subnet.test.ipv6_cidr_block, 4)] - security_groups = [aws_security_group.test.id] - description = "Managed by Terraform" + subnet_id = aws_subnet.test.id } `) } -func testAccAWSENIIPV6MultipleConfig(rName string) string { - return composeConfig(testAccAWSENIIPV6ConfigBase(rName), ` +func testAccAWSENIConfigIPV6(rName string) string { + return composeConfig(testAccAWSENIIPV6ConfigBase(rName), fmt.Sprintf(` resource "aws_network_interface" "test" { subnet_id = aws_subnet.test.id private_ips = ["172.16.10.100"] - ipv6_addresses = [cidrhost(aws_subnet.test.ipv6_cidr_block, 4), cidrhost(aws_subnet.test.ipv6_cidr_block, 8)] + ipv6_addresses = [cidrhost(aws_subnet.test.ipv6_cidr_block, 4)] security_groups = [aws_security_group.test.id] - description = "Managed by Terraform" -} -`) -} - -func testAccAWSENIIPV6CountConfig(ipCount int, rName string) string { - return testAccAWSENIIPV6ConfigBase(rName) + fmt.Sprintf(` -resource "aws_network_interface" "test" { - subnet_id = aws_subnet.test.id - private_ips = ["172.16.10.100"] - ipv6_address_count = %[1]d - security_groups = [aws_security_group.test.id] - description = "Managed by Terraform" -} -`, ipCount) -} - -func testAccAWSENIConfigUpdatedDescription() string { - return composeConfig(testAccAvailableAZsNoOptInConfig(), ` -resource "aws_vpc" "test" { - cidr_block = "172.16.0.0/16" - enable_dns_hostnames = true - - tags = { - Name = "terraform-testacc-network-interface" - } -} -resource "aws_subnet" "test" { - vpc_id = aws_vpc.test.id - cidr_block = "172.16.10.0/24" - availability_zone = data.aws_availability_zones.available.names[0] tags = { - Name = "tf-acc-network-interface" + Name = %[1]q } } - -resource "aws_security_group" "test" { - vpc_id = aws_vpc.test.id - description = "test" - name = "tf-acc-network-interface" - - egress { - from_port = 0 - to_port = 0 - protocol = "tcp" - cidr_blocks = ["10.0.0.0/16"] - } +`, rName)) } +func testAccAWSENIConfigIPV6Multiple(rName string) string { + return composeConfig(testAccAWSENIIPV6ConfigBase(rName), fmt.Sprintf(` resource "aws_network_interface" "test" { subnet_id = aws_subnet.test.id private_ips = ["172.16.10.100"] + ipv6_addresses = [cidrhost(aws_subnet.test.ipv6_cidr_block, 4), cidrhost(aws_subnet.test.ipv6_cidr_block, 8)] security_groups = [aws_security_group.test.id] - description = "Updated ENI Description" -} -`) -} - -func testAccAWSENIConfigWithSourceDestCheck(enabled bool) string { - return testAccAvailableAZsNoOptInConfig() + fmt.Sprintf(` -resource "aws_vpc" "test" { - cidr_block = "172.16.0.0/16" - enable_dns_hostnames = true tags = { - Name = "terraform-testacc-network-interface-w-source-dest-check" + Name = %[1]q } } - -resource "aws_subnet" "test" { - vpc_id = aws_vpc.test.id - cidr_block = "172.16.10.0/24" - availability_zone = data.aws_availability_zones.available.names[0] - - tags = { - Name = "tf-acc-network-interface-w-source-dest-check" - } +`, rName)) } +func testAccAWSENIConfigIPV6Count(rName string, ipv6Count int) string { + return composeConfig(testAccAWSENIIPV6ConfigBase(rName) + fmt.Sprintf(` resource "aws_network_interface" "test" { - subnet_id = aws_subnet.test.id - source_dest_check = %[1]t - private_ips = ["172.16.10.100"] + subnet_id = aws_subnet.test.id + private_ips = ["172.16.10.100"] + ipv6_address_count = %[2]d + security_groups = [aws_security_group.test.id] tags = { - Name = "tf-acc-network-interface-w-source-dest-check" + Name = %[1]q } } -`, enabled) +`, rName, ipv6Count)) } -func testAccAWSENIConfigWithNoPrivateIPs() string { - return composeConfig(testAccAvailableAZsNoOptInConfig(), ` -resource "aws_vpc" "test" { - cidr_block = "172.16.0.0/16" - enable_dns_hostnames = true +func testAccAWSENIConfigDescription(rName, description string) string { + return composeConfig(testAccAWSENIIPV4ConfigBase(rName), fmt.Sprintf(` +resource "aws_network_interface" "test" { + subnet_id = aws_subnet.test.id + private_ips = ["172.16.10.100"] + security_groups = [aws_security_group.test.id] + description = %[2]q tags = { - Name = "terraform-testacc-network-interface-w-no-private-ips" + Name = %[1]q } } - -resource "aws_subnet" "test" { - vpc_id = aws_vpc.test.id - cidr_block = "172.16.10.0/24" - availability_zone = data.aws_availability_zones.available.names[0] - - tags = { - Name = "tf-acc-network-interface-w-no-private-ips" - } +`, rName, description)) } +func testAccAWSENIConfigSourceDestCheck(rName string, sourceDestCheck bool) string { + return composeConfig(testAccAWSENIIPV6ConfigBase(rName) + fmt.Sprintf(` resource "aws_network_interface" "test" { subnet_id = aws_subnet.test.id - source_dest_check = false -} -`) -} - -func testAccAWSENIConfigWithAttachment() string { - return composeConfig(testAccLatestAmazonLinuxHvmEbsAmiConfig(), - testAccAvailableEc2InstanceTypeForRegion("t3.micro", "t2.micro"), - testAccAvailableAZsNoOptInConfig(), ` -resource "aws_vpc" "test" { - cidr_block = "172.16.0.0/16" - enable_dns_hostnames = true + source_dest_check = %[2]t + private_ips = ["172.16.10.100"] tags = { - Name = "terraform-testacc-network-interface-w-attachment" + Name = %[1]q } } - -resource "aws_subnet" "test1" { - vpc_id = aws_vpc.test.id - cidr_block = "172.16.10.0/24" - availability_zone = data.aws_availability_zones.available.names[0] - - tags = { - Name = "tf-acc-network-interface-w-attachment-test" - } +`, rName, sourceDestCheck)) } +func testAccAWSENIConfigAttachment(rName string) string { + return composeConfig( + testAccLatestAmazonLinuxHvmEbsAmiConfig(), + testAccAvailableEc2InstanceTypeForRegion("t3.micro", "t2.micro"), + testAccAWSENIIPV4ConfigBase(rName), + fmt.Sprintf(` resource "aws_subnet" "test2" { vpc_id = aws_vpc.test.id cidr_block = "172.16.11.0/24" availability_zone = data.aws_availability_zones.available.names[0] tags = { - Name = "tf-acc-network-interface-w-attachment-test" + Name = %[1]q } } -resource "aws_security_group" "test" { - vpc_id = aws_vpc.test.id - description = "test" - name = "tf-acc-network-interface-w-attachment-test" -} - resource "aws_instance" "test" { ami = data.aws_ami.amzn-ami-minimal-hvm-ebs.id instance_type = data.aws_ec2_instance_type_offering.available.instance_type @@ -902,12 +791,12 @@ resource "aws_instance" "test" { private_ip = "172.16.11.50" tags = { - Name = "test-tf-eni-test" + Name = %[1]q } } resource "aws_network_interface" "test" { - subnet_id = aws_subnet.test1.id + subnet_id = aws_subnet.test.id private_ips = ["172.16.10.100"] security_groups = [aws_security_group.test.id] @@ -917,51 +806,28 @@ resource "aws_network_interface" "test" { } tags = { - Name = "test_interface" + Name = %[1]q } } -`) +`, rName)) } -func testAccAWSENIConfigExternalAttachment() string { - return composeConfig(testAccLatestAmazonLinuxHvmEbsAmiConfig(), +func testAccAWSENIConfigExternalAttachment(rName string) string { + return composeConfig( + testAccLatestAmazonLinuxHvmEbsAmiConfig(), testAccAvailableEc2InstanceTypeForRegion("t3.micro", "t2.micro"), - testAccAvailableAZsNoOptInConfig(), ` -resource "aws_vpc" "test" { - cidr_block = "172.16.0.0/16" - enable_dns_hostnames = true - - tags = { - Name = "terraform-testacc-network-interface-external-attachment" - } -} - -resource "aws_subnet" "test1" { - vpc_id = aws_vpc.test.id - cidr_block = "172.16.10.0/24" - availability_zone = data.aws_availability_zones.available.names[0] - - tags = { - Name = "tf-acc-network-interface-external-attachment-test" - } -} - + testAccAWSENIIPV4ConfigBase(rName), + fmt.Sprintf(` resource "aws_subnet" "test2" { vpc_id = aws_vpc.test.id cidr_block = "172.16.11.0/24" availability_zone = data.aws_availability_zones.available.names[0] tags = { - Name = "tf-acc-network-interface-external-attachment-test" + Name = %[1]q } } -resource "aws_security_group" "test" { - vpc_id = aws_vpc.test.id - description = "test" - name = "tf-acc-network-interface-external-attachment-test" -} - resource "aws_instance" "test" { ami = data.aws_ami.amzn-ami-minimal-hvm-ebs.id instance_type = data.aws_ec2_instance_type_offering.available.instance_type @@ -970,147 +836,75 @@ resource "aws_instance" "test" { private_ip = "172.16.11.50" tags = { - Name = "tf-eni-test" + Name = %[1]q } } resource "aws_network_interface" "test" { - subnet_id = aws_subnet.test1.id + subnet_id = aws_subnet.test.id private_ips = ["172.16.10.100"] security_groups = [aws_security_group.test.id] tags = { - Name = "test_interface" - } -} -`) -} - -func testAccAWSENIConfigPrivateIpsCount(privateIpsCount int) string { - return fmt.Sprintf(` -resource "aws_vpc" "test" { - cidr_block = "10.0.0.0/16" - - tags = { - Name = "tf-acc-test-network-interface-private-ips-count" + Name = %[1]q } } - -resource "aws_subnet" "test" { - cidr_block = "10.0.0.0/24" - vpc_id = aws_vpc.test.id - - tags = { - Name = "tf-acc-test-network-interface-private-ips-count" - } +`, rName)) } +func testAccAWSENIConfigPrivateIpsCount(rName string, privateIpsCount int) string { + return composeConfig(testAccAWSENIIPV4ConfigBase(rName), fmt.Sprintf(` resource "aws_network_interface" "test" { - private_ips_count = %[1]d + private_ips_count = %[2]d subnet_id = aws_subnet.test.id -} -`, privateIpsCount) -} - -func testAccAWSENITagsConfig1(rName, tagKey1, tagValue1 string) string { - return testAccAvailableAZsNoOptInConfig() + fmt.Sprintf(` -resource "aws_vpc" "test" { - cidr_block = "172.16.0.0/16" - enable_dns_hostnames = true tags = { Name = %[1]q } } - -resource "aws_subnet" "test" { - vpc_id = aws_vpc.test.id - cidr_block = "172.16.10.0/24" - availability_zone = data.aws_availability_zones.available.names[0] - - tags = { - Name = %[1]q - } -} - -resource "aws_security_group" "test" { - vpc_id = aws_vpc.test.id - description = %[1]q - name = %[1]q - - egress { - from_port = 0 - to_port = 0 - protocol = "tcp" - cidr_blocks = ["10.0.0.0/16"] - } - - tags = { - Name = %[1]q - } +`, rName, privateIpsCount)) } +func testAccAWSENIConfigTags1(rName, tagKey1, tagValue1 string) string { + return composeConfig(testAccAWSENIIPV4ConfigBase(rName), fmt.Sprintf(` resource "aws_network_interface" "test" { subnet_id = aws_subnet.test.id private_ips = ["172.16.10.100"] security_groups = [aws_security_group.test.id] - description = "Managed by Terraform" tags = { - %[2]q = %[3]q + %[1]q = %[2]q } } -`, rName, tagKey1, tagValue1) -} - -func testAccAWSENITagsConfig2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { - return testAccAvailableAZsNoOptInConfig() + fmt.Sprintf(` -resource "aws_vpc" "test" { - cidr_block = "172.16.0.0/16" - enable_dns_hostnames = true - - tags = { - Name = %[1]q - } +`, tagKey1, tagValue1)) } -resource "aws_subnet" "test" { - vpc_id = aws_vpc.test.id - cidr_block = "172.16.10.0/24" - availability_zone = data.aws_availability_zones.available.names[0] +func testAccAWSENIConfigTags2(rName, tagKey1, tagValue1, tagKey2, tagValue2 string) string { + return composeConfig(testAccAWSENIIPV4ConfigBase(rName), fmt.Sprintf(` +resource "aws_network_interface" "test" { + subnet_id = aws_subnet.test.id + private_ips = ["172.16.10.100"] + security_groups = [aws_security_group.test.id] tags = { - Name = %[1]q + %[1]q = %[2]q + %[3]q = %[4]q } } - -resource "aws_security_group" "test" { - vpc_id = aws_vpc.test.id - description = %[1]q - name = %[1]q - - egress { - from_port = 0 - to_port = 0 - protocol = "tcp" - cidr_blocks = ["10.0.0.0/16"] - } - - tags = { - Name = %[1]q - } +`, tagKey1, tagValue1, tagKey2, tagValue2)) } +func testAccAWSENIConfigInterfaceType(rName, interfaceType string) string { + return composeConfig(testAccAWSENIIPV4ConfigBase(rName), fmt.Sprintf(` resource "aws_network_interface" "test" { subnet_id = aws_subnet.test.id private_ips = ["172.16.10.100"] security_groups = [aws_security_group.test.id] - description = "Managed by Terraform" + interface_type = %[2]q tags = { - %[2]q = %[3]q - %[4]q = %[5]q + Name = %[1]q } } -`, rName, tagKey1, tagValue1, tagKey2, tagValue2) +`, rName, interfaceType)) } diff --git a/website/docs/r/launch_template.html.markdown b/website/docs/r/launch_template.html.markdown index 93ec27634e9..e2a18382f85 100644 --- a/website/docs/r/launch_template.html.markdown +++ b/website/docs/r/launch_template.html.markdown @@ -296,6 +296,7 @@ Each `network_interfaces` block supports the following: * `delete_on_termination` - Whether the network interface should be destroyed on instance termination. Defaults to `false` if not set. * `description` - Description of the network interface. * `device_index` - The integer index of the network interface attachment. +* `interface_type` - The type of network interface. To create an Elastic Fabric Adapter (EFA), specify `efa`. * `ipv6_addresses` - One or more specific IPv6 addresses from the IPv6 CIDR block range of your subnet. Conflicts with `ipv6_address_count` * `ipv6_address_count` - The number of IPv6 addresses to assign to a network interface. Conflicts with `ipv6_addresses` * `network_interface_id` - The ID of the network interface to attach. diff --git a/website/docs/r/network_interface.markdown b/website/docs/r/network_interface.markdown index 9711eb08455..8278879fbed 100644 --- a/website/docs/r/network_interface.markdown +++ b/website/docs/r/network_interface.markdown @@ -38,6 +38,10 @@ The following arguments are supported: * `security_groups` - (Optional) List of security group IDs to assign to the ENI. * `attachment` - (Optional) Block to define the attachment of the ENI. Documented below. * `source_dest_check` - (Optional) Whether to enable source destination checking for the ENI. Default true. + +-> **NOTE:** Changing `interface_type` will cause the resource to be destroyed and re-created. + +* `interface_type` - (Optional) Type of network interface to create. Set to `efa` for Elastic Fabric Adapter. * `tags` - (Optional) A map of tags to assign to the resource. If configured with a provider [`default_tags` configuration block](/docs/providers/aws/index.html#default_tags-configuration-block) present, tags with matching keys will overwrite those defined at the provider-level. The `attachment` block supports: