From b5b53bc56a060aac8f27e8833a778fa17ea3b7c3 Mon Sep 17 00:00:00 2001 From: Paul Stack Date: Wed, 8 Mar 2017 21:11:59 +0200 Subject: [PATCH] provider/aws: Error on trying to recreate an existing customer gateway (#12501) Fixes: #7492 When we use the same IP Address, BGP ASN and VPN Type as an existing aws_customer_gateway, terraform will take control of that gateway (not import it!) and try and modify it. This could be very bad There is a warning on the AWS documentation that one gateway of the same parameters can be created, Terraform is now going to error if a gateway of the same parameters is attempted to be created ``` % make testacc TEST=./builtin/providers/aws TESTARGS='-run=TestAccAWSCustomerGateway_' ==> Checking that code complies with gofmt requirements... go generate $(go list ./... | grep -v /terraform/vendor/) 2017/03/07 18:40:39 Generated command/internal_plugin_list.go TF_ACC=1 go test ./builtin/providers/aws -v -run=TestAccAWSCustomerGateway_ -timeout 120m === RUN TestAccAWSCustomerGateway_importBasic --- PASS: TestAccAWSCustomerGateway_importBasic (31.11s) === RUN TestAccAWSCustomerGateway_basic --- PASS: TestAccAWSCustomerGateway_basic (68.72s) === RUN TestAccAWSCustomerGateway_similarAlreadyExists --- PASS: TestAccAWSCustomerGateway_similarAlreadyExists (35.18s) === RUN TestAccAWSCustomerGateway_disappears --- PASS: TestAccAWSCustomerGateway_disappears (25.13s) PASS ok github.com/hashicorp/terraform/builtin/providers/aws 160.172s ``` --- .../aws/resource_aws_customer_gateway.go | 56 +++++++++++++++++-- .../aws/resource_aws_customer_gateway_test.go | 51 +++++++++++++++-- 2 files changed, 97 insertions(+), 10 deletions(-) diff --git a/builtin/providers/aws/resource_aws_customer_gateway.go b/builtin/providers/aws/resource_aws_customer_gateway.go index 28f2278cd..668f8a80c 100644 --- a/builtin/providers/aws/resource_aws_customer_gateway.go +++ b/builtin/providers/aws/resource_aws_customer_gateway.go @@ -25,19 +25,19 @@ func resourceAwsCustomerGateway() *schema.Resource { }, Schema: map[string]*schema.Schema{ - "bgp_asn": &schema.Schema{ + "bgp_asn": { Type: schema.TypeInt, Required: true, ForceNew: true, }, - "ip_address": &schema.Schema{ + "ip_address": { Type: schema.TypeString, Required: true, ForceNew: true, }, - "type": &schema.Schema{ + "type": { Type: schema.TypeString, Required: true, ForceNew: true, @@ -51,10 +51,23 @@ func resourceAwsCustomerGateway() *schema.Resource { func resourceAwsCustomerGatewayCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ec2conn + ipAddress := d.Get("ip_address").(string) + vpnType := d.Get("type").(string) + bgpAsn := d.Get("bgp_asn").(int) + + alreadyExists, err := resourceAwsCustomerGatewayExists(vpnType, ipAddress, bgpAsn, conn) + if err != nil { + return err + } + + if alreadyExists { + return fmt.Errorf("An existing customer gateway for IpAddress: %s, VpnType: %s, BGP ASN: %d has been found", ipAddress, vpnType, bgpAsn) + } + createOpts := &ec2.CreateCustomerGatewayInput{ - BgpAsn: aws.Int64(int64(d.Get("bgp_asn").(int))), - PublicIp: aws.String(d.Get("ip_address").(string)), - Type: aws.String(d.Get("type").(string)), + BgpAsn: aws.Int64(int64(bgpAsn)), + PublicIp: aws.String(ipAddress), + Type: aws.String(vpnType), } // Create the Customer Gateway. @@ -123,6 +136,37 @@ func customerGatewayRefreshFunc(conn *ec2.EC2, gatewayId string) resource.StateR } } +func resourceAwsCustomerGatewayExists(vpnType, ipAddress string, bgpAsn int, conn *ec2.EC2) (bool, error) { + ipAddressFilter := &ec2.Filter{ + Name: aws.String("ip-address"), + Values: []*string{aws.String(ipAddress)}, + } + + typeFilter := &ec2.Filter{ + Name: aws.String("type"), + Values: []*string{aws.String(vpnType)}, + } + + bgp := strconv.Itoa(bgpAsn) + bgpAsnFilter := &ec2.Filter{ + Name: aws.String("bgp-asn"), + Values: []*string{aws.String(bgp)}, + } + + resp, err := conn.DescribeCustomerGateways(&ec2.DescribeCustomerGatewaysInput{ + Filters: []*ec2.Filter{ipAddressFilter, typeFilter, bgpAsnFilter}, + }) + if err != nil { + return false, err + } + + if len(resp.CustomerGateways) > 0 && *resp.CustomerGateways[0].State != "deleted" { + return true, nil + } + + return false, nil +} + func resourceAwsCustomerGatewayRead(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ec2conn diff --git a/builtin/providers/aws/resource_aws_customer_gateway_test.go b/builtin/providers/aws/resource_aws_customer_gateway_test.go index 311fe746d..1938ce0bd 100644 --- a/builtin/providers/aws/resource_aws_customer_gateway_test.go +++ b/builtin/providers/aws/resource_aws_customer_gateway_test.go @@ -2,6 +2,7 @@ package aws import ( "fmt" + "regexp" "testing" "time" @@ -21,19 +22,19 @@ func TestAccAWSCustomerGateway_basic(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckCustomerGatewayDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccCustomerGatewayConfig, Check: resource.ComposeTestCheckFunc( testAccCheckCustomerGateway("aws_customer_gateway.foo", &gateway), ), }, - resource.TestStep{ + { Config: testAccCustomerGatewayConfigUpdateTags, Check: resource.ComposeTestCheckFunc( testAccCheckCustomerGateway("aws_customer_gateway.foo", &gateway), ), }, - resource.TestStep{ + { Config: testAccCustomerGatewayConfigForceReplace, Check: resource.ComposeTestCheckFunc( testAccCheckCustomerGateway("aws_customer_gateway.foo", &gateway), @@ -43,6 +44,28 @@ func TestAccAWSCustomerGateway_basic(t *testing.T) { }) } +func TestAccAWSCustomerGateway_similarAlreadyExists(t *testing.T) { + var gateway ec2.CustomerGateway + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + IDRefreshName: "aws_customer_gateway.foo", + Providers: testAccProviders, + CheckDestroy: testAccCheckCustomerGatewayDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCustomerGatewayConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckCustomerGateway("aws_customer_gateway.foo", &gateway), + ), + }, + { + Config: testAccCustomerGatewayConfigIdentical, + ExpectError: regexp.MustCompile("An existing customer gateway"), + }, + }, + }) +} + func TestAccAWSCustomerGateway_disappears(t *testing.T) { var gateway ec2.CustomerGateway resource.Test(t, resource.TestCase{ @@ -50,7 +73,7 @@ func TestAccAWSCustomerGateway_disappears(t *testing.T) { Providers: testAccProviders, CheckDestroy: testAccCheckCustomerGatewayDestroy, Steps: []resource.TestStep{ - resource.TestStep{ + { Config: testAccCustomerGatewayConfig, Check: resource.ComposeTestCheckFunc( testAccCheckCustomerGateway("aws_customer_gateway.foo", &gateway), @@ -178,6 +201,26 @@ resource "aws_customer_gateway" "foo" { } ` +const testAccCustomerGatewayConfigIdentical = ` +resource "aws_customer_gateway" "foo" { + bgp_asn = 65000 + ip_address = "172.0.0.1" + type = "ipsec.1" + tags { + Name = "foo-gateway" + } +} + +resource "aws_customer_gateway" "identical" { + bgp_asn = 65000 + ip_address = "172.0.0.1" + type = "ipsec.1" + tags { + Name = "foo-gateway-identical" + } +} +` + // Add the Another: "tag" tag. const testAccCustomerGatewayConfigUpdateTags = ` resource "aws_customer_gateway" "foo" {