diff --git a/aws/resource_aws_vpn_gateway.go b/aws/resource_aws_vpn_gateway.go index 05af657e9036..d4006d37c1b8 100644 --- a/aws/resource_aws_vpn_gateway.go +++ b/aws/resource_aws_vpn_gateway.go @@ -104,7 +104,7 @@ func resourceAwsVpnGatewayRead(d *schema.ResourceData, meta interface{}) error { } vpnAttachment := vpnGatewayGetAttachment(vpnGateway) - if len(vpnGateway.VpcAttachments) == 0 || *vpnAttachment.State == "detached" { + if vpnAttachment == nil { // Gateway exists but not attached to the VPC d.Set("vpc_id", "") } else { @@ -181,7 +181,9 @@ func resourceAwsVpnGatewayDelete(d *schema.ResourceData, meta interface{}) error func resourceAwsVpnGatewayAttach(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ec2conn - if d.Get("vpc_id").(string) == "" { + vpcId := d.Get("vpc_id").(string) + + if vpcId == "" { log.Printf( "[DEBUG] Not attaching VPN Gateway '%s' as no VPC ID is set", d.Id()) @@ -191,11 +193,11 @@ func resourceAwsVpnGatewayAttach(d *schema.ResourceData, meta interface{}) error log.Printf( "[INFO] Attaching VPN Gateway '%s' to VPC '%s'", d.Id(), - d.Get("vpc_id").(string)) + vpcId) req := &ec2.AttachVpnGatewayInput{ VpnGatewayId: aws.String(d.Id()), - VpcId: aws.String(d.Get("vpc_id").(string)), + VpcId: aws.String(vpcId), } err := resource.Retry(1*time.Minute, func() *resource.RetryError { @@ -218,7 +220,7 @@ func resourceAwsVpnGatewayAttach(d *schema.ResourceData, meta interface{}) error stateConf := &resource.StateChangeConf{ Pending: []string{"detached", "attaching"}, Target: []string{"attached"}, - Refresh: vpnGatewayAttachStateRefreshFunc(conn, d.Id()), + Refresh: vpnGatewayAttachmentStateRefresh(conn, vpcId, d.Id()), Timeout: 15 * time.Minute, } if _, err := stateConf.WaitForState(); err != nil { @@ -234,9 +236,10 @@ func resourceAwsVpnGatewayDetach(d *schema.ResourceData, meta interface{}) error conn := meta.(*AWSClient).ec2conn // Get the old VPC ID to detach from - vpcID, _ := d.GetChange("vpc_id") + vpcIdRaw, _ := d.GetChange("vpc_id") + vpcId := vpcIdRaw.(string) - if vpcID.(string) == "" { + if vpcId == "" { log.Printf( "[DEBUG] Not detaching VPN Gateway '%s' as no VPC ID is set", d.Id()) @@ -246,12 +249,12 @@ func resourceAwsVpnGatewayDetach(d *schema.ResourceData, meta interface{}) error log.Printf( "[INFO] Detaching VPN Gateway '%s' from VPC '%s'", d.Id(), - vpcID.(string)) + vpcId) wait := true _, err := conn.DetachVpnGateway(&ec2.DetachVpnGatewayInput{ VpnGatewayId: aws.String(d.Id()), - VpcId: aws.String(vpcID.(string)), + VpcId: aws.String(vpcId), }) if err != nil { ec2err, ok := err.(awserr.Error) @@ -279,7 +282,7 @@ func resourceAwsVpnGatewayDetach(d *schema.ResourceData, meta interface{}) error stateConf := &resource.StateChangeConf{ Pending: []string{"attached", "detaching", "available"}, Target: []string{"detached"}, - Refresh: vpnGatewayAttachStateRefreshFunc(conn, d.Id()), + Refresh: vpnGatewayAttachmentStateRefresh(conn, vpcId, d.Id()), Timeout: 10 * time.Minute, } if _, err := stateConf.WaitForState(); err != nil { @@ -291,50 +294,12 @@ func resourceAwsVpnGatewayDetach(d *schema.ResourceData, meta interface{}) error return nil } -// vpnGatewayAttachStateRefreshFunc returns a resource.StateRefreshFunc that is used to watch -// the state of a VPN gateway's attachment -func vpnGatewayAttachStateRefreshFunc(conn *ec2.EC2, id string) resource.StateRefreshFunc { - var start time.Time - return func() (interface{}, string, error) { - if start.IsZero() { - start = time.Now() - } - - resp, err := conn.DescribeVpnGateways(&ec2.DescribeVpnGatewaysInput{ - VpnGatewayIds: []*string{aws.String(id)}, - }) - - if err != nil { - if ec2err, ok := err.(awserr.Error); ok && ec2err.Code() == "InvalidVpnGatewayID.NotFound" { - resp = nil - } else { - log.Printf("[ERROR] Error on VpnGatewayStateRefresh: %s", err) - return nil, "", err - } - } - - if resp == nil { - // Sometimes AWS just has consistency issues and doesn't see - // our instance yet. Return an empty state. - return nil, "", nil - } - - vpnGateway := resp.VpnGateways[0] - if len(vpnGateway.VpcAttachments) == 0 { - // No attachments, we're detached - return vpnGateway, "detached", nil - } - - vpnAttachment := vpnGatewayGetAttachment(vpnGateway) - return vpnGateway, *vpnAttachment.State, nil - } -} - +// vpnGatewayGetAttachment returns any VGW attachment that's in "attached" state or nil. func vpnGatewayGetAttachment(vgw *ec2.VpnGateway) *ec2.VpcAttachment { - for _, v := range vgw.VpcAttachments { - if *v.State == "attached" { - return v + for _, vpcAttachment := range vgw.VpcAttachments { + if aws.StringValue(vpcAttachment.State) == ec2.AttachmentStatusAttached { + return vpcAttachment } } - return &ec2.VpcAttachment{State: aws.String("detached")} + return nil } diff --git a/aws/resource_aws_vpn_gateway_attachment.go b/aws/resource_aws_vpn_gateway_attachment.go index fd081b81bf3b..bdee8503e179 100644 --- a/aws/resource_aws_vpn_gateway_attachment.go +++ b/aws/resource_aws_vpn_gateway_attachment.go @@ -101,7 +101,7 @@ func resourceAwsVpnGatewayAttachmentRead(d *schema.ResourceData, meta interface{ } vga := vpnGatewayGetAttachment(vgw) - if len(vgw.VpcAttachments) == 0 || *vga.State == "detached" { + if vga == nil { d.Set("vpc_id", "") return nil } @@ -163,12 +163,9 @@ func resourceAwsVpnGatewayAttachmentDelete(d *schema.ResourceData, meta interfac func vpnGatewayAttachmentStateRefresh(conn *ec2.EC2, vpcId, vgwId string) resource.StateRefreshFunc { return func() (interface{}, string, error) { resp, err := conn.DescribeVpnGateways(&ec2.DescribeVpnGatewaysInput{ - Filters: []*ec2.Filter{ - { - Name: aws.String("attachment.vpc-id"), - Values: []*string{aws.String(vpcId)}, - }, - }, + Filters: buildEC2AttributeFilterList(map[string]string{ + "attachment.vpc-id": vpcId, + }), VpnGatewayIds: []*string{aws.String(vgwId)}, }) @@ -187,15 +184,19 @@ func vpnGatewayAttachmentStateRefresh(conn *ec2.EC2, vpcId, vgwId string) resour } vgw := resp.VpnGateways[0] - if len(vgw.VpcAttachments) == 0 { - return vgw, "detached", nil - } - vga := vpnGatewayGetAttachment(vgw) + return vgw, vpnGatewayGetAttachmentState(vgw, vpcId), nil + } +} - log.Printf("[DEBUG] VPN Gateway %q attachment status: %s", vgwId, *vga.State) - return vgw, *vga.State, nil +// vpnGatewayGetAttachmentState returns the state of any VGW attachment to the specified VPC or "detached". +func vpnGatewayGetAttachmentState(vgw *ec2.VpnGateway, vpcId string) string { + for _, vpcAttachment := range vgw.VpcAttachments { + if aws.StringValue(vpcAttachment.VpcId) == vpcId { + return aws.StringValue(vpcAttachment.State) + } } + return ec2.AttachmentStatusDetached } func vpnGatewayAttachmentId(vpcId, vgwId string) string {