diff --git a/builtin/providers/aws/provider.go b/builtin/providers/aws/provider.go index 69e264dd9..234a9c90e 100644 --- a/builtin/providers/aws/provider.go +++ b/builtin/providers/aws/provider.go @@ -282,6 +282,7 @@ func Provider() terraform.ResourceProvider { "aws_vpn_connection": resourceAwsVpnConnection(), "aws_vpn_connection_route": resourceAwsVpnConnectionRoute(), "aws_vpn_gateway": resourceAwsVpnGateway(), + "aws_vpn_gateway_attachment": resourceAwsVpnGatewayAttachment(), }, ConfigureFunc: providerConfigure, } diff --git a/builtin/providers/aws/resource_aws_vpn_gateway.go b/builtin/providers/aws/resource_aws_vpn_gateway.go index 27f4a45f7..845e11cd3 100644 --- a/builtin/providers/aws/resource_aws_vpn_gateway.go +++ b/builtin/providers/aws/resource_aws_vpn_gateway.go @@ -32,6 +32,7 @@ func resourceAwsVpnGateway() *schema.Resource { "vpc_id": &schema.Schema{ Type: schema.TypeString, Optional: true, + Computed: true, }, "tags": tagsSchema(), @@ -80,17 +81,18 @@ func resourceAwsVpnGatewayRead(d *schema.ResourceData, meta interface{}) error { } vpnGateway := resp.VpnGateways[0] - if vpnGateway == nil { + if vpnGateway == nil || *vpnGateway.State == "deleted" { // Seems we have lost our VPN gateway d.SetId("") return nil } - if len(vpnGateway.VpcAttachments) == 0 || *vpnGateway.VpcAttachments[0].State == "detached" || *vpnGateway.VpcAttachments[0].State == "deleted" { + vpnAttachment := vpnGatewayGetAttachment(vpnGateway) + if len(vpnGateway.VpcAttachments) == 0 || *vpnAttachment.State == "detached" { // Gateway exists but not attached to the VPC d.Set("vpc_id", "") } else { - d.Set("vpc_id", vpnGateway.VpcAttachments[0].VpcId) + d.Set("vpc_id", *vpnAttachment.VpcId) } d.Set("availability_zone", vpnGateway.AvailabilityZone) d.Set("tags", tagsToMap(vpnGateway.Tags)) @@ -301,12 +303,21 @@ func vpnGatewayAttachStateRefreshFunc(conn *ec2.EC2, id string, expected string) } vpnGateway := resp.VpnGateways[0] - if len(vpnGateway.VpcAttachments) == 0 { // No attachments, we're detached return vpnGateway, "detached", nil } - return vpnGateway, *vpnGateway.VpcAttachments[0].State, nil + vpnAttachment := vpnGatewayGetAttachment(vpnGateway) + return vpnGateway, *vpnAttachment.State, nil } } + +func vpnGatewayGetAttachment(vgw *ec2.VpnGateway) *ec2.VpcAttachment { + for _, v := range vgw.VpcAttachments { + if *v.State == "attached" { + return v + } + } + return &ec2.VpcAttachment{State: aws.String("detached")} +} diff --git a/builtin/providers/aws/resource_aws_vpn_gateway_attachment.go b/builtin/providers/aws/resource_aws_vpn_gateway_attachment.go new file mode 100644 index 000000000..b19393bfb --- /dev/null +++ b/builtin/providers/aws/resource_aws_vpn_gateway_attachment.go @@ -0,0 +1,210 @@ +package aws + +import ( + "fmt" + "log" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/awserr" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/hashicorp/terraform/helper/hashcode" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" +) + +func resourceAwsVpnGatewayAttachment() *schema.Resource { + return &schema.Resource{ + Create: resourceAwsVpnGatewayAttachmentCreate, + Read: resourceAwsVpnGatewayAttachmentRead, + Delete: resourceAwsVpnGatewayAttachmentDelete, + + Schema: map[string]*schema.Schema{ + "vpc_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "vpn_gateway_id": &schema.Schema{ + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + }, + } +} + +func resourceAwsVpnGatewayAttachmentCreate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + vpcId := d.Get("vpc_id").(string) + vgwId := d.Get("vpn_gateway_id").(string) + + createOpts := &ec2.AttachVpnGatewayInput{ + VpcId: aws.String(vpcId), + VpnGatewayId: aws.String(vgwId), + } + log.Printf("[DEBUG] VPN Gateway attachment options: %#v", *createOpts) + + _, err := conn.AttachVpnGateway(createOpts) + if err != nil { + return fmt.Errorf("Error attaching VPN Gateway %q to VPC %q: %s", + vgwId, vpcId, err) + } + + d.SetId(vpnGatewayAttachmentId(vpcId, vgwId)) + log.Printf("[INFO] VPN Gateway %q attachment ID: %s", vgwId, d.Id()) + + stateConf := &resource.StateChangeConf{ + Pending: []string{"detached", "attaching"}, + Target: []string{"attached"}, + Refresh: vpnGatewayAttachmentStateRefresh(conn, vpcId, vgwId), + Timeout: 5 * time.Minute, + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + } + + _, err = stateConf.WaitForState() + if err != nil { + return fmt.Errorf("Error waiting for VPN Gateway %q to attach to VPC %q: %s", + vgwId, vpcId, err) + } + log.Printf("[DEBUG] VPN Gateway %q attached to VPC %q.", vgwId, vpcId) + + return resourceAwsVpnGatewayAttachmentRead(d, meta) +} + +func resourceAwsVpnGatewayAttachmentRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + vgwId := d.Get("vpn_gateway_id").(string) + + resp, err := conn.DescribeVpnGateways(&ec2.DescribeVpnGatewaysInput{ + VpnGatewayIds: []*string{aws.String(vgwId)}, + }) + + if err != nil { + awsErr, ok := err.(awserr.Error) + if ok && awsErr.Code() == "InvalidVPNGatewayID.NotFound" { + log.Printf("[WARN] VPN Gateway %q not found.", vgwId) + d.SetId("") + return nil + } + return err + } + + vgw := resp.VpnGateways[0] + if *vgw.State == "deleted" { + log.Printf("[INFO] VPN Gateway %q appears to have been deleted.", vgwId) + d.SetId("") + return nil + } + + vga := vpnGatewayGetAttachment(vgw) + if len(vgw.VpcAttachments) == 0 || *vga.State == "detached" { + d.Set("vpc_id", "") + return nil + } + + d.Set("vpc_id", *vga.VpcId) + return nil +} + +func resourceAwsVpnGatewayAttachmentDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + vpcId := d.Get("vpc_id").(string) + vgwId := d.Get("vpn_gateway_id").(string) + + if vpcId == "" { + log.Printf("[DEBUG] Not detaching VPN Gateway %q as no VPC ID is set.", vgwId) + return nil + } + + _, err := conn.DetachVpnGateway(&ec2.DetachVpnGatewayInput{ + VpcId: aws.String(vpcId), + VpnGatewayId: aws.String(vgwId), + }) + + if err != nil { + awsErr, ok := err.(awserr.Error) + if ok { + switch awsErr.Code() { + case "InvalidVPNGatewayID.NotFound": + log.Printf("[WARN] VPN Gateway %q not found.", vgwId) + d.SetId("") + return nil + case "InvalidVpnGatewayAttachment.NotFound": + log.Printf( + "[WARN] VPN Gateway %q attachment to VPC %q not found.", + vgwId, vpcId) + d.SetId("") + return nil + } + } + + return fmt.Errorf("Error detaching VPN Gateway %q from VPC %q: %s", + vgwId, vpcId, err) + } + + stateConf := &resource.StateChangeConf{ + Pending: []string{"attached", "detaching"}, + Target: []string{"detached"}, + Refresh: vpnGatewayAttachmentStateRefresh(conn, vpcId, vgwId), + Timeout: 5 * time.Minute, + Delay: 10 * time.Second, + MinTimeout: 3 * time.Second, + } + + _, err = stateConf.WaitForState() + if err != nil { + return fmt.Errorf("Error waiting for VPN Gateway %q to detach from VPC %q: %s", + vgwId, vpcId, err) + } + log.Printf("[DEBUG] VPN Gateway %q detached from VPC %q.", vgwId, vpcId) + + d.SetId("") + return nil +} + +func vpnGatewayAttachmentStateRefresh(conn *ec2.EC2, vpcId, vgwId string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + resp, err := conn.DescribeVpnGateways(&ec2.DescribeVpnGatewaysInput{ + Filters: []*ec2.Filter{ + &ec2.Filter{ + Name: aws.String("attachment.vpc-id"), + Values: []*string{aws.String(vpcId)}, + }, + }, + VpnGatewayIds: []*string{aws.String(vgwId)}, + }) + + if err != nil { + awsErr, ok := err.(awserr.Error) + if ok { + switch awsErr.Code() { + case "InvalidVPNGatewayID.NotFound": + fallthrough + case "InvalidVpnGatewayAttachment.NotFound": + return nil, "", nil + } + } + + return nil, "", err + } + + vgw := resp.VpnGateways[0] + if len(vgw.VpcAttachments) == 0 { + return vgw, "detached", nil + } + + vga := vpnGatewayGetAttachment(vgw) + + log.Printf("[DEBUG] VPN Gateway %q attachment status: %s", vgwId, *vga.State) + return vgw, *vga.State, nil + } +} + +func vpnGatewayAttachmentId(vpcId, vgwId string) string { + return fmt.Sprintf("vpn-attachment-%x", hashcode.String(fmt.Sprintf("%s-%s", vpcId, vgwId))) +} diff --git a/builtin/providers/aws/resource_aws_vpn_gateway_attachment_test.go b/builtin/providers/aws/resource_aws_vpn_gateway_attachment_test.go new file mode 100644 index 000000000..5f12d6fb8 --- /dev/null +++ b/builtin/providers/aws/resource_aws_vpn_gateway_attachment_test.go @@ -0,0 +1,163 @@ +package aws + +import ( + "fmt" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" +) + +func TestAccAWSVpnGatewayAttachment_basic(t *testing.T) { + var vpc ec2.Vpc + var vgw ec2.VpnGateway + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + IDRefreshName: "aws_vpn_gateway_attachment.test", + Providers: testAccProviders, + CheckDestroy: testAccCheckVpnGatewayAttachmentDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccVpnGatewayAttachmentConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckVpcExists( + "aws_vpc.test", + &vpc), + testAccCheckVpnGatewayExists( + "aws_vpn_gateway.test", + &vgw), + testAccCheckVpnGatewayAttachmentExists( + "aws_vpn_gateway_attachment.test", + &vpc, &vgw), + ), + }, + }, + }) +} + +func TestAccAWSVpnGatewayAttachment_deleted(t *testing.T) { + var vpc ec2.Vpc + var vgw ec2.VpnGateway + + testDeleted := func(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + _, ok := s.RootModule().Resources[n] + if ok { + return fmt.Errorf("Expected VPN Gateway attachment resource %q to be deleted.", n) + } + return nil + } + } + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + IDRefreshName: "aws_vpn_gateway_attachment.test", + Providers: testAccProviders, + CheckDestroy: testAccCheckVpnGatewayAttachmentDestroy, + Steps: []resource.TestStep{ + resource.TestStep{ + Config: testAccVpnGatewayAttachmentConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckVpcExists( + "aws_vpc.test", + &vpc), + testAccCheckVpnGatewayExists( + "aws_vpn_gateway.test", + &vgw), + testAccCheckVpnGatewayAttachmentExists( + "aws_vpn_gateway_attachment.test", + &vpc, &vgw), + ), + }, + resource.TestStep{ + Config: testAccNoVpnGatewayAttachmentConfig, + Check: resource.ComposeTestCheckFunc( + testDeleted("aws_vpn_gateway_attachment.test"), + ), + }, + }, + }) +} + +func testAccCheckVpnGatewayAttachmentExists(n string, vpc *ec2.Vpc, vgw *ec2.VpnGateway) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("Not found: %s", n) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("No ID is set") + } + + vpcId := rs.Primary.Attributes["vpc_id"] + vgwId := rs.Primary.Attributes["vpn_gateway_id"] + + if len(vgw.VpcAttachments) == 0 { + return fmt.Errorf("VPN Gateway %q has no attachments.", vgwId) + } + + if *vgw.VpcAttachments[0].State != "attached" { + return fmt.Errorf("Expected VPN Gateway %q to be in attached state, but got: %q", + vgwId, *vgw.VpcAttachments[0].State) + } + + if *vgw.VpcAttachments[0].VpcId != *vpc.VpcId { + return fmt.Errorf("Expected VPN Gateway %q to be attached to VPC %q, but got: %q", + vgwId, vpcId, *vgw.VpcAttachments[0].VpcId) + } + + return nil + } +} + +func testAccCheckVpnGatewayAttachmentDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*AWSClient).ec2conn + + for _, rs := range s.RootModule().Resources { + if rs.Type != "aws_vpn_gateway_attachment" { + continue + } + + vgwId := rs.Primary.Attributes["vpn_gateway_id"] + + resp, err := conn.DescribeVpnGateways(&ec2.DescribeVpnGatewaysInput{ + VpnGatewayIds: []*string{aws.String(vgwId)}, + }) + if err != nil { + return err + } + + vgw := resp.VpnGateways[0] + if *vgw.VpcAttachments[0].State != "detached" { + return fmt.Errorf("Expected VPN Gateway %q to be in detached state, but got: %q", + vgwId, *vgw.VpcAttachments[0].State) + } + } + + return nil +} + +const testAccNoVpnGatewayAttachmentConfig = ` +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" +} + +resource "aws_vpn_gateway" "test" { } +` + +const testAccVpnGatewayAttachmentConfig = ` +resource "aws_vpc" "test" { + cidr_block = "10.0.0.0/16" +} + +resource "aws_vpn_gateway" "test" { } + +resource "aws_vpn_gateway_attachment" "test" { + vpc_id = "${aws_vpc.test.id}" + vpn_gateway_id = "${aws_vpn_gateway.test.id}" +} +` diff --git a/builtin/providers/aws/resource_aws_vpn_gateway_test.go b/builtin/providers/aws/resource_aws_vpn_gateway_test.go index 0e3677d6f..c9d2d921a 100644 --- a/builtin/providers/aws/resource_aws_vpn_gateway_test.go +++ b/builtin/providers/aws/resource_aws_vpn_gateway_test.go @@ -16,10 +16,10 @@ func TestAccAWSVpnGateway_basic(t *testing.T) { testNotEqual := func(*terraform.State) error { if len(v.VpcAttachments) == 0 { - return fmt.Errorf("VPN gateway A is not attached") + return fmt.Errorf("VPN Gateway A is not attached") } if len(v2.VpcAttachments) == 0 { - return fmt.Errorf("VPN gateway B is not attached") + return fmt.Errorf("VPN Gateway B is not attached") } id1 := v.VpcAttachments[0].VpcId @@ -58,20 +58,38 @@ func TestAccAWSVpnGateway_basic(t *testing.T) { } func TestAccAWSVpnGateway_reattach(t *testing.T) { - var v ec2.VpnGateway + var vpc1, vpc2 ec2.Vpc + var vgw1, vgw2 ec2.VpnGateway - genTestStateFunc := func(expectedState string) func(*terraform.State) error { + testAttachmentFunc := func(vgw *ec2.VpnGateway, vpc *ec2.Vpc) func(*terraform.State) error { return func(*terraform.State) error { - if len(v.VpcAttachments) == 0 { - if expectedState != "detached" { - return fmt.Errorf("VPN gateway has no VPC attachments") + if len(vgw.VpcAttachments) == 0 { + return fmt.Errorf("VPN Gateway %q has no VPC attachments.", + *vgw.VpnGatewayId) + } + + if len(vgw.VpcAttachments) > 1 { + count := 0 + for _, v := range vgw.VpcAttachments { + if *v.State == "attached" { + count += 1 + } } - } else if len(v.VpcAttachments) == 1 { - if *v.VpcAttachments[0].State != expectedState { - return fmt.Errorf("Expected VPC gateway VPC attachment to be in '%s' state, but was not: %s", expectedState, v) + if count > 1 { + return fmt.Errorf( + "VPN Gateway %q has an unexpected number of VPC attachments (more than 1): %#v", + *vgw.VpnGatewayId, vgw.VpcAttachments) } - } else { - return fmt.Errorf("VPN gateway has unexpected number of VPC attachments(more than 1): %s", v) + } + + if *vgw.VpcAttachments[0].State != "attached" { + return fmt.Errorf("Expected VPN Gateway %q to be attached.", + *vgw.VpnGatewayId) + } + + if *vgw.VpcAttachments[0].VpcId != *vpc.VpcId { + return fmt.Errorf("Expected VPN Gateway %q to be attached to VPC %q, but got: %q", + *vgw.VpnGatewayId, *vpc.VpcId, *vgw.VpcAttachments[0].VpcId) } return nil } @@ -84,27 +102,38 @@ func TestAccAWSVpnGateway_reattach(t *testing.T) { CheckDestroy: testAccCheckVpnGatewayDestroy, Steps: []resource.TestStep{ resource.TestStep{ - Config: testAccVpnGatewayConfig, + Config: testAccCheckVpnGatewayConfigReattach, Check: resource.ComposeTestCheckFunc( + testAccCheckVpcExists("aws_vpc.foo", &vpc1), + testAccCheckVpcExists("aws_vpc.bar", &vpc2), testAccCheckVpnGatewayExists( - "aws_vpn_gateway.foo", &v), - genTestStateFunc("attached"), + "aws_vpn_gateway.foo", &vgw1), + testAccCheckVpnGatewayExists( + "aws_vpn_gateway.bar", &vgw2), + testAttachmentFunc(&vgw1, &vpc1), + testAttachmentFunc(&vgw2, &vpc2), ), }, resource.TestStep{ - Config: testAccVpnGatewayConfigDetach, + Config: testAccCheckVpnGatewayConfigReattachChange, Check: resource.ComposeTestCheckFunc( testAccCheckVpnGatewayExists( - "aws_vpn_gateway.foo", &v), - genTestStateFunc("detached"), + "aws_vpn_gateway.foo", &vgw1), + testAccCheckVpnGatewayExists( + "aws_vpn_gateway.bar", &vgw2), + testAttachmentFunc(&vgw2, &vpc1), + testAttachmentFunc(&vgw1, &vpc2), ), }, resource.TestStep{ - Config: testAccVpnGatewayConfig, + Config: testAccCheckVpnGatewayConfigReattach, Check: resource.ComposeTestCheckFunc( testAccCheckVpnGatewayExists( - "aws_vpn_gateway.foo", &v), - genTestStateFunc("attached"), + "aws_vpn_gateway.foo", &vgw1), + testAccCheckVpnGatewayExists( + "aws_vpn_gateway.bar", &vgw2), + testAttachmentFunc(&vgw1, &vpc1), + testAttachmentFunc(&vgw2, &vpc2), ), }, }, @@ -118,7 +147,7 @@ func TestAccAWSVpnGateway_delete(t *testing.T) { return func(s *terraform.State) error { _, ok := s.RootModule().Resources[r] if ok { - return fmt.Errorf("VPN Gateway %q should have been deleted", r) + return fmt.Errorf("VPN Gateway %q should have been deleted.", r) } return nil } @@ -159,7 +188,6 @@ func TestAccAWSVpnGateway_tags(t *testing.T) { testAccCheckTags(&v.Tags, "foo", "bar"), ), }, - resource.TestStep{ Config: testAccCheckVpnGatewayConfigTagsUpdate, Check: resource.ComposeTestCheckFunc( @@ -198,7 +226,7 @@ func testAccCheckVpnGatewayDestroy(s *terraform.State) error { } if *v.State != "deleted" { - return fmt.Errorf("Expected VpnGateway to be in deleted state, but was not: %s", v) + return fmt.Errorf("Expected VPN Gateway to be in deleted state, but was not: %s", v) } return nil } @@ -235,7 +263,7 @@ func testAccCheckVpnGatewayExists(n string, ig *ec2.VpnGateway) resource.TestChe return err } if len(resp.VpnGateways) == 0 { - return fmt.Errorf("VPNGateway not found") + return fmt.Errorf("VPN Gateway not found") } *ig = *resp.VpnGateways[0] @@ -270,16 +298,6 @@ resource "aws_vpn_gateway" "foo" { } ` -const testAccVpnGatewayConfigDetach = ` -resource "aws_vpc" "foo" { - cidr_block = "10.1.0.0/16" -} - -resource "aws_vpn_gateway" "foo" { - vpc_id = "" -} -` - const testAccCheckVpnGatewayConfigTags = ` resource "aws_vpc" "foo" { cidr_block = "10.1.0.0/16" @@ -305,3 +323,39 @@ resource "aws_vpn_gateway" "foo" { } } ` + +const testAccCheckVpnGatewayConfigReattach = ` +resource "aws_vpc" "foo" { + cidr_block = "10.1.0.0/16" +} + +resource "aws_vpc" "bar" { + cidr_block = "10.2.0.0/16" +} + +resource "aws_vpn_gateway" "foo" { + vpc_id = "${aws_vpc.foo.id}" +} + +resource "aws_vpn_gateway" "bar" { + vpc_id = "${aws_vpc.bar.id}" +} +` + +const testAccCheckVpnGatewayConfigReattachChange = ` +resource "aws_vpc" "foo" { + cidr_block = "10.1.0.0/16" +} + +resource "aws_vpc" "bar" { + cidr_block = "10.2.0.0/16" +} + +resource "aws_vpn_gateway" "foo" { + vpc_id = "${aws_vpc.bar.id}" +} + +resource "aws_vpn_gateway" "bar" { + vpc_id = "${aws_vpc.foo.id}" +} +` diff --git a/website/source/docs/providers/aws/r/vpn_gateway_attachment.html.markdown b/website/source/docs/providers/aws/r/vpn_gateway_attachment.html.markdown new file mode 100644 index 000000000..809912831 --- /dev/null +++ b/website/source/docs/providers/aws/r/vpn_gateway_attachment.html.markdown @@ -0,0 +1,57 @@ +--- +layout: "aws" +page_title: "AWS: aws_vpn_gateway_attachment" +sidebar_current: "docs-aws-resource-vpn-gateway-attachment" +description: |- + Provides a Virtual Private Gateway attachment resource. +--- + +# aws\_vpn\_gateway\_attachment + +Provides a Virtual Private Gateway attachment resource, allowing for an existing +hardware VPN gateway to be attached and/or detached from a VPC. + +-> **Note:** The [`aws_vpn_gateway`](vpn_gateway.html) +resource can also automatically attach the Virtual Private Gateway it creates +to an existing VPC by setting the [`vpc_id`](vpn_gateway.html#vpc_id) attribute accordingly. + +## Example Usage + +``` +resource "aws_vpc" "network" { + cidr_block = "10.0.0.0/16" +} + +resource "aws_vpn_gateway" "vpn" { + tags { + Name = "example-vpn-gateway" + } +} + +resource "aws_vpn_gateway_attachment" "vpn_attachment" { + vpc_id = "${aws_vpc.network.id}" + vpn_gateway_id = "${aws_vpn_gateway.vpn.id}" +} +``` + +See [Virtual Private Cloud](http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_Introduction.html) +and [Virtual Private Gateway](http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/VPC_VPN.html) user +guides for more information. + +## Argument Reference + +The following arguments are supported: + +* `vpc_id` - (Required) The ID of the VPC. +* `vpn_gateway_id` - (Required) The ID of the Virtual Private Gateway. + +## Attributes Reference + +The following attributes are exported: + +* `vpc_id` - The ID of the VPC that Virtual Private Gateway is attached to. +* `vpn_gateway_id` - The ID of the Virtual Private Gateway. + +## Import + +This resource does not support importing. diff --git a/website/source/layouts/aws.erb b/website/source/layouts/aws.erb index 8ba2ae80e..4eec00ae6 100644 --- a/website/source/layouts/aws.erb +++ b/website/source/layouts/aws.erb @@ -885,6 +885,10 @@ aws_vpn_gateway + > + aws_vpn_gateway_attachment + +