diff --git a/aws/resource_aws_dx_hosted_private_virtual_interface.go b/aws/resource_aws_dx_hosted_private_virtual_interface.go index dad93784e46..5752e765a0d 100644 --- a/aws/resource_aws_dx_hosted_private_virtual_interface.go +++ b/aws/resource_aws_dx_hosted_private_virtual_interface.go @@ -22,25 +22,28 @@ func resourceAwsDxHostedPrivateVirtualInterface() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "arn": { - Type: schema.TypeString, - Computed: true, - }, - "connection_id": { + "address_family": { Type: schema.TypeString, Required: true, ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + directconnect.AddressFamilyIpv4, + directconnect.AddressFamilyIpv6, + }, false), }, - "name": { + "amazon_address": { Type: schema.TypeString, - Required: true, + Optional: true, + Computed: true, ForceNew: true, }, - "vlan": { - Type: schema.TypeInt, - Required: true, - ForceNew: true, - ValidateFunc: validation.IntBetween(1, 4094), + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "aws_device": { + Type: schema.TypeString, + Computed: true, }, "bgp_asn": { Type: schema.TypeInt, @@ -53,11 +56,10 @@ func resourceAwsDxHostedPrivateVirtualInterface() *schema.Resource { Computed: true, ForceNew: true, }, - "address_family": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{directconnect.AddressFamilyIpv4, directconnect.AddressFamilyIpv6}, false), + "connection_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, }, "customer_address": { Type: schema.TypeString, @@ -65,10 +67,20 @@ func resourceAwsDxHostedPrivateVirtualInterface() *schema.Resource { Computed: true, ForceNew: true, }, - "amazon_address": { - Type: schema.TypeString, - Optional: true, + "jumbo_frame_capable": { + Type: schema.TypeBool, Computed: true, + }, + "mtu": { + Type: schema.TypeInt, + Default: 1500, + Optional: true, + ForceNew: true, + ValidateFunc: validation.IntInSlice([]int{1500, 9001}), + }, + "name": { + Type: schema.TypeString, + Required: true, ForceNew: true, }, "owner_account_id": { @@ -77,20 +89,11 @@ func resourceAwsDxHostedPrivateVirtualInterface() *schema.Resource { ForceNew: true, ValidateFunc: validateAwsAccountId, }, - "mtu": { + "vlan": { Type: schema.TypeInt, - Default: 1500, - Optional: true, + Required: true, ForceNew: true, - ValidateFunc: validation.IntInSlice([]int{1500, 9001}), - }, - "jumbo_frame_capable": { - Type: schema.TypeBool, - Computed: true, - }, - "aws_device": { - Type: schema.TypeString, - Computed: true, + ValidateFunc: validation.IntBetween(1, 4094), }, }, @@ -107,14 +110,17 @@ func resourceAwsDxHostedPrivateVirtualInterfaceCreate(d *schema.ResourceData, me req := &directconnect.AllocatePrivateVirtualInterfaceInput{ ConnectionId: aws.String(d.Get("connection_id").(string)), - OwnerAccount: aws.String(d.Get("owner_account_id").(string)), NewPrivateVirtualInterfaceAllocation: &directconnect.NewPrivateVirtualInterfaceAllocation{ - VirtualInterfaceName: aws.String(d.Get("name").(string)), - Vlan: aws.Int64(int64(d.Get("vlan").(int))), - Asn: aws.Int64(int64(d.Get("bgp_asn").(int))), AddressFamily: aws.String(d.Get("address_family").(string)), + Asn: aws.Int64(int64(d.Get("bgp_asn").(int))), Mtu: aws.Int64(int64(d.Get("mtu").(int))), + VirtualInterfaceName: aws.String(d.Get("name").(string)), + Vlan: aws.Int64(int64(d.Get("vlan").(int))), }, + OwnerAccount: aws.String(d.Get("owner_account_id").(string)), + } + if v, ok := d.GetOk("amazon_address"); ok && v.(string) != "" { + req.NewPrivateVirtualInterfaceAllocation.AmazonAddress = aws.String(v.(string)) } if v, ok := d.GetOk("bgp_auth_key"); ok && v.(string) != "" { req.NewPrivateVirtualInterfaceAllocation.AuthKey = aws.String(v.(string)) @@ -122,28 +128,17 @@ func resourceAwsDxHostedPrivateVirtualInterfaceCreate(d *schema.ResourceData, me if v, ok := d.GetOk("customer_address"); ok && v.(string) != "" { req.NewPrivateVirtualInterfaceAllocation.CustomerAddress = aws.String(v.(string)) } - if v, ok := d.GetOk("amazon_address"); ok && v.(string) != "" { - req.NewPrivateVirtualInterfaceAllocation.AmazonAddress = aws.String(v.(string)) - } if v, ok := d.GetOk("mtu"); ok && v.(int) != 0 { req.NewPrivateVirtualInterfaceAllocation.Mtu = aws.Int64(int64(v.(int))) } - log.Printf("[DEBUG] Creating Direct Connect hosted private virtual interface: %#v", req) + log.Printf("[DEBUG] Creating Direct Connect hosted private virtual interface: %s", req) resp, err := conn.AllocatePrivateVirtualInterface(req) if err != nil { - return fmt.Errorf("Error creating Direct Connect hosted private virtual interface: %s", err.Error()) + return fmt.Errorf("error creating Direct Connect hosted private virtual interface: %s", err) } d.SetId(aws.StringValue(resp.VirtualInterfaceId)) - arn := arn.ARN{ - Partition: meta.(*AWSClient).partition, - Region: meta.(*AWSClient).region, - Service: "directconnect", - AccountID: meta.(*AWSClient).accountid, - Resource: fmt.Sprintf("dxvif/%s", d.Id()), - }.String() - d.Set("arn", arn) if err := dxHostedPrivateVirtualInterfaceWaitUntilAvailable(conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { return err @@ -160,23 +155,31 @@ func resourceAwsDxHostedPrivateVirtualInterfaceRead(d *schema.ResourceData, meta return err } if vif == nil { - log.Printf("[WARN] Direct Connect virtual interface (%s) not found, removing from state", d.Id()) + log.Printf("[WARN] Direct Connect hosted private virtual interface (%s) not found, removing from state", d.Id()) d.SetId("") return nil } - d.Set("connection_id", vif.ConnectionId) - d.Set("name", vif.VirtualInterfaceName) - d.Set("vlan", vif.Vlan) + d.Set("address_family", vif.AddressFamily) + arn := arn.ARN{ + Partition: meta.(*AWSClient).partition, + Region: meta.(*AWSClient).region, + Service: "directconnect", + AccountID: meta.(*AWSClient).accountid, + Resource: fmt.Sprintf("dxvif/%s", d.Id()), + }.String() + d.Set("amazon_address", vif.AmazonAddress) + d.Set("arn", arn) + d.Set("aws_device", vif.AwsDeviceV2) d.Set("bgp_asn", vif.Asn) d.Set("bgp_auth_key", vif.AuthKey) - d.Set("address_family", vif.AddressFamily) + d.Set("connection_id", vif.ConnectionId) d.Set("customer_address", vif.CustomerAddress) - d.Set("amazon_address", vif.AmazonAddress) - d.Set("owner_account_id", vif.OwnerAccount) - d.Set("mtu", vif.Mtu) d.Set("jumbo_frame_capable", vif.JumboFrameCapable) - d.Set("aws_device", vif.AwsDeviceV2) + d.Set("mtu", vif.Mtu) + d.Set("name", vif.VirtualInterfaceName) + d.Set("owner_account_id", vif.OwnerAccount) + d.Set("vlan", vif.Vlan) return nil } @@ -186,14 +189,19 @@ func resourceAwsDxHostedPrivateVirtualInterfaceDelete(d *schema.ResourceData, me } func resourceAwsDxHostedPrivateVirtualInterfaceImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - arn := arn.ARN{ - Partition: meta.(*AWSClient).partition, - Region: meta.(*AWSClient).region, - Service: "directconnect", - AccountID: meta.(*AWSClient).accountid, - Resource: fmt.Sprintf("dxvif/%s", d.Id()), - }.String() - d.Set("arn", arn) + conn := meta.(*AWSClient).dxconn + + vif, err := dxVirtualInterfaceRead(d.Id(), conn) + if err != nil { + return nil, err + } + if vif == nil { + return nil, fmt.Errorf("virtual interface (%s) not found", d.Id()) + } + + if vifType := aws.StringValue(vif.VirtualInterfaceType); vifType != "private" { + return nil, fmt.Errorf("virtual interface (%s) has incorrect type: %s", d.Id(), vifType) + } return []*schema.ResourceData{d}, nil } diff --git a/aws/resource_aws_dx_hosted_private_virtual_interface_accepter.go b/aws/resource_aws_dx_hosted_private_virtual_interface_accepter.go index f7dbca21adf..e6dacd34978 100644 --- a/aws/resource_aws_dx_hosted_private_virtual_interface_accepter.go +++ b/aws/resource_aws_dx_hosted_private_virtual_interface_accepter.go @@ -26,6 +26,13 @@ func resourceAwsDxHostedPrivateVirtualInterfaceAccepter() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "dx_gateway_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"vpn_gateway_id"}, + }, + "tags": tagsSchema(), "virtual_interface_id": { Type: schema.TypeString, Required: true, @@ -37,13 +44,6 @@ func resourceAwsDxHostedPrivateVirtualInterfaceAccepter() *schema.Resource { ForceNew: true, ConflictsWith: []string{"dx_gateway_id"}, }, - "dx_gateway_id": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ConflictsWith: []string{"vpn_gateway_id"}, - }, - "tags": tagsSchema(), }, Timeouts: &schema.ResourceTimeout{ @@ -67,17 +67,17 @@ func resourceAwsDxHostedPrivateVirtualInterfaceAccepterCreate(d *schema.Resource req := &directconnect.ConfirmPrivateVirtualInterfaceInput{ VirtualInterfaceId: aws.String(vifId), } - if vgwOk && vgwIdRaw.(string) != "" { - req.VirtualGatewayId = aws.String(vgwIdRaw.(string)) - } if dxgwOk && dxgwIdRaw.(string) != "" { req.DirectConnectGatewayId = aws.String(dxgwIdRaw.(string)) } + if vgwOk && vgwIdRaw.(string) != "" { + req.VirtualGatewayId = aws.String(vgwIdRaw.(string)) + } - log.Printf("[DEBUG] Accepting Direct Connect hosted private virtual interface: %#v", req) + log.Printf("[DEBUG] Accepting Direct Connect hosted private virtual interface: %s", req) _, err := conn.ConfirmPrivateVirtualInterface(req) if err != nil { - return fmt.Errorf("Error accepting Direct Connect hosted private virtual interface: %s", err.Error()) + return fmt.Errorf("error accepting Direct Connect hosted private virtual interface: %s", err) } d.SetId(vifId) @@ -105,23 +105,26 @@ func resourceAwsDxHostedPrivateVirtualInterfaceAccepterRead(d *schema.ResourceDa return err } if vif == nil { - log.Printf("[WARN] Direct Connect virtual interface (%s) not found, removing from state", d.Id()) + log.Printf("[WARN] Direct Connect hosted private virtual interface (%s) not found, removing from state", d.Id()) d.SetId("") return nil } vifState := aws.StringValue(vif.VirtualInterfaceState) if vifState != directconnect.VirtualInterfaceStateAvailable && vifState != directconnect.VirtualInterfaceStateDown { - log.Printf("[WARN] Direct Connect virtual interface (%s) is '%s', removing from state", vifState, d.Id()) + log.Printf("[WARN] Direct Connect hosted private virtual interface (%s) is '%s', removing from state", vifState, d.Id()) d.SetId("") return nil } + d.Set("dx_gateway_id", vif.DirectConnectGatewayId) d.Set("virtual_interface_id", vif.VirtualInterfaceId) d.Set("vpn_gateway_id", vif.VirtualGatewayId) - d.Set("dx_gateway_id", vif.DirectConnectGatewayId) - err1 := getTagsDX(conn, d, d.Get("arn").(string)) - return err1 + if err := getTagsDX(conn, d, d.Get("arn").(string)); err != nil { + return fmt.Errorf("error getting Direct Connect hosted private virtual interface (%s) tags: %s", d.Id(), err) + } + + return nil } func resourceAwsDxHostedPrivateVirtualInterfaceAccepterUpdate(d *schema.ResourceData, meta interface{}) error { @@ -138,6 +141,20 @@ func resourceAwsDxHostedPrivateVirtualInterfaceAccepterDelete(d *schema.Resource } func resourceAwsDxHostedPrivateVirtualInterfaceAccepterImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + conn := meta.(*AWSClient).dxconn + + vif, err := dxVirtualInterfaceRead(d.Id(), conn) + if err != nil { + return nil, err + } + if vif == nil { + return nil, fmt.Errorf("virtual interface (%s) not found", d.Id()) + } + + if vifType := aws.StringValue(vif.VirtualInterfaceType); vifType != "private" { + return nil, fmt.Errorf("virtual interface (%s) has incorrect type: %s", d.Id(), vifType) + } + arn := arn.ARN{ Partition: meta.(*AWSClient).partition, Region: meta.(*AWSClient).region, diff --git a/aws/resource_aws_dx_hosted_private_virtual_interface_test.go b/aws/resource_aws_dx_hosted_private_virtual_interface_test.go index 4f40d621edd..81e721130aa 100644 --- a/aws/resource_aws_dx_hosted_private_virtual_interface_test.go +++ b/aws/resource_aws_dx_hosted_private_virtual_interface_test.go @@ -3,6 +3,8 @@ package aws import ( "fmt" "os" + "regexp" + "strconv" "testing" "github.com/aws/aws-sdk-go/aws" @@ -21,8 +23,10 @@ func TestAccAwsDxHostedPrivateVirtualInterface_basic(t *testing.T) { } var providers []*schema.Provider - resourceNameHostedVif := "aws_dx_hosted_private_virtual_interface.test" - resourceNameHostedVifAccepter := "aws_dx_hosted_private_virtual_interface_accepter.test" + var vif directconnect.VirtualInterface + resourceName := "aws_dx_hosted_private_virtual_interface.test" + accepterResourceName := "aws_dx_hosted_private_virtual_interface_accepter.test" + vpnGatewayResourceName := "aws_vpn_gateway.test" rName := fmt.Sprintf("tf-testacc-private-vif-%s", acctest.RandString(9)) bgpAsn := randIntRange(64512, 65534) vlan := randIntRange(2049, 4094) @@ -38,28 +42,29 @@ func TestAccAwsDxHostedPrivateVirtualInterface_basic(t *testing.T) { { Config: testAccDxHostedPrivateVirtualInterfaceConfig_basic(connectionId, rName, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( - testAccCheckAwsDxHostedPrivateVirtualInterfaceExists(resourceNameHostedVif), - testAccCheckAwsDxHostedPrivateVirtualInterfaceAccepterExists(resourceNameHostedVifAccepter), - resource.TestCheckResourceAttr(resourceNameHostedVif, "name", rName), - resource.TestCheckResourceAttr(resourceNameHostedVif, "mtu", "1500"), - resource.TestCheckResourceAttr(resourceNameHostedVif, "jumbo_frame_capable", "true"), - resource.TestCheckResourceAttr(resourceNameHostedVifAccepter, "tags.%", "0"), - ), - }, - { - Config: testAccDxHostedPrivateVirtualInterfaceConfig_updated(connectionId, rName, bgpAsn, vlan), - Check: resource.ComposeTestCheckFunc( - testAccCheckAwsDxHostedPrivateVirtualInterfaceExists(resourceNameHostedVif), - testAccCheckAwsDxHostedPrivateVirtualInterfaceAccepterExists(resourceNameHostedVifAccepter), - resource.TestCheckResourceAttr(resourceNameHostedVif, "name", rName), - resource.TestCheckResourceAttr(resourceNameHostedVifAccepter, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceNameHostedVifAccepter, "tags.Environment", "test"), + testAccCheckAwsDxHostedPrivateVirtualInterfaceExists(resourceName, &vif), + resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "directconnect", regexp.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + resource.TestCheckResourceAttrSet(resourceName, "aws_device"), + resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), + resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), + resource.TestCheckResourceAttr(resourceName, "connection_id", connectionId), + resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", "true"), + resource.TestCheckResourceAttr(resourceName, "mtu", "1500"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttrSet(resourceName, "owner_account_id"), + resource.TestCheckResourceAttr(resourceName, "vlan", strconv.Itoa(vlan)), + // Accepter's attributes: + resource.TestCheckResourceAttrSet(accepterResourceName, "arn"), + resource.TestCheckResourceAttr(accepterResourceName, "tags.%", "0"), + resource.TestCheckResourceAttrPair(accepterResourceName, "virtual_interface_id", resourceName, "id"), + resource.TestCheckResourceAttrPair(accepterResourceName, "vpn_gateway_id", vpnGatewayResourceName, "id"), ), }, // Test import. { - Config: testAccDxHostedPrivateVirtualInterfaceConfig_updated(connectionId, rName, bgpAsn, vlan), - ResourceName: resourceNameHostedVif, + Config: testAccDxHostedPrivateVirtualInterfaceConfig_basic(connectionId, rName, bgpAsn, vlan), + ResourceName: resourceName, ImportState: true, ImportStateVerify: true, }, @@ -67,64 +72,102 @@ func TestAccAwsDxHostedPrivateVirtualInterface_basic(t *testing.T) { }) } -func testAccCheckAwsDxHostedPrivateVirtualInterfaceDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*AWSClient).dxconn - - for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_dx_hosted_private_virtual_interface" { - continue - } - - input := &directconnect.DescribeVirtualInterfacesInput{ - VirtualInterfaceId: aws.String(rs.Primary.ID), - } - - resp, err := conn.DescribeVirtualInterfaces(input) - if err != nil { - return err - } - for _, v := range resp.VirtualInterfaces { - if *v.VirtualInterfaceId == rs.Primary.ID && !(*v.VirtualInterfaceState == directconnect.VirtualInterfaceStateDeleted) { - return fmt.Errorf("[DESTROY ERROR] Dx Private VIF (%s) not deleted", rs.Primary.ID) - } - } +func TestAccAwsDxHostedPrivateVirtualInterface_AccepterTags(t *testing.T) { + key := "DX_CONNECTION_ID" + connectionId := os.Getenv(key) + if connectionId == "" { + t.Skipf("Environment variable %s is not set", key) } - return nil -} -func testAccCheckAwsDxHostedPrivateVirtualInterfaceExists(name string) resource.TestCheckFunc { - return func(s *terraform.State) error { - _, ok := s.RootModule().Resources[name] - if !ok { - return fmt.Errorf("Not found: %s", name) - } + var providers []*schema.Provider + var vif directconnect.VirtualInterface + resourceName := "aws_dx_hosted_private_virtual_interface.test" + accepterResourceName := "aws_dx_hosted_private_virtual_interface_accepter.test" + vpnGatewayResourceName := "aws_vpn_gateway.test" + rName := fmt.Sprintf("tf-testacc-private-vif-%s", acctest.RandString(9)) + bgpAsn := randIntRange(64512, 65534) + vlan := randIntRange(2049, 4094) - return nil - } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccAlternateAccountPreCheck(t) + }, + ProviderFactories: testAccProviderFactories(&providers), + CheckDestroy: testAccCheckAwsDxHostedPrivateVirtualInterfaceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDxHostedPrivateVirtualInterfaceConfig_accepterTags(connectionId, rName, bgpAsn, vlan), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsDxHostedPrivateVirtualInterfaceExists(resourceName, &vif), + resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "directconnect", regexp.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + resource.TestCheckResourceAttrSet(resourceName, "aws_device"), + resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), + resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), + resource.TestCheckResourceAttr(resourceName, "connection_id", connectionId), + resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", "true"), + resource.TestCheckResourceAttr(resourceName, "mtu", "1500"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttrSet(resourceName, "owner_account_id"), + resource.TestCheckResourceAttr(resourceName, "vlan", strconv.Itoa(vlan)), + // Accepter's attributes: + resource.TestCheckResourceAttrSet(accepterResourceName, "arn"), + resource.TestCheckResourceAttr(accepterResourceName, "tags.%", "3"), + resource.TestCheckResourceAttr(accepterResourceName, "tags.Name", rName), + resource.TestCheckResourceAttr(accepterResourceName, "tags.Key1", "Value1"), + resource.TestCheckResourceAttr(accepterResourceName, "tags.Key2", "Value2a"), + resource.TestCheckResourceAttrPair(accepterResourceName, "virtual_interface_id", resourceName, "id"), + resource.TestCheckResourceAttrPair(accepterResourceName, "vpn_gateway_id", vpnGatewayResourceName, "id"), + ), + }, + { + Config: testAccDxHostedPrivateVirtualInterfaceConfig_accepterTagsUpdated(connectionId, rName, bgpAsn, vlan), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsDxHostedPrivateVirtualInterfaceExists(resourceName, &vif), + resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "directconnect", regexp.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + resource.TestCheckResourceAttrSet(resourceName, "aws_device"), + resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), + resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), + resource.TestCheckResourceAttr(resourceName, "connection_id", connectionId), + resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", "true"), + resource.TestCheckResourceAttr(resourceName, "mtu", "1500"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttrSet(resourceName, "owner_account_id"), + resource.TestCheckResourceAttr(resourceName, "vlan", strconv.Itoa(vlan)), + // Accepter's attributes: + resource.TestCheckResourceAttrSet(accepterResourceName, "arn"), + resource.TestCheckResourceAttr(accepterResourceName, "tags.%", "3"), + resource.TestCheckResourceAttr(accepterResourceName, "tags.Name", rName), + resource.TestCheckResourceAttr(accepterResourceName, "tags.Key2", "Value2b"), + resource.TestCheckResourceAttr(accepterResourceName, "tags.Key3", "Value3"), + resource.TestCheckResourceAttrPair(accepterResourceName, "virtual_interface_id", resourceName, "id"), + resource.TestCheckResourceAttrPair(accepterResourceName, "vpn_gateway_id", vpnGatewayResourceName, "id"), + ), + }, + }, + }) } -func testAccCheckAwsDxHostedPrivateVirtualInterfaceAccepterExists(name string) resource.TestCheckFunc { - return func(s *terraform.State) error { - _, ok := s.RootModule().Resources[name] - if !ok { - return fmt.Errorf("Not found: %s", name) - } +func testAccCheckAwsDxHostedPrivateVirtualInterfaceDestroy(s *terraform.State) error { + return testAccCheckDxVirtualInterfaceDestroy(s, "aws_dx_hosted_private_virtual_interface") +} - return nil - } +func testAccCheckAwsDxHostedPrivateVirtualInterfaceExists(name string, vif *directconnect.VirtualInterface) resource.TestCheckFunc { + return testAccCheckDxVirtualInterfaceExists(name, vif) } func testAccDxHostedPrivateVirtualInterfaceConfig_base(cid, rName string, bgpAsn, vlan int) string { return testAccAlternateAccountProviderConfig() + fmt.Sprintf(` # Creator resource "aws_dx_hosted_private_virtual_interface" "test" { + address_family = "ipv4" + bgp_asn = %[3]d connection_id = %[1]q + name = %[2]q owner_account_id = "${data.aws_caller_identity.accepter.account_id}" - - name = %[2]q - vlan = %[4]d - address_family = "ipv4" - bgp_asn = %[3]d + vlan = %[4]d # The aws_dx_hosted_private_virtual_interface # must be destroyed before the aws_vpn_gateway. @@ -149,7 +192,7 @@ resource "aws_vpn_gateway" "test" { func testAccDxHostedPrivateVirtualInterfaceConfig_basic(cid, rName string, bgpAsn, vlan int) string { return testAccDxHostedPrivateVirtualInterfaceConfig_base(cid, rName, bgpAsn, vlan) + fmt.Sprintf(` resource "aws_dx_hosted_private_virtual_interface_accepter" "test" { - provider = "aws.alternate" + provider = "aws.alternate" virtual_interface_id = "${aws_dx_hosted_private_virtual_interface.test.id}" vpn_gateway_id = "${aws_vpn_gateway.test.id}" @@ -157,17 +200,36 @@ resource "aws_dx_hosted_private_virtual_interface_accepter" "test" { `) } -func testAccDxHostedPrivateVirtualInterfaceConfig_updated(cid, rName string, bgpAsn, vlan int) string { +func testAccDxHostedPrivateVirtualInterfaceConfig_accepterTags(cid, rName string, bgpAsn, vlan int) string { + return testAccDxHostedPrivateVirtualInterfaceConfig_base(cid, rName, bgpAsn, vlan) + fmt.Sprintf(` +resource "aws_dx_hosted_private_virtual_interface_accepter" "test" { + provider = "aws.alternate" + + virtual_interface_id = "${aws_dx_hosted_private_virtual_interface.test.id}" + vpn_gateway_id = "${aws_vpn_gateway.test.id}" + + tags = { + Name = %[1]q + Key1 = "Value1" + Key2 = "Value2a" + } +} +`, rName) +} + +func testAccDxHostedPrivateVirtualInterfaceConfig_accepterTagsUpdated(cid, rName string, bgpAsn, vlan int) string { return testAccDxHostedPrivateVirtualInterfaceConfig_base(cid, rName, bgpAsn, vlan) + fmt.Sprintf(` resource "aws_dx_hosted_private_virtual_interface_accepter" "test" { - provider = "aws.alternate" + provider = "aws.alternate" virtual_interface_id = "${aws_dx_hosted_private_virtual_interface.test.id}" vpn_gateway_id = "${aws_vpn_gateway.test.id}" tags = { - Environment = "test" + Name = %[1]q + Key2 = "Value2b" + Key3 = "Value3" } } -`) +`, rName) } diff --git a/aws/resource_aws_dx_hosted_public_virtual_interface.go b/aws/resource_aws_dx_hosted_public_virtual_interface.go index 5d123f6c841..60a2a8d0d52 100644 --- a/aws/resource_aws_dx_hosted_public_virtual_interface.go +++ b/aws/resource_aws_dx_hosted_public_virtual_interface.go @@ -20,27 +20,31 @@ func resourceAwsDxHostedPublicVirtualInterface() *schema.Resource { Importer: &schema.ResourceImporter{ State: resourceAwsDxHostedPublicVirtualInterfaceImport, }, + CustomizeDiff: resourceAwsDxHostedPublicVirtualInterfaceCustomizeDiff, Schema: map[string]*schema.Schema{ - "arn": { - Type: schema.TypeString, - Computed: true, - }, - "connection_id": { + "address_family": { Type: schema.TypeString, Required: true, ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + directconnect.AddressFamilyIpv4, + directconnect.AddressFamilyIpv6, + }, false), }, - "name": { + "amazon_address": { Type: schema.TypeString, - Required: true, + Optional: true, + Computed: true, ForceNew: true, }, - "vlan": { - Type: schema.TypeInt, - Required: true, - ForceNew: true, - ValidateFunc: validation.IntBetween(1, 4094), + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "aws_device": { + Type: schema.TypeString, + Computed: true, }, "bgp_asn": { Type: schema.TypeInt, @@ -53,11 +57,10 @@ func resourceAwsDxHostedPublicVirtualInterface() *schema.Resource { Computed: true, ForceNew: true, }, - "address_family": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{directconnect.AddressFamilyIpv4, directconnect.AddressFamilyIpv6}, false), + "connection_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, }, "customer_address": { Type: schema.TypeString, @@ -65,10 +68,9 @@ func resourceAwsDxHostedPublicVirtualInterface() *schema.Resource { Computed: true, ForceNew: true, }, - "amazon_address": { + "name": { Type: schema.TypeString, - Optional: true, - Computed: true, + Required: true, ForceNew: true, }, "owner_account_id": { @@ -84,9 +86,11 @@ func resourceAwsDxHostedPublicVirtualInterface() *schema.Resource { Elem: &schema.Schema{Type: schema.TypeString}, MinItems: 1, }, - "aws_device": { - Type: schema.TypeString, - Computed: true, + "vlan": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + ValidateFunc: validation.IntBetween(1, 4094), }, }, @@ -100,56 +104,36 @@ func resourceAwsDxHostedPublicVirtualInterface() *schema.Resource { func resourceAwsDxHostedPublicVirtualInterfaceCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).dxconn - addressFamily := d.Get("address_family").(string) - caRaw, caOk := d.GetOk("customer_address") - aaRaw, aaOk := d.GetOk("amazon_address") - if addressFamily == directconnect.AddressFamilyIpv4 { - if !caOk { - return fmt.Errorf("'customer_address' must be set when 'address_family' is '%s'", addressFamily) - } - if !aaOk { - return fmt.Errorf("'amazon_address' must be set when 'address_family' is '%s'", addressFamily) - } - } - req := &directconnect.AllocatePublicVirtualInterfaceInput{ ConnectionId: aws.String(d.Get("connection_id").(string)), - OwnerAccount: aws.String(d.Get("owner_account_id").(string)), NewPublicVirtualInterfaceAllocation: &directconnect.NewPublicVirtualInterfaceAllocation{ + AddressFamily: aws.String(d.Get("address_family").(string)), + Asn: aws.Int64(int64(d.Get("bgp_asn").(int))), VirtualInterfaceName: aws.String(d.Get("name").(string)), Vlan: aws.Int64(int64(d.Get("vlan").(int))), - Asn: aws.Int64(int64(d.Get("bgp_asn").(int))), - AddressFamily: aws.String(addressFamily), }, + OwnerAccount: aws.String(d.Get("owner_account_id").(string)), + } + if v, ok := d.GetOk("amazon_address"); ok && v.(string) != "" { + req.NewPublicVirtualInterfaceAllocation.AmazonAddress = aws.String(v.(string)) } if v, ok := d.GetOk("bgp_auth_key"); ok && v.(string) != "" { req.NewPublicVirtualInterfaceAllocation.AuthKey = aws.String(v.(string)) } - if caOk && caRaw.(string) != "" { - req.NewPublicVirtualInterfaceAllocation.CustomerAddress = aws.String(caRaw.(string)) - } - if aaOk && aaRaw.(string) != "" { - req.NewPublicVirtualInterfaceAllocation.AmazonAddress = aws.String(aaRaw.(string)) + if v, ok := d.GetOk("customer_address"); ok && v.(string) != "" { + req.NewPublicVirtualInterfaceAllocation.CustomerAddress = aws.String(v.(string)) } if v, ok := d.GetOk("route_filter_prefixes"); ok { req.NewPublicVirtualInterfaceAllocation.RouteFilterPrefixes = expandDxRouteFilterPrefixes(v.(*schema.Set)) } - log.Printf("[DEBUG] Allocating Direct Connect hosted public virtual interface: %#v", req) + log.Printf("[DEBUG] Allocating Direct Connect hosted public virtual interface: %s", req) resp, err := conn.AllocatePublicVirtualInterface(req) if err != nil { - return fmt.Errorf("Error allocating Direct Connect hosted public virtual interface: %s", err.Error()) + return fmt.Errorf("error allocating Direct Connect hosted public virtual interface: %s", err) } d.SetId(aws.StringValue(resp.VirtualInterfaceId)) - arn := arn.ARN{ - Partition: meta.(*AWSClient).partition, - Region: meta.(*AWSClient).region, - Service: "directconnect", - AccountID: meta.(*AWSClient).accountid, - Resource: fmt.Sprintf("dxvif/%s", d.Id()), - }.String() - d.Set("arn", arn) if err := dxHostedPublicVirtualInterfaceWaitUntilAvailable(conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { return err @@ -171,17 +155,27 @@ func resourceAwsDxHostedPublicVirtualInterfaceRead(d *schema.ResourceData, meta return nil } - d.Set("connection_id", vif.ConnectionId) - d.Set("name", vif.VirtualInterfaceName) - d.Set("vlan", vif.Vlan) + d.Set("address_family", vif.AddressFamily) + d.Set("amazon_address", vif.AmazonAddress) + arn := arn.ARN{ + Partition: meta.(*AWSClient).partition, + Region: meta.(*AWSClient).region, + Service: "directconnect", + AccountID: meta.(*AWSClient).accountid, + Resource: fmt.Sprintf("dxvif/%s", d.Id()), + }.String() + d.Set("arn", arn) + d.Set("aws_device", vif.AwsDeviceV2) d.Set("bgp_asn", vif.Asn) d.Set("bgp_auth_key", vif.AuthKey) - d.Set("address_family", vif.AddressFamily) + d.Set("connection_id", vif.ConnectionId) d.Set("customer_address", vif.CustomerAddress) - d.Set("amazon_address", vif.AmazonAddress) - d.Set("route_filter_prefixes", flattenDxRouteFilterPrefixes(vif.RouteFilterPrefixes)) + d.Set("name", vif.VirtualInterfaceName) d.Set("owner_account_id", vif.OwnerAccount) - d.Set("aws_device", vif.AwsDeviceV2) + if err := d.Set("route_filter_prefixes", flattenDxRouteFilterPrefixes(vif.RouteFilterPrefixes)); err != nil { + return fmt.Errorf("error setting route_filter_prefixes: %s", err) + } + d.Set("vlan", vif.Vlan) return nil } @@ -191,18 +185,39 @@ func resourceAwsDxHostedPublicVirtualInterfaceDelete(d *schema.ResourceData, met } func resourceAwsDxHostedPublicVirtualInterfaceImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - arn := arn.ARN{ - Partition: meta.(*AWSClient).partition, - Region: meta.(*AWSClient).region, - Service: "directconnect", - AccountID: meta.(*AWSClient).accountid, - Resource: fmt.Sprintf("dxvif/%s", d.Id()), - }.String() - d.Set("arn", arn) + conn := meta.(*AWSClient).dxconn + + vif, err := dxVirtualInterfaceRead(d.Id(), conn) + if err != nil { + return nil, err + } + if vif == nil { + return nil, fmt.Errorf("virtual interface (%s) not found", d.Id()) + } + + if vifType := aws.StringValue(vif.VirtualInterfaceType); vifType != "public" { + return nil, fmt.Errorf("virtual interface (%s) has incorrect type: %s", d.Id(), vifType) + } return []*schema.ResourceData{d}, nil } +func resourceAwsDxHostedPublicVirtualInterfaceCustomizeDiff(diff *schema.ResourceDiff, meta interface{}) error { + if diff.Id() == "" { + // New resource. + if addressFamily := diff.Get("address_family").(string); addressFamily == directconnect.AddressFamilyIpv4 { + if _, ok := diff.GetOk("customer_address"); !ok { + return fmt.Errorf("'customer_address' must be set when 'address_family' is '%s'", addressFamily) + } + if _, ok := diff.GetOk("amazon_address"); !ok { + return fmt.Errorf("'amazon_address' must be set when 'address_family' is '%s'", addressFamily) + } + } + } + + return nil +} + func dxHostedPublicVirtualInterfaceWaitUntilAvailable(conn *directconnect.DirectConnect, vifId string, timeout time.Duration) error { return dxVirtualInterfaceWaitUntilAvailable( conn, diff --git a/aws/resource_aws_dx_hosted_public_virtual_interface_accepter.go b/aws/resource_aws_dx_hosted_public_virtual_interface_accepter.go index 1038229bc38..8b2c05bb225 100644 --- a/aws/resource_aws_dx_hosted_public_virtual_interface_accepter.go +++ b/aws/resource_aws_dx_hosted_public_virtual_interface_accepter.go @@ -26,12 +26,12 @@ func resourceAwsDxHostedPublicVirtualInterfaceAccepter() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "tags": tagsSchema(), "virtual_interface_id": { Type: schema.TypeString, Required: true, ForceNew: true, }, - "tags": tagsSchema(), }, Timeouts: &schema.ResourceTimeout{ @@ -49,10 +49,10 @@ func resourceAwsDxHostedPublicVirtualInterfaceAccepterCreate(d *schema.ResourceD VirtualInterfaceId: aws.String(vifId), } - log.Printf("[DEBUG] Accepting Direct Connect hosted public virtual interface: %#v", req) + log.Printf("[DEBUG] Accepting Direct Connect hosted public virtual interface: %s", req) _, err := conn.ConfirmPublicVirtualInterface(req) if err != nil { - return fmt.Errorf("Error accepting Direct Connect hosted public virtual interface: %s", err.Error()) + return fmt.Errorf("error accepting Direct Connect hosted public virtual interface: %s", err) } d.SetId(vifId) @@ -80,7 +80,7 @@ func resourceAwsDxHostedPublicVirtualInterfaceAccepterRead(d *schema.ResourceDat return err } if vif == nil { - log.Printf("[WARN] Direct Connect virtual interface (%s) not found, removing from state", d.Id()) + log.Printf("[WARN] Direct Connect hosted public virtual interface (%s) not found, removing from state", d.Id()) d.SetId("") return nil } @@ -88,14 +88,17 @@ func resourceAwsDxHostedPublicVirtualInterfaceAccepterRead(d *schema.ResourceDat if vifState != directconnect.VirtualInterfaceStateAvailable && vifState != directconnect.VirtualInterfaceStateDown && vifState != directconnect.VirtualInterfaceStateVerifying { - log.Printf("[WARN] Direct Connect virtual interface (%s) is '%s', removing from state", vifState, d.Id()) + log.Printf("[WARN] Direct Connect hosted public virtual interface (%s) is '%s', removing from state", vifState, d.Id()) d.SetId("") return nil } d.Set("virtual_interface_id", vif.VirtualInterfaceId) - err1 := getTagsDX(conn, d, d.Get("arn").(string)) - return err1 + if err := getTagsDX(conn, d, d.Get("arn").(string)); err != nil { + return fmt.Errorf("error getting Direct Connect hosted private virtual interface (%s) tags: %s", d.Id(), err) + } + + return nil } func resourceAwsDxHostedPublicVirtualInterfaceAccepterUpdate(d *schema.ResourceData, meta interface{}) error { @@ -112,6 +115,20 @@ func resourceAwsDxHostedPublicVirtualInterfaceAccepterDelete(d *schema.ResourceD } func resourceAwsDxHostedPublicVirtualInterfaceAccepterImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + conn := meta.(*AWSClient).dxconn + + vif, err := dxVirtualInterfaceRead(d.Id(), conn) + if err != nil { + return nil, err + } + if vif == nil { + return nil, fmt.Errorf("virtual interface (%s) not found", d.Id()) + } + + if vifType := aws.StringValue(vif.VirtualInterfaceType); vifType != "public" { + return nil, fmt.Errorf("virtual interface (%s) has incorrect type: %s", d.Id(), vifType) + } + arn := arn.ARN{ Partition: meta.(*AWSClient).partition, Region: meta.(*AWSClient).region, diff --git a/aws/resource_aws_dx_hosted_public_virtual_interface_test.go b/aws/resource_aws_dx_hosted_public_virtual_interface_test.go index 9ba863f3c1a..a8cd04a5e4a 100644 --- a/aws/resource_aws_dx_hosted_public_virtual_interface_test.go +++ b/aws/resource_aws_dx_hosted_public_virtual_interface_test.go @@ -3,6 +3,8 @@ package aws import ( "fmt" "os" + "regexp" + "strconv" "testing" "github.com/aws/aws-sdk-go/aws" @@ -21,9 +23,12 @@ func TestAccAwsDxHostedPublicVirtualInterface_basic(t *testing.T) { } var providers []*schema.Provider - resourceNameHostedVif := "aws_dx_hosted_public_virtual_interface.test" - resourceNameHostedVifAccepter := "aws_dx_hosted_public_virtual_interface_accepter.test" + var vif directconnect.VirtualInterface + resourceName := "aws_dx_hosted_public_virtual_interface.test" + accepterResourceName := "aws_dx_hosted_public_virtual_interface_accepter.test" rName := fmt.Sprintf("tf-testacc-public-vif-%s", acctest.RandString(10)) + amazonAddress := "175.45.176.5/28" + customerAddress := "175.45.176.6/28" bgpAsn := randIntRange(64512, 65534) vlan := randIntRange(2049, 4094) @@ -36,28 +41,32 @@ func TestAccAwsDxHostedPublicVirtualInterface_basic(t *testing.T) { CheckDestroy: testAccCheckAwsDxHostedPublicVirtualInterfaceDestroy, Steps: []resource.TestStep{ { - Config: testAccDxHostedPublicVirtualInterfaceConfig_basic(connectionId, rName, bgpAsn, vlan), + Config: testAccDxHostedPublicVirtualInterfaceConfig_basic(connectionId, rName, amazonAddress, customerAddress, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( - testAccCheckAwsDxHostedPublicVirtualInterfaceExists(resourceNameHostedVif), - testAccCheckAwsDxHostedPublicVirtualInterfaceAccepterExists(resourceNameHostedVifAccepter), - resource.TestCheckResourceAttr(resourceNameHostedVif, "name", rName), - resource.TestCheckResourceAttr(resourceNameHostedVifAccepter, "tags.%", "0"), - ), - }, - { - Config: testAccDxHostedPublicVirtualInterfaceConfig_updated(connectionId, rName, bgpAsn, vlan), - Check: resource.ComposeTestCheckFunc( - testAccCheckAwsDxHostedPublicVirtualInterfaceExists(resourceNameHostedVif), - testAccCheckAwsDxHostedPublicVirtualInterfaceAccepterExists(resourceNameHostedVifAccepter), - resource.TestCheckResourceAttr(resourceNameHostedVif, "name", rName), - resource.TestCheckResourceAttr(resourceNameHostedVifAccepter, "tags.%", "1"), - resource.TestCheckResourceAttr(resourceNameHostedVifAccepter, "tags.Environment", "test"), + testAccCheckAwsDxHostedPublicVirtualInterfaceExists(resourceName, &vif), + resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), + resource.TestCheckResourceAttr(resourceName, "amazon_address", amazonAddress), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "directconnect", regexp.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + resource.TestCheckResourceAttrSet(resourceName, "aws_device"), + resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), + resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), + resource.TestCheckResourceAttr(resourceName, "connection_id", connectionId), + resource.TestCheckResourceAttr(resourceName, "customer_address", customerAddress), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "route_filter_prefixes.#", "2"), + resource.TestCheckResourceAttr(resourceName, "route_filter_prefixes.1752038751", "210.52.109.0/24"), + resource.TestCheckResourceAttr(resourceName, "route_filter_prefixes.4290081960", "175.45.176.0/22"), + resource.TestCheckResourceAttr(resourceName, "vlan", strconv.Itoa(vlan)), + // Accepter's attributes: + resource.TestCheckResourceAttrSet(accepterResourceName, "arn"), + resource.TestCheckResourceAttr(accepterResourceName, "tags.%", "0"), + resource.TestCheckResourceAttrPair(accepterResourceName, "virtual_interface_id", resourceName, "id"), ), }, // Test import. { - Config: testAccDxHostedPublicVirtualInterfaceConfig_updated(connectionId, rName, bgpAsn, vlan), - ResourceName: resourceNameHostedVif, + Config: testAccDxHostedPublicVirtualInterfaceConfig_basic(connectionId, rName, amazonAddress, customerAddress, bgpAsn, vlan), + ResourceName: resourceName, ImportState: true, ImportStateVerify: true, }, @@ -65,71 +74,111 @@ func TestAccAwsDxHostedPublicVirtualInterface_basic(t *testing.T) { }) } -func testAccCheckAwsDxHostedPublicVirtualInterfaceDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*AWSClient).dxconn - - for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_dx_hosted_public_virtual_interface" { - continue - } - - input := &directconnect.DescribeVirtualInterfacesInput{ - VirtualInterfaceId: aws.String(rs.Primary.ID), - } - - resp, err := conn.DescribeVirtualInterfaces(input) - if err != nil { - return err - } - for _, v := range resp.VirtualInterfaces { - if *v.VirtualInterfaceId == rs.Primary.ID && !(*v.VirtualInterfaceState == directconnect.VirtualInterfaceStateDeleted) { - return fmt.Errorf("[DESTROY ERROR] Dx Public VIF (%s) not deleted", rs.Primary.ID) - } - } +func TestAccAwsDxHostedPublicVirtualInterface_AccepterTags(t *testing.T) { + key := "DX_CONNECTION_ID" + connectionId := os.Getenv(key) + if connectionId == "" { + t.Skipf("Environment variable %s is not set", key) } - return nil -} -func testAccCheckAwsDxHostedPublicVirtualInterfaceExists(name string) resource.TestCheckFunc { - return func(s *terraform.State) error { - _, ok := s.RootModule().Resources[name] - if !ok { - return fmt.Errorf("Not found: %s", name) - } + var providers []*schema.Provider + var vif directconnect.VirtualInterface + resourceName := "aws_dx_hosted_public_virtual_interface.test" + accepterResourceName := "aws_dx_hosted_public_virtual_interface_accepter.test" + rName := fmt.Sprintf("tf-testacc-public-vif-%s", acctest.RandString(10)) + amazonAddress := "175.45.176.7/28" + customerAddress := "175.45.176.8/28" + bgpAsn := randIntRange(64512, 65534) + vlan := randIntRange(2049, 4094) - return nil - } + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccAlternateAccountPreCheck(t) + }, + ProviderFactories: testAccProviderFactories(&providers), + CheckDestroy: testAccCheckAwsDxHostedPublicVirtualInterfaceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDxHostedPublicVirtualInterfaceConfig_accepterTags(connectionId, rName, amazonAddress, customerAddress, bgpAsn, vlan), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsDxHostedPublicVirtualInterfaceExists(resourceName, &vif), + resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), + resource.TestCheckResourceAttr(resourceName, "amazon_address", amazonAddress), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "directconnect", regexp.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + resource.TestCheckResourceAttrSet(resourceName, "aws_device"), + resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), + resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), + resource.TestCheckResourceAttr(resourceName, "connection_id", connectionId), + resource.TestCheckResourceAttr(resourceName, "customer_address", customerAddress), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "route_filter_prefixes.#", "2"), + resource.TestCheckResourceAttr(resourceName, "route_filter_prefixes.1752038751", "210.52.109.0/24"), + resource.TestCheckResourceAttr(resourceName, "route_filter_prefixes.4290081960", "175.45.176.0/22"), + resource.TestCheckResourceAttr(resourceName, "vlan", strconv.Itoa(vlan)), + // Accepter's attributes: + resource.TestCheckResourceAttrSet(accepterResourceName, "arn"), + resource.TestCheckResourceAttr(accepterResourceName, "tags.%", "3"), + resource.TestCheckResourceAttr(accepterResourceName, "tags.Name", rName), + resource.TestCheckResourceAttr(accepterResourceName, "tags.Key1", "Value1"), + resource.TestCheckResourceAttr(accepterResourceName, "tags.Key2", "Value2a"), + resource.TestCheckResourceAttrPair(accepterResourceName, "virtual_interface_id", resourceName, "id"), + ), + }, + { + Config: testAccDxHostedPublicVirtualInterfaceConfig_accepterTagsUpdated(connectionId, rName, amazonAddress, customerAddress, bgpAsn, vlan), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsDxHostedPublicVirtualInterfaceExists(resourceName, &vif), + resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), + resource.TestCheckResourceAttr(resourceName, "amazon_address", amazonAddress), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "directconnect", regexp.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + resource.TestCheckResourceAttrSet(resourceName, "aws_device"), + resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), + resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), + resource.TestCheckResourceAttr(resourceName, "connection_id", connectionId), + resource.TestCheckResourceAttr(resourceName, "customer_address", customerAddress), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "route_filter_prefixes.#", "2"), + resource.TestCheckResourceAttr(resourceName, "route_filter_prefixes.1752038751", "210.52.109.0/24"), + resource.TestCheckResourceAttr(resourceName, "route_filter_prefixes.4290081960", "175.45.176.0/22"), + resource.TestCheckResourceAttr(resourceName, "vlan", strconv.Itoa(vlan)), + // Accepter's attributes: + resource.TestCheckResourceAttrSet(accepterResourceName, "arn"), + resource.TestCheckResourceAttr(accepterResourceName, "tags.%", "3"), + resource.TestCheckResourceAttr(accepterResourceName, "tags.Name", rName), + resource.TestCheckResourceAttr(accepterResourceName, "tags.Key2", "Value2b"), + resource.TestCheckResourceAttr(accepterResourceName, "tags.Key3", "Value3"), + resource.TestCheckResourceAttrPair(accepterResourceName, "virtual_interface_id", resourceName, "id"), + ), + }, + }, + }) } -func testAccCheckAwsDxHostedPublicVirtualInterfaceAccepterExists(name string) resource.TestCheckFunc { - return func(s *terraform.State) error { - _, ok := s.RootModule().Resources[name] - if !ok { - return fmt.Errorf("Not found: %s", name) - } +func testAccCheckAwsDxHostedPublicVirtualInterfaceDestroy(s *terraform.State) error { + return testAccCheckDxVirtualInterfaceDestroy(s, "aws_dx_hosted_public_virtual_interface") +} - return nil - } +func testAccCheckAwsDxHostedPublicVirtualInterfaceExists(name string, vif *directconnect.VirtualInterface) resource.TestCheckFunc { + return testAccCheckDxVirtualInterfaceExists(name, vif) } -func testAccDxHostedPublicVirtualInterfaceConfig_base(cid, rName string, bgpAsn, vlan int) string { +func testAccDxHostedPublicVirtualInterfaceConfig_base(cid, rName, amzAddr, custAddr string, bgpAsn, vlan int) string { return testAccAlternateAccountProviderConfig() + fmt.Sprintf(` # Creator resource "aws_dx_hosted_public_virtual_interface" "test" { + address_family = "ipv4" + amazon_address = %[3]q + bgp_asn = %[5]d connection_id = %[1]q + customer_address = %[4]q + name = %[2]q owner_account_id = "${data.aws_caller_identity.accepter.account_id}" - - name = %[2]q - vlan = %[4]d - address_family = "ipv4" - bgp_asn = %[3]d - - customer_address = "175.45.176.1/30" - amazon_address = "175.45.176.2/30" + vlan = %[6]d route_filter_prefixes = [ - "210.52.109.0/24", "175.45.176.0/22", + "210.52.109.0/24", ] } @@ -137,29 +186,47 @@ resource "aws_dx_hosted_public_virtual_interface" "test" { data "aws_caller_identity" "accepter" { provider = "aws.alternate" } -`, cid, rName, bgpAsn, vlan) +`, cid, rName, amzAddr, custAddr, bgpAsn, vlan) } -func testAccDxHostedPublicVirtualInterfaceConfig_basic(cid, rName string, bgpAsn, vlan int) string { - return testAccDxHostedPublicVirtualInterfaceConfig_base(cid, rName, bgpAsn, vlan) + fmt.Sprintf(` +func testAccDxHostedPublicVirtualInterfaceConfig_basic(cid, rName, amzAddr, custAddr string, bgpAsn, vlan int) string { + return testAccDxHostedPublicVirtualInterfaceConfig_base(cid, rName, amzAddr, custAddr, bgpAsn, vlan) + fmt.Sprintf(` resource "aws_dx_hosted_public_virtual_interface_accepter" "test" { - provider = "aws.alternate" + provider = "aws.alternate" virtual_interface_id = "${aws_dx_hosted_public_virtual_interface.test.id}" } `) } -func testAccDxHostedPublicVirtualInterfaceConfig_updated(cid, rName string, bgpAsn, vlan int) string { - return testAccDxHostedPublicVirtualInterfaceConfig_base(cid, rName, bgpAsn, vlan) + fmt.Sprintf(` +func testAccDxHostedPublicVirtualInterfaceConfig_accepterTags(cid, rName, amzAddr, custAddr string, bgpAsn, vlan int) string { + return testAccDxHostedPublicVirtualInterfaceConfig_base(cid, rName, amzAddr, custAddr, bgpAsn, vlan) + fmt.Sprintf(` resource "aws_dx_hosted_public_virtual_interface_accepter" "test" { - provider = "aws.alternate" + provider = "aws.alternate" virtual_interface_id = "${aws_dx_hosted_public_virtual_interface.test.id}" tags = { - Environment = "test" + Name = %[1]q + Key1 = "Value1" + Key2 = "Value2a" } } -`) +`, rName) +} + +func testAccDxHostedPublicVirtualInterfaceConfig_accepterTagsUpdated(cid, rName, amzAddr, custAddr string, bgpAsn, vlan int) string { + return testAccDxHostedPublicVirtualInterfaceConfig_base(cid, rName, amzAddr, custAddr, bgpAsn, vlan) + fmt.Sprintf(` +resource "aws_dx_hosted_public_virtual_interface_accepter" "test" { + provider = "aws.alternate" + + virtual_interface_id = "${aws_dx_hosted_public_virtual_interface.test.id}" + + tags = { + Name = %[1]q + Key2 = "Value2b" + Key3 = "Value3" + } +} +`, rName) } diff --git a/aws/resource_aws_dx_private_virtual_interface.go b/aws/resource_aws_dx_private_virtual_interface.go index daf1dcf6522..e357911d51f 100644 --- a/aws/resource_aws_dx_private_virtual_interface.go +++ b/aws/resource_aws_dx_private_virtual_interface.go @@ -23,37 +23,28 @@ func resourceAwsDxPrivateVirtualInterface() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "arn": { - Type: schema.TypeString, - Computed: true, - }, - "connection_id": { + "address_family": { Type: schema.TypeString, Required: true, ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + directconnect.AddressFamilyIpv4, + directconnect.AddressFamilyIpv6, + }, false), }, - "name": { + "amazon_address": { Type: schema.TypeString, - Required: true, + Optional: true, + Computed: true, ForceNew: true, }, - "vpn_gateway_id": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ConflictsWith: []string{"dx_gateway_id"}, - }, - "dx_gateway_id": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - ConflictsWith: []string{"vpn_gateway_id"}, + "arn": { + Type: schema.TypeString, + Computed: true, }, - "vlan": { - Type: schema.TypeInt, - Required: true, - ForceNew: true, - ValidateFunc: validation.IntBetween(1, 4094), + "aws_device": { + Type: schema.TypeString, + Computed: true, }, "bgp_asn": { Type: schema.TypeInt, @@ -66,11 +57,10 @@ func resourceAwsDxPrivateVirtualInterface() *schema.Resource { Computed: true, ForceNew: true, }, - "address_family": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{directconnect.AddressFamilyIpv4, directconnect.AddressFamilyIpv6}, false), + "connection_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, }, "customer_address": { Type: schema.TypeString, @@ -78,11 +68,15 @@ func resourceAwsDxPrivateVirtualInterface() *schema.Resource { Computed: true, ForceNew: true, }, - "amazon_address": { - Type: schema.TypeString, - Optional: true, + "dx_gateway_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"vpn_gateway_id"}, + }, + "jumbo_frame_capable": { + Type: schema.TypeBool, Computed: true, - ForceNew: true, }, "mtu": { Type: schema.TypeInt, @@ -90,14 +84,23 @@ func resourceAwsDxPrivateVirtualInterface() *schema.Resource { Optional: true, ValidateFunc: validation.IntInSlice([]int{1500, 9001}), }, - "jumbo_frame_capable": { - Type: schema.TypeBool, - Computed: true, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, }, "tags": tagsSchema(), - "aws_device": { - Type: schema.TypeString, - Computed: true, + "vlan": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + ValidateFunc: validation.IntBetween(1, 4094), + }, + "vpn_gateway_id": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ConflictsWith: []string{"dx_gateway_id"}, }, }, @@ -122,11 +125,11 @@ func resourceAwsDxPrivateVirtualInterfaceCreate(d *schema.ResourceData, meta int req := &directconnect.CreatePrivateVirtualInterfaceInput{ ConnectionId: aws.String(d.Get("connection_id").(string)), NewPrivateVirtualInterface: &directconnect.NewPrivateVirtualInterface{ - VirtualInterfaceName: aws.String(d.Get("name").(string)), - Vlan: aws.Int64(int64(d.Get("vlan").(int))), - Asn: aws.Int64(int64(d.Get("bgp_asn").(int))), AddressFamily: aws.String(d.Get("address_family").(string)), + Asn: aws.Int64(int64(d.Get("bgp_asn").(int))), Mtu: aws.Int64(int64(d.Get("mtu").(int))), + VirtualInterfaceName: aws.String(d.Get("name").(string)), + Vlan: aws.Int64(int64(d.Get("vlan").(int))), }, } if vgwOk && vgwIdRaw.(string) != "" { @@ -135,37 +138,32 @@ func resourceAwsDxPrivateVirtualInterfaceCreate(d *schema.ResourceData, meta int if dxgwOk && dxgwIdRaw.(string) != "" { req.NewPrivateVirtualInterface.DirectConnectGatewayId = aws.String(dxgwIdRaw.(string)) } + if v, ok := d.GetOk("amazon_address"); ok && v.(string) != "" { + req.NewPrivateVirtualInterface.AmazonAddress = aws.String(v.(string)) + } if v, ok := d.GetOk("bgp_auth_key"); ok { req.NewPrivateVirtualInterface.AuthKey = aws.String(v.(string)) } if v, ok := d.GetOk("customer_address"); ok && v.(string) != "" { req.NewPrivateVirtualInterface.CustomerAddress = aws.String(v.(string)) } - if v, ok := d.GetOk("amazon_address"); ok && v.(string) != "" { - req.NewPrivateVirtualInterface.AmazonAddress = aws.String(v.(string)) + if v, ok := d.GetOk("tags"); ok { + req.NewPrivateVirtualInterface.Tags = tagsFromMapDX(v.(map[string]interface{})) } - log.Printf("[DEBUG] Creating Direct Connect private virtual interface: %#v", req) + log.Printf("[DEBUG] Creating Direct Connect private virtual interface: %s", req) resp, err := conn.CreatePrivateVirtualInterface(req) if err != nil { - return fmt.Errorf("Error creating Direct Connect private virtual interface: %s", err.Error()) + return fmt.Errorf("error creating Direct Connect private virtual interface: %s", err) } d.SetId(aws.StringValue(resp.VirtualInterfaceId)) - arn := arn.ARN{ - Partition: meta.(*AWSClient).partition, - Region: meta.(*AWSClient).region, - Service: "directconnect", - AccountID: meta.(*AWSClient).accountid, - Resource: fmt.Sprintf("dxvif/%s", d.Id()), - }.String() - d.Set("arn", arn) if err := dxPrivateVirtualInterfaceWaitUntilAvailable(conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { return err } - return resourceAwsDxPrivateVirtualInterfaceUpdate(d, meta) + return resourceAwsDxPrivateVirtualInterfaceRead(d, meta) } func resourceAwsDxPrivateVirtualInterfaceRead(d *schema.ResourceData, meta interface{}) error { @@ -176,26 +174,37 @@ func resourceAwsDxPrivateVirtualInterfaceRead(d *schema.ResourceData, meta inter return err } if vif == nil { - log.Printf("[WARN] Direct Connect virtual interface (%s) not found, removing from state", d.Id()) + log.Printf("[WARN] Direct Connect private virtual interface (%s) not found, removing from state", d.Id()) d.SetId("") return nil } - d.Set("connection_id", vif.ConnectionId) - d.Set("name", vif.VirtualInterfaceName) - d.Set("vlan", vif.Vlan) + d.Set("address_family", vif.AddressFamily) + d.Set("amazon_address", vif.AmazonAddress) + arn := arn.ARN{ + Partition: meta.(*AWSClient).partition, + Region: meta.(*AWSClient).region, + Service: "directconnect", + AccountID: meta.(*AWSClient).accountid, + Resource: fmt.Sprintf("dxvif/%s", d.Id()), + }.String() + d.Set("arn", arn) + d.Set("aws_device", vif.AwsDeviceV2) d.Set("bgp_asn", vif.Asn) d.Set("bgp_auth_key", vif.AuthKey) - d.Set("address_family", vif.AddressFamily) + d.Set("connection_id", vif.ConnectionId) d.Set("customer_address", vif.CustomerAddress) - d.Set("amazon_address", vif.AmazonAddress) - d.Set("vpn_gateway_id", vif.VirtualGatewayId) d.Set("dx_gateway_id", vif.DirectConnectGatewayId) - d.Set("mtu", vif.Mtu) d.Set("jumbo_frame_capable", vif.JumboFrameCapable) - d.Set("aws_device", vif.AwsDeviceV2) - err1 := getTagsDX(conn, d, d.Get("arn").(string)) - return err1 + d.Set("mtu", vif.Mtu) + d.Set("name", vif.VirtualInterfaceName) + d.Set("vlan", vif.Vlan) + d.Set("vpn_gateway_id", vif.VirtualGatewayId) + if err := getTagsDX(conn, d, d.Get("arn").(string)); err != nil { + return fmt.Errorf("error getting Direct Connect private virtual interface (%s) tags: %s", d.Id(), err) + } + + return nil } func resourceAwsDxPrivateVirtualInterfaceUpdate(d *schema.ResourceData, meta interface{}) error { @@ -215,14 +224,19 @@ func resourceAwsDxPrivateVirtualInterfaceDelete(d *schema.ResourceData, meta int } func resourceAwsDxPrivateVirtualInterfaceImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - arn := arn.ARN{ - Partition: meta.(*AWSClient).partition, - Region: meta.(*AWSClient).region, - Service: "directconnect", - AccountID: meta.(*AWSClient).accountid, - Resource: fmt.Sprintf("dxvif/%s", d.Id()), - }.String() - d.Set("arn", arn) + conn := meta.(*AWSClient).dxconn + + vif, err := dxVirtualInterfaceRead(d.Id(), conn) + if err != nil { + return nil, err + } + if vif == nil { + return nil, fmt.Errorf("virtual interface (%s) not found", d.Id()) + } + + if vifType := aws.StringValue(vif.VirtualInterfaceType); vifType != "private" { + return nil, fmt.Errorf("virtual interface (%s) has incorrect type: %s", d.Id(), vifType) + } return []*schema.ResourceData{d}, nil } diff --git a/aws/resource_aws_dx_private_virtual_interface_test.go b/aws/resource_aws_dx_private_virtual_interface_test.go index 8298da927ab..bc99a3adc7c 100644 --- a/aws/resource_aws_dx_private_virtual_interface_test.go +++ b/aws/resource_aws_dx_private_virtual_interface_test.go @@ -3,6 +3,8 @@ package aws import ( "fmt" "os" + "regexp" + "strconv" "testing" "github.com/aws/aws-sdk-go/aws" @@ -18,7 +20,11 @@ func TestAccAwsDxPrivateVirtualInterface_basic(t *testing.T) { if connectionId == "" { t.Skipf("Environment variable %s is not set", key) } - vifName := fmt.Sprintf("terraform-testacc-dxvif-%s", acctest.RandString(5)) + + var vif directconnect.VirtualInterface + resourceName := "aws_dx_private_virtual_interface.test" + vpnGatewayResourceName := "aws_vpn_gateway.test" + rName := fmt.Sprintf("tf-testacc-private-vif-%s", acctest.RandString(9)) bgpAsn := randIntRange(64512, 65534) vlan := randIntRange(2049, 4094) @@ -28,25 +34,48 @@ func TestAccAwsDxPrivateVirtualInterface_basic(t *testing.T) { CheckDestroy: testAccCheckAwsDxPrivateVirtualInterfaceDestroy, Steps: []resource.TestStep{ { - Config: testAccDxPrivateVirtualInterfaceConfig_noTags(connectionId, vifName, bgpAsn, vlan), + Config: testAccDxPrivateVirtualInterfaceConfig_basic(connectionId, rName, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( - testAccCheckAwsDxPrivateVirtualInterfaceExists("aws_dx_private_virtual_interface.foo"), - resource.TestCheckResourceAttr("aws_dx_private_virtual_interface.foo", "name", vifName), - resource.TestCheckResourceAttr("aws_dx_private_virtual_interface.foo", "tags.%", "0"), + testAccCheckAwsDxPrivateVirtualInterfaceExists(resourceName, &vif), + resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), + resource.TestCheckResourceAttrSet(resourceName, "amazon_address"), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "directconnect", regexp.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + resource.TestCheckResourceAttrSet(resourceName, "aws_device"), + resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), + resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), + resource.TestCheckResourceAttr(resourceName, "connection_id", connectionId), + resource.TestCheckResourceAttrSet(resourceName, "customer_address"), + resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", "true"), + resource.TestCheckResourceAttr(resourceName, "mtu", "1500"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "vlan", strconv.Itoa(vlan)), + resource.TestCheckResourceAttrPair(resourceName, "vpn_gateway_id", vpnGatewayResourceName, "id"), ), }, { - Config: testAccDxPrivateVirtualInterfaceConfig_tags(connectionId, vifName, bgpAsn, vlan), + Config: testAccDxPrivateVirtualInterfaceConfig_updated(connectionId, rName, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( - testAccCheckAwsDxPrivateVirtualInterfaceExists("aws_dx_private_virtual_interface.foo"), - resource.TestCheckResourceAttr("aws_dx_private_virtual_interface.foo", "name", vifName), - resource.TestCheckResourceAttr("aws_dx_private_virtual_interface.foo", "tags.%", "1"), - resource.TestCheckResourceAttr("aws_dx_private_virtual_interface.foo", "tags.Environment", "test"), + testAccCheckAwsDxPrivateVirtualInterfaceExists(resourceName, &vif), + resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), + resource.TestCheckResourceAttrSet(resourceName, "amazon_address"), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "directconnect", regexp.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + resource.TestCheckResourceAttrSet(resourceName, "aws_device"), + resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), + resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), + resource.TestCheckResourceAttr(resourceName, "connection_id", connectionId), + resource.TestCheckResourceAttrSet(resourceName, "customer_address"), + resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", "true"), + resource.TestCheckResourceAttr(resourceName, "mtu", "9001"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "vlan", strconv.Itoa(vlan)), + resource.TestCheckResourceAttrPair(resourceName, "vpn_gateway_id", vpnGatewayResourceName, "id"), ), }, // Test import. { - ResourceName: "aws_dx_private_virtual_interface.foo", + ResourceName: resourceName, ImportState: true, ImportStateVerify: true, }, @@ -54,14 +83,17 @@ func TestAccAwsDxPrivateVirtualInterface_basic(t *testing.T) { }) } -func TestAccAwsDxPrivateVirtualInterface_dxGateway(t *testing.T) { +func TestAccAwsDxPrivateVirtualInterface_Tags(t *testing.T) { key := "DX_CONNECTION_ID" connectionId := os.Getenv(key) if connectionId == "" { t.Skipf("Environment variable %s is not set", key) } - vifName := fmt.Sprintf("terraform-testacc-dxvif-%s", acctest.RandString(5)) - amzAsn := randIntRange(64512, 65534) + + var vif directconnect.VirtualInterface + resourceName := "aws_dx_private_virtual_interface.test" + vpnGatewayResourceName := "aws_vpn_gateway.test" + rName := fmt.Sprintf("tf-testacc-private-vif-%s", acctest.RandString(9)) bgpAsn := randIntRange(64512, 65534) vlan := randIntRange(2049, 4094) @@ -71,23 +103,73 @@ func TestAccAwsDxPrivateVirtualInterface_dxGateway(t *testing.T) { CheckDestroy: testAccCheckAwsDxPrivateVirtualInterfaceDestroy, Steps: []resource.TestStep{ { - Config: testAccDxPrivateVirtualInterfaceConfig_dxGateway(connectionId, vifName, amzAsn, bgpAsn, vlan), + Config: testAccDxPrivateVirtualInterfaceConfig_tags(connectionId, rName, bgpAsn, vlan), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsDxPrivateVirtualInterfaceExists(resourceName, &vif), + resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), + resource.TestCheckResourceAttrSet(resourceName, "amazon_address"), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "directconnect", regexp.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + resource.TestCheckResourceAttrSet(resourceName, "aws_device"), + resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), + resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), + resource.TestCheckResourceAttr(resourceName, "connection_id", connectionId), + resource.TestCheckResourceAttrSet(resourceName, "customer_address"), + resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", "true"), + resource.TestCheckResourceAttr(resourceName, "mtu", "1500"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "3"), + resource.TestCheckResourceAttr(resourceName, "tags.Name", rName), + resource.TestCheckResourceAttr(resourceName, "tags.Key1", "Value1"), + resource.TestCheckResourceAttr(resourceName, "tags.Key2", "Value2a"), + resource.TestCheckResourceAttr(resourceName, "vlan", strconv.Itoa(vlan)), + resource.TestCheckResourceAttrPair(resourceName, "vpn_gateway_id", vpnGatewayResourceName, "id"), + ), + }, + { + Config: testAccDxPrivateVirtualInterfaceConfig_tagsUpdated(connectionId, rName, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( - testAccCheckAwsDxPrivateVirtualInterfaceExists("aws_dx_private_virtual_interface.foo"), - resource.TestCheckResourceAttr("aws_dx_private_virtual_interface.foo", "name", vifName), + testAccCheckAwsDxPrivateVirtualInterfaceExists(resourceName, &vif), + resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), + resource.TestCheckResourceAttrSet(resourceName, "amazon_address"), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "directconnect", regexp.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + resource.TestCheckResourceAttrSet(resourceName, "aws_device"), + resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), + resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), + resource.TestCheckResourceAttr(resourceName, "connection_id", connectionId), + resource.TestCheckResourceAttrSet(resourceName, "customer_address"), + resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", "true"), + resource.TestCheckResourceAttr(resourceName, "mtu", "1500"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "3"), + resource.TestCheckResourceAttr(resourceName, "tags.Name", rName), + resource.TestCheckResourceAttr(resourceName, "tags.Key2", "Value2b"), + resource.TestCheckResourceAttr(resourceName, "tags.Key3", "Value3"), + resource.TestCheckResourceAttr(resourceName, "vlan", strconv.Itoa(vlan)), + resource.TestCheckResourceAttrPair(resourceName, "vpn_gateway_id", vpnGatewayResourceName, "id"), ), }, + // Test import. + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, }, }) } -func TestAccAwsDxPrivateVirtualInterface_mtuUpdate(t *testing.T) { +func TestAccAwsDxPrivateVirtualInterface_DxGateway(t *testing.T) { key := "DX_CONNECTION_ID" connectionId := os.Getenv(key) if connectionId == "" { t.Skipf("Environment variable %s is not set", key) } - vifName := fmt.Sprintf("terraform-testacc-dxvif-%s", acctest.RandString(5)) + + var vif directconnect.VirtualInterface + resourceName := "aws_dx_private_virtual_interface.test" + dxGatewayResourceName := "aws_dx_gateway.test" + rName := fmt.Sprintf("tf-testacc-private-vif-%s", acctest.RandString(9)) + amzAsn := randIntRange(64512, 65534) bgpAsn := randIntRange(64512, 65534) vlan := randIntRange(2049, 4094) @@ -97,150 +179,131 @@ func TestAccAwsDxPrivateVirtualInterface_mtuUpdate(t *testing.T) { CheckDestroy: testAccCheckAwsDxPrivateVirtualInterfaceDestroy, Steps: []resource.TestStep{ { - Config: testAccDxPrivateVirtualInterfaceConfig_noTags(connectionId, vifName, bgpAsn, vlan), - Check: resource.ComposeTestCheckFunc( - testAccCheckAwsDxPrivateVirtualInterfaceExists("aws_dx_private_virtual_interface.foo"), - resource.TestCheckResourceAttr("aws_dx_private_virtual_interface.foo", "name", vifName), - resource.TestCheckResourceAttr("aws_dx_private_virtual_interface.foo", "mtu", "1500"), - resource.TestCheckResourceAttr("aws_dx_private_virtual_interface.foo", "jumbo_frame_capable", "true"), - ), - }, - { - Config: testAccDxPrivateVirtualInterfaceConfig_jumboFrames(connectionId, vifName, bgpAsn, vlan), + Config: testAccDxPrivateVirtualInterfaceConfig_dxGateway(connectionId, rName, amzAsn, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( - testAccCheckAwsDxPrivateVirtualInterfaceExists("aws_dx_private_virtual_interface.foo"), - resource.TestCheckResourceAttr("aws_dx_private_virtual_interface.foo", "name", vifName), - resource.TestCheckResourceAttr("aws_dx_private_virtual_interface.foo", "mtu", "9001"), + testAccCheckAwsDxPrivateVirtualInterfaceExists(resourceName, &vif), + resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), + resource.TestCheckResourceAttrSet(resourceName, "amazon_address"), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "directconnect", regexp.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + resource.TestCheckResourceAttrSet(resourceName, "aws_device"), + resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), + resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), + resource.TestCheckResourceAttr(resourceName, "connection_id", connectionId), + resource.TestCheckResourceAttrSet(resourceName, "customer_address"), + resource.TestCheckResourceAttrPair(resourceName, "dx_gateway_id", dxGatewayResourceName, "id"), + resource.TestCheckResourceAttr(resourceName, "jumbo_frame_capable", "true"), + resource.TestCheckResourceAttr(resourceName, "mtu", "1500"), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "vlan", strconv.Itoa(vlan)), ), }, + // Test import. { - Config: testAccDxPrivateVirtualInterfaceConfig_noTags(connectionId, vifName, bgpAsn, vlan), - Check: resource.ComposeTestCheckFunc( - testAccCheckAwsDxPrivateVirtualInterfaceExists("aws_dx_private_virtual_interface.foo"), - resource.TestCheckResourceAttr("aws_dx_private_virtual_interface.foo", "name", vifName), - resource.TestCheckResourceAttr("aws_dx_private_virtual_interface.foo", "mtu", "1500"), - ), + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, }, }, }) } func testAccCheckAwsDxPrivateVirtualInterfaceDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*AWSClient).dxconn - - for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_dx_private_virtual_interface" { - continue - } - - input := &directconnect.DescribeVirtualInterfacesInput{ - VirtualInterfaceId: aws.String(rs.Primary.ID), - } - - resp, err := conn.DescribeVirtualInterfaces(input) - if err != nil { - return err - } - for _, v := range resp.VirtualInterfaces { - if *v.VirtualInterfaceId == rs.Primary.ID && !(*v.VirtualInterfaceState == directconnect.VirtualInterfaceStateDeleted) { - return fmt.Errorf("[DESTROY ERROR] Dx Private VIF (%s) not deleted", rs.Primary.ID) - } - } - } - return nil + return testAccCheckDxVirtualInterfaceDestroy(s, "aws_dx_private_virtual_interface") } -func testAccCheckAwsDxPrivateVirtualInterfaceExists(name string) resource.TestCheckFunc { - return func(s *terraform.State) error { - _, ok := s.RootModule().Resources[name] - if !ok { - return fmt.Errorf("Not found: %s", name) - } - - return nil - } +func testAccCheckAwsDxPrivateVirtualInterfaceExists(name string, vif *directconnect.VirtualInterface) resource.TestCheckFunc { + return testAccCheckDxVirtualInterfaceExists(name, vif) } -func testAccDxPrivateVirtualInterfaceConfig_noTags(cid, n string, bgpAsn, vlan int) string { +func testAccDxPrivateVirtualInterfaceConfig_vpnGateway(rName string) string { return fmt.Sprintf(` -resource "aws_vpn_gateway" "foo" { +resource "aws_vpn_gateway" "test" { tags = { - Name = "%s" + Name = %[1]q } +}`, rName) } -resource "aws_dx_private_virtual_interface" "foo" { - connection_id = "%s" - - vpn_gateway_id = "${aws_vpn_gateway.foo.id}" - name = "%s" - vlan = %d +func testAccDxPrivateVirtualInterfaceConfig_basic(cid, rName string, bgpAsn, vlan int) string { + return testAccDxPrivateVirtualInterfaceConfig_vpnGateway(rName) + fmt.Sprintf(` +resource "aws_dx_private_virtual_interface" "test" { address_family = "ipv4" - bgp_asn = %d + bgp_asn = %[3]d + connection_id = %[1]q + name = %[2]q + vlan = %[4]d + vpn_gateway_id = "${aws_vpn_gateway.test.id}" } -`, n, cid, n, vlan, bgpAsn) +`, cid, rName, bgpAsn, vlan) } -func testAccDxPrivateVirtualInterfaceConfig_tags(cid, n string, bgpAsn, vlan int) string { - return fmt.Sprintf(` -resource "aws_vpn_gateway" "foo" { - tags = { - Name = "%s" - } +func testAccDxPrivateVirtualInterfaceConfig_updated(cid, rName string, bgpAsn, vlan int) string { + return testAccDxPrivateVirtualInterfaceConfig_vpnGateway(rName) + fmt.Sprintf(` +resource "aws_dx_private_virtual_interface" "test" { + address_family = "ipv4" + bgp_asn = %[3]d + connection_id = %[1]q + mtu = 9001 + name = %[2]q + vlan = %[4]d + vpn_gateway_id = "${aws_vpn_gateway.test.id}" +} +`, cid, rName, bgpAsn, vlan) } -resource "aws_dx_private_virtual_interface" "foo" { - connection_id = "%s" - - vpn_gateway_id = "${aws_vpn_gateway.foo.id}" - name = "%s" - vlan = %d +func testAccDxPrivateVirtualInterfaceConfig_tags(cid, rName string, bgpAsn, vlan int) string { + return testAccDxPrivateVirtualInterfaceConfig_vpnGateway(rName) + fmt.Sprintf(` +resource "aws_dx_private_virtual_interface" "test" { address_family = "ipv4" - bgp_asn = %d + bgp_asn = %[3]d + connection_id = %[1]q + name = %[2]q + vlan = %[4]d + vpn_gateway_id = "${aws_vpn_gateway.test.id}" tags = { - Environment = "test" + Name = %[2]q + Key1 = "Value1" + Key2 = "Value2a" } } -`, n, cid, n, vlan, bgpAsn) +`, cid, rName, bgpAsn, vlan) } -func testAccDxPrivateVirtualInterfaceConfig_dxGateway(cid, n string, amzAsn, bgpAsn, vlan int) string { - return fmt.Sprintf(` -resource "aws_dx_gateway" "foo" { - name = "%s" - amazon_side_asn = %d -} - -resource "aws_dx_private_virtual_interface" "foo" { - connection_id = "%s" - - dx_gateway_id = "${aws_dx_gateway.foo.id}" - name = "%s" - vlan = %d +func testAccDxPrivateVirtualInterfaceConfig_tagsUpdated(cid, rName string, bgpAsn, vlan int) string { + return testAccDxPrivateVirtualInterfaceConfig_vpnGateway(rName) + fmt.Sprintf(` +resource "aws_dx_private_virtual_interface" "test" { address_family = "ipv4" - bgp_asn = %d -} -`, n, amzAsn, cid, n, vlan, bgpAsn) -} + bgp_asn = %[3]d + connection_id = %[1]q + name = %[2]q + vlan = %[4]d + vpn_gateway_id = "${aws_vpn_gateway.test.id}" -func testAccDxPrivateVirtualInterfaceConfig_jumboFrames(cid, n string, bgpAsn, vlan int) string { - return fmt.Sprintf(` -resource "aws_vpn_gateway" "foo" { tags = { - Name = "%s" + Name = %[2]q + Key2 = "Value2b" + Key3 = "Value3" } } +`, cid, rName, bgpAsn, vlan) +} -resource "aws_dx_private_virtual_interface" "foo" { - connection_id = "%s" +func testAccDxPrivateVirtualInterfaceConfig_dxGateway(cid, rName string, amzAsn, bgpAsn, vlan int) string { + return fmt.Sprintf(` +resource "aws_dx_gateway" "test" { + amazon_side_asn = %[3]d + name = %[2]q +} - vpn_gateway_id = "${aws_vpn_gateway.foo.id}" - name = "%s" - vlan = %d +resource "aws_dx_private_virtual_interface" "test" { address_family = "ipv4" - bgp_asn = %d - mtu = 9001 + bgp_asn = %[4]d + dx_gateway_id = "${aws_dx_gateway.test.id}" + connection_id = %[1]q + name = %[2]q + vlan = %[5]d } -`, n, cid, n, vlan, bgpAsn) +`, cid, rName, amzAsn, bgpAsn, vlan) } diff --git a/aws/resource_aws_dx_public_virtual_interface.go b/aws/resource_aws_dx_public_virtual_interface.go index b65c5113c36..9eaec488823 100644 --- a/aws/resource_aws_dx_public_virtual_interface.go +++ b/aws/resource_aws_dx_public_virtual_interface.go @@ -24,25 +24,28 @@ func resourceAwsDxPublicVirtualInterface() *schema.Resource { CustomizeDiff: resourceAwsDxPublicVirtualInterfaceCustomizeDiff, Schema: map[string]*schema.Schema{ - "arn": { - Type: schema.TypeString, - Computed: true, - }, - "connection_id": { + "address_family": { Type: schema.TypeString, Required: true, ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + directconnect.AddressFamilyIpv4, + directconnect.AddressFamilyIpv6, + }, false), }, - "name": { + "amazon_address": { Type: schema.TypeString, - Required: true, + Optional: true, + Computed: true, ForceNew: true, }, - "vlan": { - Type: schema.TypeInt, - Required: true, - ForceNew: true, - ValidateFunc: validation.IntBetween(1, 4094), + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "aws_device": { + Type: schema.TypeString, + Computed: true, }, "bgp_asn": { Type: schema.TypeInt, @@ -55,11 +58,10 @@ func resourceAwsDxPublicVirtualInterface() *schema.Resource { Computed: true, ForceNew: true, }, - "address_family": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{directconnect.AddressFamilyIpv4, directconnect.AddressFamilyIpv6}, false), + "connection_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, }, "customer_address": { Type: schema.TypeString, @@ -67,10 +69,9 @@ func resourceAwsDxPublicVirtualInterface() *schema.Resource { Computed: true, ForceNew: true, }, - "amazon_address": { + "name": { Type: schema.TypeString, - Optional: true, - Computed: true, + Required: true, ForceNew: true, }, "route_filter_prefixes": { @@ -81,9 +82,11 @@ func resourceAwsDxPublicVirtualInterface() *schema.Resource { MinItems: 1, }, "tags": tagsSchema(), - "aws_device": { - Type: schema.TypeString, - Computed: true, + "vlan": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + ValidateFunc: validation.IntBetween(1, 4094), }, }, @@ -100,46 +103,41 @@ func resourceAwsDxPublicVirtualInterfaceCreate(d *schema.ResourceData, meta inte req := &directconnect.CreatePublicVirtualInterfaceInput{ ConnectionId: aws.String(d.Get("connection_id").(string)), NewPublicVirtualInterface: &directconnect.NewPublicVirtualInterface{ + AddressFamily: aws.String(d.Get("address_family").(string)), + Asn: aws.Int64(int64(d.Get("bgp_asn").(int))), VirtualInterfaceName: aws.String(d.Get("name").(string)), Vlan: aws.Int64(int64(d.Get("vlan").(int))), - Asn: aws.Int64(int64(d.Get("bgp_asn").(int))), - AddressFamily: aws.String(d.Get("address_family").(string)), }, } + if v, ok := d.GetOk("amazon_address"); ok && v.(string) != "" { + req.NewPublicVirtualInterface.AmazonAddress = aws.String(v.(string)) + } if v, ok := d.GetOk("bgp_auth_key"); ok && v.(string) != "" { req.NewPublicVirtualInterface.AuthKey = aws.String(v.(string)) } if v, ok := d.GetOk("customer_address"); ok && v.(string) != "" { req.NewPublicVirtualInterface.CustomerAddress = aws.String(v.(string)) } - if v, ok := d.GetOk("amazon_address"); ok && v.(string) != "" { - req.NewPublicVirtualInterface.AmazonAddress = aws.String(v.(string)) - } if v, ok := d.GetOk("route_filter_prefixes"); ok { req.NewPublicVirtualInterface.RouteFilterPrefixes = expandDxRouteFilterPrefixes(v.(*schema.Set)) } + if v, ok := d.GetOk("tags"); ok { + req.NewPublicVirtualInterface.Tags = tagsFromMapDX(v.(map[string]interface{})) + } - log.Printf("[DEBUG] Creating Direct Connect public virtual interface: %#v", req) + log.Printf("[DEBUG] Creating Direct Connect public virtual interface: %s", req) resp, err := conn.CreatePublicVirtualInterface(req) if err != nil { - return fmt.Errorf("Error creating Direct Connect public virtual interface: %s", err) + return fmt.Errorf("error creating Direct Connect public virtual interface: %s", err) } d.SetId(aws.StringValue(resp.VirtualInterfaceId)) - arn := arn.ARN{ - Partition: meta.(*AWSClient).partition, - Region: meta.(*AWSClient).region, - Service: "directconnect", - AccountID: meta.(*AWSClient).accountid, - Resource: fmt.Sprintf("dxvif/%s", d.Id()), - }.String() - d.Set("arn", arn) if err := dxPublicVirtualInterfaceWaitUntilAvailable(conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { return err } - return resourceAwsDxPublicVirtualInterfaceUpdate(d, meta) + return resourceAwsDxPublicVirtualInterfaceRead(d, meta) } func resourceAwsDxPublicVirtualInterfaceRead(d *schema.ResourceData, meta interface{}) error { @@ -155,18 +153,31 @@ func resourceAwsDxPublicVirtualInterfaceRead(d *schema.ResourceData, meta interf return nil } - d.Set("connection_id", vif.ConnectionId) - d.Set("name", vif.VirtualInterfaceName) - d.Set("vlan", vif.Vlan) - d.Set("bgp_asn", vif.Asn) - d.Set("bgp_auth_key", vif.AuthKey) d.Set("address_family", vif.AddressFamily) - d.Set("customer_address", vif.CustomerAddress) d.Set("amazon_address", vif.AmazonAddress) - d.Set("route_filter_prefixes", flattenDxRouteFilterPrefixes(vif.RouteFilterPrefixes)) + arn := arn.ARN{ + Partition: meta.(*AWSClient).partition, + Region: meta.(*AWSClient).region, + Service: "directconnect", + AccountID: meta.(*AWSClient).accountid, + Resource: fmt.Sprintf("dxvif/%s", d.Id()), + }.String() + d.Set("arn", arn) d.Set("aws_device", vif.AwsDeviceV2) - err1 := getTagsDX(conn, d, d.Get("arn").(string)) - return err1 + d.Set("bgp_asn", vif.Asn) + d.Set("bgp_auth_key", vif.AuthKey) + d.Set("customer_address", vif.CustomerAddress) + d.Set("connection_id", vif.ConnectionId) + d.Set("name", vif.VirtualInterfaceName) + if err := d.Set("route_filter_prefixes", flattenDxRouteFilterPrefixes(vif.RouteFilterPrefixes)); err != nil { + return fmt.Errorf("error setting route_filter_prefixes: %s", err) + } + d.Set("vlan", vif.Vlan) + if err := getTagsDX(conn, d, d.Get("arn").(string)); err != nil { + return fmt.Errorf("error getting Direct Connect public virtual interface (%s) tags: %s", d.Id(), err) + } + + return nil } func resourceAwsDxPublicVirtualInterfaceUpdate(d *schema.ResourceData, meta interface{}) error { @@ -182,14 +193,19 @@ func resourceAwsDxPublicVirtualInterfaceDelete(d *schema.ResourceData, meta inte } func resourceAwsDxPublicVirtualInterfaceImport(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - arn := arn.ARN{ - Partition: meta.(*AWSClient).partition, - Region: meta.(*AWSClient).region, - Service: "directconnect", - AccountID: meta.(*AWSClient).accountid, - Resource: fmt.Sprintf("dxvif/%s", d.Id()), - }.String() - d.Set("arn", arn) + conn := meta.(*AWSClient).dxconn + + vif, err := dxVirtualInterfaceRead(d.Id(), conn) + if err != nil { + return nil, err + } + if vif == nil { + return nil, fmt.Errorf("virtual interface (%s) not found", d.Id()) + } + + if vifType := aws.StringValue(vif.VirtualInterfaceType); vifType != "public" { + return nil, fmt.Errorf("virtual interface (%s) has incorrect type: %s", d.Id(), vifType) + } return []*schema.ResourceData{d}, nil } diff --git a/aws/resource_aws_dx_public_virtual_interface_test.go b/aws/resource_aws_dx_public_virtual_interface_test.go index 48096a954c1..fc530f908f3 100644 --- a/aws/resource_aws_dx_public_virtual_interface_test.go +++ b/aws/resource_aws_dx_public_virtual_interface_test.go @@ -3,6 +3,8 @@ package aws import ( "fmt" "os" + "regexp" + "strconv" "testing" "github.com/aws/aws-sdk-go/aws" @@ -18,7 +20,16 @@ func TestAccAwsDxPublicVirtualInterface_basic(t *testing.T) { if connectionId == "" { t.Skipf("Environment variable %s is not set", key) } - vifName := fmt.Sprintf("terraform-testacc-dxvif-%s", acctest.RandString(5)) + + var vif directconnect.VirtualInterface + resourceName := "aws_dx_public_virtual_interface.test" + rName := fmt.Sprintf("tf-testacc-public-vif-%s", acctest.RandString(10)) + // DirectConnectClientException: Amazon Address is not allowed to contain a private IP + // DirectConnectClientException: Amazon Address and Customer Address must be in the same CIDR + // DirectConnectClientException: Amazon Address is address 0 on its subnet. + // DirectConnectClientException: Amazon Address is the broadcast address on its subnet. + amazonAddress := "175.45.176.1/28" + customerAddress := "175.45.176.2/28" bgpAsn := randIntRange(64512, 65534) vlan := randIntRange(2049, 4094) @@ -28,25 +39,104 @@ func TestAccAwsDxPublicVirtualInterface_basic(t *testing.T) { CheckDestroy: testAccCheckAwsDxPublicVirtualInterfaceDestroy, Steps: []resource.TestStep{ { - Config: testAccDxPublicVirtualInterfaceConfig_noTags(connectionId, vifName, bgpAsn, vlan), + Config: testAccDxPublicVirtualInterfaceConfig_basic(connectionId, rName, amazonAddress, customerAddress, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( - testAccCheckAwsDxPublicVirtualInterfaceExists("aws_dx_public_virtual_interface.foo"), - resource.TestCheckResourceAttr("aws_dx_public_virtual_interface.foo", "name", vifName), - resource.TestCheckResourceAttr("aws_dx_public_virtual_interface.foo", "tags.%", "0"), + testAccCheckAwsDxPublicVirtualInterfaceExists(resourceName, &vif), + resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), + resource.TestCheckResourceAttr(resourceName, "amazon_address", amazonAddress), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "directconnect", regexp.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + resource.TestCheckResourceAttrSet(resourceName, "aws_device"), + resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), + resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), + resource.TestCheckResourceAttr(resourceName, "connection_id", connectionId), + resource.TestCheckResourceAttr(resourceName, "customer_address", customerAddress), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "route_filter_prefixes.#", "2"), + resource.TestCheckResourceAttr(resourceName, "route_filter_prefixes.1752038751", "210.52.109.0/24"), + resource.TestCheckResourceAttr(resourceName, "route_filter_prefixes.4290081960", "175.45.176.0/22"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "vlan", strconv.Itoa(vlan)), ), }, + // Test import. + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAwsDxPublicVirtualInterface_Tags(t *testing.T) { + key := "DX_CONNECTION_ID" + connectionId := os.Getenv(key) + if connectionId == "" { + t.Skipf("Environment variable %s is not set", key) + } + + var vif directconnect.VirtualInterface + resourceName := "aws_dx_public_virtual_interface.test" + rName := fmt.Sprintf("tf-testacc-public-vif-%s", acctest.RandString(10)) + amazonAddress := "175.45.176.3/28" + customerAddress := "175.45.176.4/28" + bgpAsn := randIntRange(64512, 65534) + vlan := randIntRange(2049, 4094) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckAwsDxPublicVirtualInterfaceDestroy, + Steps: []resource.TestStep{ { - Config: testAccDxPublicVirtualInterfaceConfig_tags(connectionId, vifName, bgpAsn, vlan), + Config: testAccDxPublicVirtualInterfaceConfig_tags(connectionId, rName, amazonAddress, customerAddress, bgpAsn, vlan), Check: resource.ComposeTestCheckFunc( - testAccCheckAwsDxPublicVirtualInterfaceExists("aws_dx_public_virtual_interface.foo"), - resource.TestCheckResourceAttr("aws_dx_public_virtual_interface.foo", "name", vifName), - resource.TestCheckResourceAttr("aws_dx_public_virtual_interface.foo", "tags.%", "1"), - resource.TestCheckResourceAttr("aws_dx_public_virtual_interface.foo", "tags.Environment", "test"), + testAccCheckAwsDxPublicVirtualInterfaceExists(resourceName, &vif), + resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), + resource.TestCheckResourceAttr(resourceName, "amazon_address", amazonAddress), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "directconnect", regexp.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + resource.TestCheckResourceAttrSet(resourceName, "aws_device"), + resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), + resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), + resource.TestCheckResourceAttr(resourceName, "connection_id", connectionId), + resource.TestCheckResourceAttr(resourceName, "customer_address", customerAddress), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "route_filter_prefixes.#", "2"), + resource.TestCheckResourceAttr(resourceName, "route_filter_prefixes.1752038751", "210.52.109.0/24"), + resource.TestCheckResourceAttr(resourceName, "route_filter_prefixes.4290081960", "175.45.176.0/22"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "3"), + resource.TestCheckResourceAttr(resourceName, "tags.Name", rName), + resource.TestCheckResourceAttr(resourceName, "tags.Key1", "Value1"), + resource.TestCheckResourceAttr(resourceName, "tags.Key2", "Value2a"), + resource.TestCheckResourceAttr(resourceName, "vlan", strconv.Itoa(vlan)), + ), + }, + { + Config: testAccDxPublicVirtualInterfaceConfig_tagsUpdated(connectionId, rName, amazonAddress, customerAddress, bgpAsn, vlan), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsDxPublicVirtualInterfaceExists(resourceName, &vif), + resource.TestCheckResourceAttr(resourceName, "address_family", "ipv4"), + resource.TestCheckResourceAttr(resourceName, "amazon_address", amazonAddress), + testAccMatchResourceAttrRegionalARN(resourceName, "arn", "directconnect", regexp.MustCompile(fmt.Sprintf("dxvif/%s", aws.StringValue(vif.VirtualInterfaceId)))), + resource.TestCheckResourceAttrSet(resourceName, "aws_device"), + resource.TestCheckResourceAttr(resourceName, "bgp_asn", strconv.Itoa(bgpAsn)), + resource.TestCheckResourceAttrSet(resourceName, "bgp_auth_key"), + resource.TestCheckResourceAttr(resourceName, "connection_id", connectionId), + resource.TestCheckResourceAttr(resourceName, "customer_address", customerAddress), + resource.TestCheckResourceAttr(resourceName, "name", rName), + resource.TestCheckResourceAttr(resourceName, "route_filter_prefixes.#", "2"), + resource.TestCheckResourceAttr(resourceName, "route_filter_prefixes.1752038751", "210.52.109.0/24"), + resource.TestCheckResourceAttr(resourceName, "route_filter_prefixes.4290081960", "175.45.176.0/22"), + resource.TestCheckResourceAttr(resourceName, "tags.%", "3"), + resource.TestCheckResourceAttr(resourceName, "tags.Name", rName), + resource.TestCheckResourceAttr(resourceName, "tags.Key2", "Value2b"), + resource.TestCheckResourceAttr(resourceName, "tags.Key3", "Value3"), + resource.TestCheckResourceAttr(resourceName, "vlan", strconv.Itoa(vlan)), ), }, // Test import. { - ResourceName: "aws_dx_public_virtual_interface.foo", + ResourceName: resourceName, ImportState: true, ImportStateVerify: true, }, @@ -55,83 +145,78 @@ func TestAccAwsDxPublicVirtualInterface_basic(t *testing.T) { } func testAccCheckAwsDxPublicVirtualInterfaceDestroy(s *terraform.State) error { - conn := testAccProvider.Meta().(*AWSClient).dxconn - - for _, rs := range s.RootModule().Resources { - if rs.Type != "aws_dx_public_virtual_interface" { - continue - } - - input := &directconnect.DescribeVirtualInterfacesInput{ - VirtualInterfaceId: aws.String(rs.Primary.ID), - } - - resp, err := conn.DescribeVirtualInterfaces(input) - if err != nil { - return err - } - for _, v := range resp.VirtualInterfaces { - if *v.VirtualInterfaceId == rs.Primary.ID && !(*v.VirtualInterfaceState == directconnect.VirtualInterfaceStateDeleted) { - return fmt.Errorf("[DESTROY ERROR] Dx Public VIF (%s) not deleted", rs.Primary.ID) - } - } - } - return nil + return testAccCheckDxVirtualInterfaceDestroy(s, "aws_dx_public_virtual_interface") } -func testAccCheckAwsDxPublicVirtualInterfaceExists(name string) resource.TestCheckFunc { - return func(s *terraform.State) error { - _, ok := s.RootModule().Resources[name] - if !ok { - return fmt.Errorf("Not found: %s", name) - } - - return nil - } +func testAccCheckAwsDxPublicVirtualInterfaceExists(name string, vif *directconnect.VirtualInterface) resource.TestCheckFunc { + return testAccCheckDxVirtualInterfaceExists(name, vif) } -func testAccDxPublicVirtualInterfaceConfig_noTags(cid, n string, bgpAsn, vlan int) string { +func testAccDxPublicVirtualInterfaceConfig_basic(cid, rName, amzAddr, custAddr string, bgpAsn, vlan int) string { return fmt.Sprintf(` -resource "aws_dx_public_virtual_interface" "foo" { - connection_id = "%s" - - name = "%s" - vlan = %d - address_family = "ipv4" - bgp_asn = %d - - customer_address = "175.45.176.1/30" - amazon_address = "175.45.176.2/30" +resource "aws_dx_public_virtual_interface" "test" { + address_family = "ipv4" + amazon_address = %[3]q + bgp_asn = %[5]d + connection_id = %[1]q + customer_address = %[4]q + name = %[2]q + vlan = %[6]d route_filter_prefixes = [ - "210.52.109.0/24", "175.45.176.0/22", + "210.52.109.0/24", ] } -`, cid, n, vlan, bgpAsn) +`, cid, rName, amzAddr, custAddr, bgpAsn, vlan) } -func testAccDxPublicVirtualInterfaceConfig_tags(cid, n string, bgpAsn, vlan int) string { +func testAccDxPublicVirtualInterfaceConfig_tags(cid, rName, amzAddr, custAddr string, bgpAsn, vlan int) string { return fmt.Sprintf(` -resource "aws_dx_public_virtual_interface" "foo" { - connection_id = "%s" +resource "aws_dx_public_virtual_interface" "test" { + address_family = "ipv4" + amazon_address = %[3]q + bgp_asn = %[5]d + connection_id = %[1]q + customer_address = %[4]q + name = %[2]q + vlan = %[6]d - name = "%s" - vlan = %d - address_family = "ipv4" - bgp_asn = %d + route_filter_prefixes = [ + "175.45.176.0/22", + "210.52.109.0/24", + ] - customer_address = "175.45.176.1/30" - amazon_address = "175.45.176.2/30" + tags = { + Name = %[2]q + Key1 = "Value1" + Key2 = "Value2a" + } +} +`, cid, rName, amzAddr, custAddr, bgpAsn, vlan) +} + +func testAccDxPublicVirtualInterfaceConfig_tagsUpdated(cid, rName, amzAddr, custAddr string, bgpAsn, vlan int) string { + return fmt.Sprintf(` +resource "aws_dx_public_virtual_interface" "test" { + address_family = "ipv4" + amazon_address = %[3]q + bgp_asn = %[5]d + connection_id = %[1]q + customer_address = %[4]q + name = %[2]q + vlan = %[6]d route_filter_prefixes = [ - "210.52.109.0/24", "175.45.176.0/22", + "210.52.109.0/24", ] tags = { - Environment = "test" + Name = %[2]q + Key2 = "Value2b" + Key3 = "Value3" } } -`, cid, n, vlan, bgpAsn) +`, cid, rName, amzAddr, custAddr, bgpAsn, vlan) }