diff --git a/aws/provider.go b/aws/provider.go index 0655be2d4c6..53cd84c4412 100644 --- a/aws/provider.go +++ b/aws/provider.go @@ -445,6 +445,8 @@ func Provider() terraform.ResourceProvider { "aws_dx_hosted_private_virtual_interface_accepter": resourceAwsDxHostedPrivateVirtualInterfaceAccepter(), "aws_dx_hosted_public_virtual_interface": resourceAwsDxHostedPublicVirtualInterface(), "aws_dx_hosted_public_virtual_interface_accepter": resourceAwsDxHostedPublicVirtualInterfaceAccepter(), + "aws_dx_hosted_transit_virtual_interface": resourceAwsDxHostedTransitVirtualInterface(), + "aws_dx_hosted_transit_virtual_interface_accepter": resourceAwsDxHostedTransitVirtualInterfaceAccepter(), "aws_dx_lag": resourceAwsDxLag(), "aws_dx_private_virtual_interface": resourceAwsDxPrivateVirtualInterface(), "aws_dx_public_virtual_interface": resourceAwsDxPublicVirtualInterface(), diff --git a/aws/resource_aws_dx_hosted_transit_virtual_interface.go b/aws/resource_aws_dx_hosted_transit_virtual_interface.go new file mode 100644 index 00000000000..194eac432cc --- /dev/null +++ b/aws/resource_aws_dx_hosted_transit_virtual_interface.go @@ -0,0 +1,219 @@ +package aws + +import ( + "fmt" + "log" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" + "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" +) + +func resourceAwsDxHostedTransitVirtualInterface() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsDxHostedTransitVirtualInterfaceCreate, + Read: resourceAwsDxHostedTransitVirtualInterfaceRead, + Delete: resourceAwsDxHostedTransitVirtualInterfaceDelete, + Importer: &schema.ResourceImporter{ + State: resourceAwsDxHostedTransitVirtualInterfaceImport, + }, + + Schema: map[string]*schema.Schema{ + "address_family": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + directconnect.AddressFamilyIpv4, + directconnect.AddressFamilyIpv6, + }, false), + }, + "amazon_address": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "aws_device": { + Type: schema.TypeString, + Computed: true, + }, + "bgp_asn": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + }, + "bgp_auth_key": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "connection_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "customer_address": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "jumbo_frame_capable": { + Type: schema.TypeBool, + Computed: true, + }, + "mtu": { + Type: schema.TypeInt, + Default: 1500, + Optional: true, + ForceNew: true, + ValidateFunc: validation.IntInSlice([]int{1500, 8500}), + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "owner_account_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateAwsAccountId, + }, + "vlan": { + Type: schema.TypeInt, + Required: true, + ForceNew: true, + ValidateFunc: validation.IntBetween(1, 4094), + }, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(10 * time.Minute), + Update: schema.DefaultTimeout(10 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + }, + } +} + +func resourceAwsDxHostedTransitVirtualInterfaceCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).dxconn + + req := &directconnect.AllocateTransitVirtualInterfaceInput{ + ConnectionId: aws.String(d.Get("connection_id").(string)), + OwnerAccount: aws.String(d.Get("owner_account_id").(string)), + NewTransitVirtualInterfaceAllocation: &directconnect.NewTransitVirtualInterfaceAllocation{ + 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 v, ok := d.GetOk("amazon_address"); ok && v.(string) != "" { + req.NewTransitVirtualInterfaceAllocation.AmazonAddress = aws.String(v.(string)) + } + if v, ok := d.GetOk("bgp_auth_key"); ok && v.(string) != "" { + req.NewTransitVirtualInterfaceAllocation.AuthKey = aws.String(v.(string)) + } + if v, ok := d.GetOk("customer_address"); ok && v.(string) != "" { + req.NewTransitVirtualInterfaceAllocation.CustomerAddress = aws.String(v.(string)) + } + + log.Printf("[DEBUG] Creating Direct Connect hosted transit virtual interface: %s", req) + resp, err := conn.AllocateTransitVirtualInterface(req) + if err != nil { + return fmt.Errorf("error creating Direct Connect hosted transit virtual interface: %s", err) + } + + d.SetId(aws.StringValue(resp.VirtualInterface.VirtualInterfaceId)) + + if err := dxHostedTransitVirtualInterfaceWaitUntilAvailable(conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { + return err + } + + return resourceAwsDxHostedTransitVirtualInterfaceRead(d, meta) +} + +func resourceAwsDxHostedTransitVirtualInterfaceRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).dxconn + + vif, err := dxVirtualInterfaceRead(d.Id(), conn) + if err != nil { + return err + } + if vif == nil { + log.Printf("[WARN] Direct Connect hosted transit virtual interface (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil + } + + 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("connection_id", vif.ConnectionId) + d.Set("customer_address", vif.CustomerAddress) + d.Set("jumbo_frame_capable", vif.JumboFrameCapable) + d.Set("mtu", vif.Mtu) + d.Set("name", vif.VirtualInterfaceName) + d.Set("owner_account_id", vif.OwnerAccount) + d.Set("vlan", vif.Vlan) + + return nil +} + +func resourceAwsDxHostedTransitVirtualInterfaceDelete(d *schema.ResourceData, meta interface{}) error { + return dxVirtualInterfaceDelete(d, meta) +} + +func resourceAwsDxHostedTransitVirtualInterfaceImport(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 != "transit" { + return nil, fmt.Errorf("virtual interface (%s) has incorrect type: %s", d.Id(), vifType) + } + + return []*schema.ResourceData{d}, nil +} + +func dxHostedTransitVirtualInterfaceWaitUntilAvailable(conn *directconnect.DirectConnect, vifId string, timeout time.Duration) error { + return dxVirtualInterfaceWaitUntilAvailable( + conn, + vifId, + timeout, + []string{ + directconnect.VirtualInterfaceStatePending, + }, + []string{ + directconnect.VirtualInterfaceStateAvailable, + directconnect.VirtualInterfaceStateConfirming, + directconnect.VirtualInterfaceStateDown, + }) +} diff --git a/aws/resource_aws_dx_hosted_transit_virtual_interface_accepter.go b/aws/resource_aws_dx_hosted_transit_virtual_interface_accepter.go new file mode 100644 index 00000000000..ba69cd5dd7e --- /dev/null +++ b/aws/resource_aws_dx_hosted_transit_virtual_interface_accepter.go @@ -0,0 +1,162 @@ +package aws + +import ( + "fmt" + "log" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/arn" + "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +func resourceAwsDxHostedTransitVirtualInterfaceAccepter() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsDxHostedTransitVirtualInterfaceAccepterCreate, + Read: resourceAwsDxHostedTransitVirtualInterfaceAccepterRead, + Update: resourceAwsDxHostedTransitVirtualInterfaceAccepterUpdate, + Delete: resourceAwsDxHostedTransitVirtualInterfaceAccepterDelete, + Importer: &schema.ResourceImporter{ + State: resourceAwsDxHostedTransitVirtualInterfaceAccepterImport, + }, + + Schema: map[string]*schema.Schema{ + "arn": { + Type: schema.TypeString, + Computed: true, + }, + "dx_gateway_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "tags": tagsSchema(), + "virtual_interface_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(10 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + }, + } +} + +func resourceAwsDxHostedTransitVirtualInterfaceAccepterCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).dxconn + + vifId := d.Get("virtual_interface_id").(string) + req := &directconnect.ConfirmTransitVirtualInterfaceInput{ + DirectConnectGatewayId: aws.String(d.Get("dx_gateway_id").(string)), + VirtualInterfaceId: aws.String(vifId), + } + + log.Printf("[DEBUG] Accepting Direct Connect hosted transit virtual interface: %s", req) + _, err := conn.ConfirmTransitVirtualInterface(req) + if err != nil { + return fmt.Errorf("error accepting Direct Connect hosted transit virtual interface (%s): %s", vifId, err) + } + + d.SetId(vifId) + 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 := dxHostedTransitVirtualInterfaceAccepterWaitUntilAvailable(conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil { + return err + } + + return resourceAwsDxHostedTransitVirtualInterfaceAccepterUpdate(d, meta) +} + +func resourceAwsDxHostedTransitVirtualInterfaceAccepterRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).dxconn + + vif, err := dxVirtualInterfaceRead(d.Id(), conn) + if err != nil { + return err + } + if vif == nil { + log.Printf("[WARN] Direct Connect transit 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()) + d.SetId("") + return nil + } + + d.Set("dx_gateway_id", vif.DirectConnectGatewayId) + d.Set("virtual_interface_id", vif.VirtualInterfaceId) + if err := getTagsDX(conn, d, d.Get("arn").(string)); err != nil { + return fmt.Errorf("error getting Direct Connect transit virtual interface (%s) tags: %s", d.Id(), err) + } + + return nil +} + +func resourceAwsDxHostedTransitVirtualInterfaceAccepterUpdate(d *schema.ResourceData, meta interface{}) error { + if err := dxVirtualInterfaceUpdate(d, meta); err != nil { + return err + } + + return resourceAwsDxHostedTransitVirtualInterfaceAccepterRead(d, meta) +} + +func resourceAwsDxHostedTransitVirtualInterfaceAccepterDelete(d *schema.ResourceData, meta interface{}) error { + log.Printf("[WARN] Will not delete Direct Connect virtual interface. Terraform will remove this resource from the state file, however resources may remain.") + return nil +} + +func resourceAwsDxHostedTransitVirtualInterfaceAccepterImport(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 != "transit" { + 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, + Service: "directconnect", + AccountID: meta.(*AWSClient).accountid, + Resource: fmt.Sprintf("dxvif/%s", d.Id()), + }.String() + d.Set("arn", arn) + + return []*schema.ResourceData{d}, nil +} + +func dxHostedTransitVirtualInterfaceAccepterWaitUntilAvailable(conn *directconnect.DirectConnect, vifId string, timeout time.Duration) error { + return dxVirtualInterfaceWaitUntilAvailable( + conn, + vifId, + timeout, + []string{ + directconnect.VirtualInterfaceStateConfirming, + directconnect.VirtualInterfaceStatePending, + }, + []string{ + directconnect.VirtualInterfaceStateAvailable, + directconnect.VirtualInterfaceStateDown, + }) +} diff --git a/aws/resource_aws_dx_hosted_transit_virtual_interface_test.go b/aws/resource_aws_dx_hosted_transit_virtual_interface_test.go new file mode 100644 index 00000000000..dd2a51b2989 --- /dev/null +++ b/aws/resource_aws_dx_hosted_transit_virtual_interface_test.go @@ -0,0 +1,250 @@ +package aws + +import ( + "fmt" + "os" + "regexp" + "strconv" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/directconnect" + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/terraform" +) + +func TestAccAwsDxHostedTransitVirtualInterface(t *testing.T) { + testCases := map[string]func(t *testing.T){ + "basic": testAccAwsDxHostedTransitVirtualInterface_basic, + "accepterTags": testAccAwsDxHostedTransitVirtualInterface_accepterTags, + } + + for name, tc := range testCases { + tc := tc + t.Run(name, func(t *testing.T) { + tc(t) + }) + } +} + +func testAccAwsDxHostedTransitVirtualInterface_basic(t *testing.T) { + key := "DX_CONNECTION_ID" + connectionId := os.Getenv(key) + if connectionId == "" { + t.Skipf("Environment variable %s is not set", key) + } + + var providers []*schema.Provider + var vif directconnect.VirtualInterface + resourceName := "aws_dx_hosted_transit_virtual_interface.test" + accepterResourceName := "aws_dx_hosted_transit_virtual_interface_accepter.test" + dxGatewayResourceName := "aws_dx_gateway.test" + rName := fmt.Sprintf("tf-testacc-transit-vif-%s", acctest.RandString(9)) + amzAsn := randIntRange(64512, 65534) + bgpAsn := randIntRange(64512, 65534) + vlan := randIntRange(2049, 4094) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccAlternateAccountPreCheck(t) + }, + ProviderFactories: testAccProviderFactories(&providers), + CheckDestroy: testAccCheckAwsDxHostedTransitVirtualInterfaceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDxHostedTransitVirtualInterfaceConfig_basic(connectionId, rName, amzAsn, bgpAsn, vlan), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsDxHostedTransitVirtualInterfaceExists(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.TestCheckResourceAttrPair(accepterResourceName, "dx_gateway_id", dxGatewayResourceName, "id"), + resource.TestCheckResourceAttr(accepterResourceName, "tags.%", "0"), + resource.TestCheckResourceAttrPair(accepterResourceName, "virtual_interface_id", resourceName, "id"), + ), + }, + // Test import. + { + Config: testAccDxHostedTransitVirtualInterfaceConfig_basic(connectionId, rName, amzAsn, bgpAsn, vlan), + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccAwsDxHostedTransitVirtualInterface_accepterTags(t *testing.T) { + key := "DX_CONNECTION_ID" + connectionId := os.Getenv(key) + if connectionId == "" { + t.Skipf("Environment variable %s is not set", key) + } + + var providers []*schema.Provider + var vif directconnect.VirtualInterface + resourceName := "aws_dx_hosted_transit_virtual_interface.test" + accepterResourceName := "aws_dx_hosted_transit_virtual_interface_accepter.test" + dxGatewayResourceName := "aws_dx_gateway.test" + rName := fmt.Sprintf("tf-testacc-transit-vif-%s", acctest.RandString(9)) + amzAsn := randIntRange(64512, 65534) + bgpAsn := randIntRange(64512, 65534) + vlan := randIntRange(2049, 4094) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { + testAccPreCheck(t) + testAccAlternateAccountPreCheck(t) + }, + ProviderFactories: testAccProviderFactories(&providers), + CheckDestroy: testAccCheckAwsDxHostedTransitVirtualInterfaceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccDxHostedTransitVirtualInterfaceConfig_accepterTags(connectionId, rName, amzAsn, bgpAsn, vlan), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsDxHostedTransitVirtualInterfaceExists(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.TestCheckResourceAttrPair(accepterResourceName, "dx_gateway_id", dxGatewayResourceName, "id"), + 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: testAccDxHostedTransitVirtualInterfaceConfig_accepterTagsUpdated(connectionId, rName, amzAsn, bgpAsn, vlan), + Check: resource.ComposeTestCheckFunc( + testAccCheckAwsDxHostedTransitVirtualInterfaceExists(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.TestCheckResourceAttrPair(accepterResourceName, "dx_gateway_id", dxGatewayResourceName, "id"), + 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 testAccCheckAwsDxHostedTransitVirtualInterfaceExists(name string, vif *directconnect.VirtualInterface) resource.TestCheckFunc { + return testAccCheckDxVirtualInterfaceExists(name, vif) +} + +func testAccCheckAwsDxHostedTransitVirtualInterfaceDestroy(s *terraform.State) error { + return testAccCheckDxVirtualInterfaceDestroy(s, "aws_dx_hosted_transit_virtual_interface") +} + +func testAccDxHostedTransitVirtualInterfaceConfig_base(cid, rName string, amzAsn, bgpAsn, vlan int) string { + return testAccAlternateAccountProviderConfig() + fmt.Sprintf(` +# Creator +resource "aws_dx_hosted_transit_virtual_interface" "test" { + address_family = "ipv4" + bgp_asn = %[4]d + connection_id = %[1]q + name = %[2]q + owner_account_id = "${data.aws_caller_identity.accepter.account_id}" + vlan = %[5]d + + # The aws_dx_hosted_transit_virtual_interface + # must be destroyed before the aws_dx_gateway. + depends_on = ["aws_dx_gateway.test"] +} + +# Accepter +data "aws_caller_identity" "accepter" { + provider = "aws.alternate" +} + +resource "aws_dx_gateway" "test" { + provider = "aws.alternate" + + amazon_side_asn = %[3]d + name = %[2]q +} +`, cid, rName, amzAsn, bgpAsn, vlan) +} + +func testAccDxHostedTransitVirtualInterfaceConfig_basic(cid, rName string, amzAsn, bgpAsn, vlan int) string { + return testAccDxHostedTransitVirtualInterfaceConfig_base(cid, rName, amzAsn, bgpAsn, vlan) + fmt.Sprintf(` +resource "aws_dx_hosted_transit_virtual_interface_accepter" "test" { + provider = "aws.alternate" + + dx_gateway_id = "${aws_dx_gateway.test.id}" + virtual_interface_id = "${aws_dx_hosted_transit_virtual_interface.test.id}" +} +`) +} + +func testAccDxHostedTransitVirtualInterfaceConfig_accepterTags(cid, rName string, amzAsn, bgpAsn, vlan int) string { + return testAccDxHostedTransitVirtualInterfaceConfig_base(cid, rName, amzAsn, bgpAsn, vlan) + fmt.Sprintf(` +resource "aws_dx_hosted_transit_virtual_interface_accepter" "test" { + provider = "aws.alternate" + + dx_gateway_id = "${aws_dx_gateway.test.id}" + virtual_interface_id = "${aws_dx_hosted_transit_virtual_interface.test.id}" + + tags = { + Name = %[1]q + Key1 = "Value1" + Key2 = "Value2a" + } +} +`, rName) +} + +func testAccDxHostedTransitVirtualInterfaceConfig_accepterTagsUpdated(cid, rName string, amzAsn, bgpAsn, vlan int) string { + return testAccDxHostedTransitVirtualInterfaceConfig_base(cid, rName, amzAsn, bgpAsn, vlan) + fmt.Sprintf(` +resource "aws_dx_hosted_transit_virtual_interface_accepter" "test" { + provider = "aws.alternate" + + dx_gateway_id = "${aws_dx_gateway.test.id}" + virtual_interface_id = "${aws_dx_hosted_transit_virtual_interface.test.id}" + + tags = { + Name = %[1]q + Key2 = "Value2b" + Key3 = "Value3" + } +} +`, rName) +} diff --git a/website/aws.erb b/website/aws.erb index 8b880c6a1b0..13d868ca19c 100644 --- a/website/aws.erb +++ b/website/aws.erb @@ -877,13 +877,13 @@ aws_dx_hosted_public_virtual_interface_accepter
  • - aws_dx_lag + aws_dx_hosted_transit_virtual_interface
  • - aws_dx_private_virtual_interface + aws_dx_hosted_transit_virtual_interface_accepter
  • - aws_dx_public_virtual_interface + aws_dx_lag
  • aws_dx_transit_virtual_interface diff --git a/website/docs/r/dx_hosted_transit_virtual_interface.html.markdown b/website/docs/r/dx_hosted_transit_virtual_interface.html.markdown new file mode 100644 index 00000000000..4448670de55 --- /dev/null +++ b/website/docs/r/dx_hosted_transit_virtual_interface.html.markdown @@ -0,0 +1,68 @@ +--- +subcategory: "Direct Connect" +layout: "aws" +page_title: "AWS: aws_dx_hosted_transit_virtual_interface" +sidebar_current: "docs-aws-resource-dx-hosted-transit-virtual-interface" +description: |- + Provides a Direct Connect hosted transit virtual interface resource. +--- + +# Resource: aws_dx_hosted_transit_virtual_interface + +Provides a Direct Connect hosted transit virtual interface resource. +This resource represents the allocator's side of the hosted virtual interface. +A hosted virtual interface is a virtual interface that is owned by another AWS account. + +## Example Usage + +```hcl +resource "aws_dx_hosted_transit_virtual_interface" "example" { + connection_id = "${aws_dx_connection.example.id}" + + name = "tf-transit-vif-example" + vlan = 4094 + address_family = "ipv4" + bgp_asn = 65352 +} +``` + +## Argument Reference + +The following arguments are supported: + +* `address_family` - (Required) The address family for the BGP peer. `ipv4 ` or `ipv6`. +* `bgp_asn` - (Required) The autonomous system (AS) number for Border Gateway Protocol (BGP) configuration. +* `connection_id` - (Required) The ID of the Direct Connect connection (or LAG) on which to create the virtual interface. +* `name` - (Required) The name for the virtual interface. +* `owner_account_id` - (Required) The AWS account that will own the new virtual interface. +* `vlan` - (Required) The VLAN ID. +* `amazon_address` - (Optional) The IPv4 CIDR address to use to send traffic to Amazon. Required for IPv4 BGP peers. +* `bgp_auth_key` - (Optional) The authentication key for BGP configuration. +* `customer_address` - (Optional) The IPv4 CIDR destination address to which Amazon should send traffic. Required for IPv4 BGP peers. +* `mtu` - (Optional) The maximum transmission unit (MTU) is the size, in bytes, of the largest permissible packet that can be passed over the connection. The MTU of a virtual transit interface can be either `1500` or `8500` (jumbo frames). Default is `1500`. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The ID of the virtual interface. +* `arn` - The ARN of the virtual interface. +* `aws_device` - The Direct Connect endpoint on which the virtual interface terminates. +* `jumbo_frame_capable` - Indicates whether jumbo frames (8500 MTU) are supported. + +## Timeouts + +`aws_dx_hosted_transit_virtual_interface` provides the following +[Timeouts](/docs/configuration/resources.html#timeouts) configuration options: + +- `create` - (Default `10 minutes`) Used for creating virtual interface +- `update` - (Default `10 minutes`) Used for virtual interface modifications +- `delete` - (Default `10 minutes`) Used for destroying virtual interface + +## Import + +Direct Connect hosted transit virtual interfaces can be imported using the `vif id`, e.g. + +``` +$ terraform import aws_dx_hosted_transit_virtual_interface.test dxvif-33cc44dd +``` diff --git a/website/docs/r/dx_hosted_transit_virtual_interface_accepter.html.markdown b/website/docs/r/dx_hosted_transit_virtual_interface_accepter.html.markdown new file mode 100644 index 00000000000..48b0935556b --- /dev/null +++ b/website/docs/r/dx_hosted_transit_virtual_interface_accepter.html.markdown @@ -0,0 +1,97 @@ +--- +subcategory: "Direct Connect" +layout: "aws" +page_title: "AWS: aws_dx_hosted_transit_virtual_interface_accepter" +sidebar_current: "docs-aws-resource-dx-hosted-transit-virtual-interface-accepter" +description: |- + Provides a resource to manage the accepter's side of a Direct Connect hosted transit virtual interface. +--- + +# Resource: aws_dx_hosted_transit_virtual_interface_accepter + +Provides a resource to manage the accepter's side of a Direct Connect hosted transit virtual interface. +This resource accepts ownership of a transit virtual interface created by another AWS account. + +-> **NOTE:** AWS allows a Direct Connect hosted transit virtual interface to be deleted from either the allocator's or accepter's side. However, Terraform only allows the Direct Connect hosted transit virtual interface to be deleted from the allocator's side by removing the corresponding `aws_dx_hosted_transit_virtual_interface` resource from your configuration. Removing a `aws_dx_hosted_transit_virtual_interface_accepter` resource from your configuration will remove it from your statefile and management, **but will not delete the Direct Connect virtual interface.** + +## Example Usage + +```hcl +provider "aws" { + # Creator's credentials. +} + +provider "aws" { + alias = "accepter" + + # Accepter's credentials. +} + +data "aws_caller_identity" "accepter" { + provider = "aws.accepter" +} + +# Creator's side of the VIF +resource "aws_dx_hosted_transit_virtual_interface" "creator" { + connection_id = "dxcon-zzzzzzzz" + owner_account_id = "${data.aws_caller_identity.accepter.account_id}" + + name = "tf-transit-vif-example" + vlan = 4094 + address_family = "ipv4" + bgp_asn = 65352 + + # The aws_dx_hosted_transit_virtual_interface + # must be destroyed before the aws_dx_gateway. + depends_on = ["aws_dx_gateway.example"] +} + +# Accepter's side of the VIF. +resource "aws_dx_gateway" "example" { + provider = "aws.accepter" + + name = "tf-dxg-example" + amazon_side_asn = 64512 +} + +resource "aws_dx_hosted_transit_virtual_interface_accepter" "accepter" { + provider = "aws.accepter" + virtual_interface_id = "${aws_dx_hosted_transit_virtual_interface.creator.id}" + dx_gateway_id = "${aws_dx_gateway.example.id}" + + tags = { + Side = "Accepter" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `dx_gateway_id` - (Required) The ID of the [Direct Connect gateway](dx_gateway.html) to which to connect the virtual interface. +* `virtual_interface_id` - (Required) The ID of the Direct Connect virtual interface to accept. +* `tags` - (Optional) A mapping of tags to assign to the resource. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The ID of the virtual interface. +* `arn` - The ARN of the virtual interface. + +## Timeouts + +`aws_dx_hosted_transit_virtual_interface_accepter` provides the following +[Timeouts](/docs/configuration/resources.html#timeouts) configuration options: + +- `create` - (Default `10 minutes`) Used for creating virtual interface +- `delete` - (Default `10 minutes`) Used for destroying virtual interface + +## Import + +Direct Connect hosted transit virtual interfaces can be imported using the `vif id`, e.g. + +``` +$ terraform import aws_dx_hosted_transit_virtual_interface_accepter.test dxvif-33cc44dd +```