Skip to content

Commit

Permalink
Added more tests to aws_vpn_connection and documentation
Browse files Browse the repository at this point in the history
a-teisseire committed Jan 10, 2018

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 8947b6b commit da3bce1
Showing 3 changed files with 253 additions and 101 deletions.
119 changes: 86 additions & 33 deletions aws/resource_aws_vpn_connection.go
Original file line number Diff line number Diff line change
@@ -5,7 +5,10 @@ import (
"encoding/xml"
"fmt"
"log"
"net"
"regexp"
"sort"
"strings"
"time"

"github.com/aws/aws-sdk-go/aws"
@@ -95,33 +98,37 @@ func resourceAwsVpnConnection() *schema.Resource {
},

"tunnel1_inside_cidr": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: validateVpnConnectionTunnelInsideCIDR,
},

"tunnel1_preshared_key": {
Type: schema.TypeString,
Optional: true,
Sensitive: true,
Computed: true,
ForceNew: true,
Type: schema.TypeString,
Optional: true,
Sensitive: true,
Computed: true,
ForceNew: true,
ValidateFunc: validateVpnConnectionTunnelPreSharedKey,
},

"tunnel2_inside_cidr": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
ValidateFunc: validateVpnConnectionTunnelInsideCIDR,
},

"tunnel2_preshared_key": {
Type: schema.TypeString,
Optional: true,
Sensitive: true,
Computed: true,
ForceNew: true,
Type: schema.TypeString,
Optional: true,
Sensitive: true,
Computed: true,
ForceNew: true,
ValidateFunc: validateVpnConnectionTunnelPreSharedKey,
},

"tags": tagsSchema(),
@@ -261,32 +268,25 @@ func resourceAwsVpnConnection() *schema.Resource {
func resourceAwsVpnConnectionCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).ec2conn

// Get the optional tunnel options
tunnel1_cidr := d.Get("tunnel1_inside_cidr").(string)
tunnel2_cidr := d.Get("tunnel2_inside_cidr").(string)

tunnel1_psk := d.Get("tunnel1_preshared_key").(string)
tunnel2_psk := d.Get("tunnel2_preshared_key").(string)

// Fill the tunnel options for the EC2 API
options := []*ec2.VpnTunnelOptionsSpecification{
{}, {},
}

if tunnel1_cidr != "" {
options[0].TunnelInsideCidr = aws.String(tunnel1_cidr)
if v, ok := d.GetOk("tunnel1_inside_cidr"); ok {
options[0].TunnelInsideCidr = aws.String(v.(string))
}

if tunnel2_cidr != "" {
options[1].TunnelInsideCidr = aws.String(tunnel2_cidr)
if v, ok := d.GetOk("tunnel2_inside_cidr"); ok {
options[1].TunnelInsideCidr = aws.String(v.(string))
}

if tunnel1_psk != "" {
options[0].PreSharedKey = aws.String(tunnel1_psk)
if v, ok := d.GetOk("tunnel1_preshared_key"); ok {
options[0].PreSharedKey = aws.String(v.(string))
}

if tunnel2_psk != "" {
options[1].PreSharedKey = aws.String(tunnel2_psk)
if v, ok := d.GetOk("tunnel2_preshared_key"); ok {
options[1].PreSharedKey = aws.String(v.(string))
}

connectOpts := &ec2.VpnConnectionOptionsSpecification{
@@ -556,3 +556,56 @@ func xmlConfigToTunnelInfo(xmlConfig string) (*TunnelInfo, error) {

return &tunnelInfo, nil
}

func validateVpnConnectionTunnelPreSharedKey(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)

if (len(value) < 8) || (len(value) > 64) {
errors = append(errors, fmt.Errorf("%q must be between 8 and 64 characters in length", k))
}

if strings.HasPrefix(value, "0") {
errors = append(errors, fmt.Errorf("%q cannot start with zero character", k))
}

if !regexp.MustCompile(`^[0-9a-zA-Z_]+$`).MatchString(value) {
errors = append(errors, fmt.Errorf("%q can only contain alphanumeric and underscore characters", k))
}

return
}

// https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_VpnTunnelOptionsSpecification.html
func validateVpnConnectionTunnelInsideCIDR(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
_, ipnet, err := net.ParseCIDR(value)

if err != nil {
errors = append(errors, fmt.Errorf("%q must contain a valid CIDR, got error parsing: %s", k, err))
return
}

if !strings.HasSuffix(ipnet.String(), "/30") {
errors = append(errors, fmt.Errorf("%q must be /30 CIDR", k))
}

if !strings.HasPrefix(ipnet.String(), "169.254.") {
errors = append(errors, fmt.Errorf("%q must be within 169.254.0.0/16", k))
} else if ipnet.String() == "169.254.0.0/30" {
errors = append(errors, fmt.Errorf("%q cannot be 169.254.0.0/30", k))
} else if ipnet.String() == "169.254.1.0/30" {
errors = append(errors, fmt.Errorf("%q cannot be 169.254.1.0/30", k))
} else if ipnet.String() == "169.254.2.0/30" {
errors = append(errors, fmt.Errorf("%q cannot be 169.254.2.0/30", k))
} else if ipnet.String() == "169.254.3.0/30" {
errors = append(errors, fmt.Errorf("%q cannot be 169.254.3.0/30", k))
} else if ipnet.String() == "169.254.4.0/30" {
errors = append(errors, fmt.Errorf("%q cannot be 169.254.4.0/30", k))
} else if ipnet.String() == "169.254.5.0/30" {
errors = append(errors, fmt.Errorf("%q cannot be 169.254.5.0/30", k))
} else if ipnet.String() == "169.254.169.252/30" {
errors = append(errors, fmt.Errorf("%q cannot be 169.254.169.252/30", k))
}

return
}
228 changes: 160 additions & 68 deletions aws/resource_aws_vpn_connection_test.go
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@ package aws

import (
"fmt"
"regexp"
"testing"
"time"

@@ -63,8 +64,70 @@ func TestAccAWSVpnConnection_tunnelOptions(t *testing.T) {
Providers: testAccProviders,
CheckDestroy: testAccAwsVpnConnectionDestroy,
Steps: []resource.TestStep{

// Checking CIDR blocks
{
Config: testAccAwsVpnConnectionConfigSingleTunnelOptions(rBgpAsn, "12345678", "not-a-cidr"),
ExpectError: regexp.MustCompile(`must contain a valid CIDR`),
},
{
Config: testAccAwsVpnConnectionConfigSingleTunnelOptions(rBgpAsn, "12345678", "169.254.254.0/31"),
ExpectError: regexp.MustCompile(`must be /30 CIDR`),
},
{
Config: testAccAwsVpnConnectionConfigSingleTunnelOptions(rBgpAsn, "12345678", "172.16.0.0/30"),
ExpectError: regexp.MustCompile(`must be within 169.254.0.0/16`),
},
{
Config: testAccAwsVpnConnectionConfigSingleTunnelOptions(rBgpAsn, "12345678", "169.254.0.0/30"),
ExpectError: regexp.MustCompile(`cannot be 169.254.0.0/30`),
},
{
Config: testAccAwsVpnConnectionConfigSingleTunnelOptions(rBgpAsn, "12345678", "169.254.1.0/30"),
ExpectError: regexp.MustCompile(`cannot be 169.254.1.0/30`),
},
{
Config: testAccAwsVpnConnectionConfigSingleTunnelOptions(rBgpAsn, "12345678", "169.254.2.0/30"),
ExpectError: regexp.MustCompile(`cannot be 169.254.2.0/30`),
},
{
Config: testAccAwsVpnConnectionConfigSingleTunnelOptions(rBgpAsn, "12345678", "169.254.3.0/30"),
ExpectError: regexp.MustCompile(`cannot be 169.254.3.0/30`),
},
{
Config: testAccAwsVpnConnectionConfigSingleTunnelOptions(rBgpAsn, "12345678", "169.254.4.0/30"),
ExpectError: regexp.MustCompile(`cannot be 169.254.4.0/30`),
},
{
Config: testAccAwsVpnConnectionConfigSingleTunnelOptions(rBgpAsn, "12345678", "169.254.5.0/30"),
ExpectError: regexp.MustCompile(`cannot be 169.254.5.0/30`),
},
{
Config: testAccAwsVpnConnectionConfigSingleTunnelOptions(rBgpAsn, "12345678", "169.254.169.252/30"),
ExpectError: regexp.MustCompile(`cannot be 169.254.169.252/30`),
},

// Checking PreShared Key
{
Config: testAccAwsVpnConnectionConfigSingleTunnelOptions(rBgpAsn, "1234567", "169.254.254.0/30"),
ExpectError: regexp.MustCompile(`must be between 8 and 64 characters in length`),
},
{
Config: testAccAwsVpnConnectionConfigSingleTunnelOptions(rBgpAsn, acctest.RandStringFromCharSet(65, acctest.CharSetAlpha), "169.254.254.0/30"),
ExpectError: regexp.MustCompile(`must be between 8 and 64 characters in length`),
},
{
Config: testAccAwsVpnConnectionConfigSingleTunnelOptions(rBgpAsn, "01234567", "169.254.254.0/30"),
ExpectError: regexp.MustCompile(`cannot start with zero character`),
},
{
Config: testAccAwsVpnConnectionConfigSingleTunnelOptions(rBgpAsn, "1234567!", "169.254.254.0/30"),
ExpectError: regexp.MustCompile(`can only contain alphanumeric and underscore characters`),
},

//Try actual building
{
Config: testAccAwsVpnConnectionConfigTunnelOptions(rBgpAsn),
Config: testAccAwsVpnConnectionConfigTunnelOptions(rBgpAsn, "12345678", "169.254.8.0/30", "abcdefgh", "169.254.9.0/30"),
Check: resource.ComposeTestCheckFunc(
testAccAwsVpnConnection(
"aws_vpc.vpc",
@@ -76,10 +139,10 @@ func TestAccAWSVpnConnection_tunnelOptions(t *testing.T) {
resource.TestCheckResourceAttr("aws_vpn_connection.foo", "static_routes_only", "false"),

resource.TestCheckResourceAttr("aws_vpn_connection.foo", "tunnel1_inside_cidr", "169.254.8.0/30"),
resource.TestCheckResourceAttr("aws_vpn_connection.foo", "tunnel1_preshared_key", "lookatmethisisaprivatekey1"),
resource.TestCheckResourceAttr("aws_vpn_connection.foo", "tunnel1_preshared_key", "12345678"),

resource.TestCheckResourceAttr("aws_vpn_connection.foo", "tunnel2_inside_cidr", "169.254.9.0/30"),
resource.TestCheckResourceAttr("aws_vpn_connection.foo", "tunnel2_preshared_key", "lookatmethisisaprivatekey2"),
resource.TestCheckResourceAttr("aws_vpn_connection.foo", "tunnel2_preshared_key", "abcdefgh"),
),
},
},
@@ -308,87 +371,116 @@ func TestAWSVpnConnection_xmlconfig(t *testing.T) {

func testAccAwsVpnConnectionConfig(rBgpAsn int) string {
return fmt.Sprintf(`
resource "aws_vpn_gateway" "vpn_gateway" {
tags {
Name = "vpn_gateway"
}
}
resource "aws_vpn_gateway" "vpn_gateway" {
tags {
Name = "vpn_gateway"
}
}
resource "aws_customer_gateway" "customer_gateway" {
bgp_asn = %d
ip_address = "178.0.0.1"
type = "ipsec.1"
tags {
Name = "main-customer-gateway"
}
}
resource "aws_customer_gateway" "customer_gateway" {
bgp_asn = %d
ip_address = "178.0.0.1"
type = "ipsec.1"
tags {
Name = "main-customer-gateway"
}
}
resource "aws_vpn_connection" "foo" {
vpn_gateway_id = "${aws_vpn_gateway.vpn_gateway.id}"
customer_gateway_id = "${aws_customer_gateway.customer_gateway.id}"
type = "ipsec.1"
static_routes_only = true
}
`, rBgpAsn)
resource "aws_vpn_connection" "foo" {
vpn_gateway_id = "${aws_vpn_gateway.vpn_gateway.id}"
customer_gateway_id = "${aws_customer_gateway.customer_gateway.id}"
type = "ipsec.1"
static_routes_only = true
}
`, rBgpAsn)
}

// Change static_routes_only to be false, forcing a refresh.
func testAccAwsVpnConnectionConfigUpdate(rInt, rBgpAsn int) string {
return fmt.Sprintf(`
resource "aws_vpn_gateway" "vpn_gateway" {
tags {
Name = "vpn_gateway"
}
}
resource "aws_vpn_gateway" "vpn_gateway" {
tags {
Name = "vpn_gateway"
}
}
resource "aws_customer_gateway" "customer_gateway" {
bgp_asn = %d
ip_address = "178.0.0.1"
type = "ipsec.1"
tags {
Name = "main-customer-gateway-%d"
}
}
resource "aws_customer_gateway" "customer_gateway" {
bgp_asn = %d
ip_address = "178.0.0.1"
type = "ipsec.1"
tags {
Name = "main-customer-gateway-%d"
}
}
resource "aws_vpn_connection" "foo" {
vpn_gateway_id = "${aws_vpn_gateway.vpn_gateway.id}"
customer_gateway_id = "${aws_customer_gateway.customer_gateway.id}"
type = "ipsec.1"
static_routes_only = false
}
`, rBgpAsn, rInt)
resource "aws_vpn_connection" "foo" {
vpn_gateway_id = "${aws_vpn_gateway.vpn_gateway.id}"
customer_gateway_id = "${aws_customer_gateway.customer_gateway.id}"
type = "ipsec.1"
static_routes_only = false
}
`, rBgpAsn, rInt)
}

func testAccAwsVpnConnectionConfigTunnelOptions(rBgpAsn int) string {
func testAccAwsVpnConnectionConfigSingleTunnelOptions(rBgpAsn int, psk string, tunnelCidr string) string {
return fmt.Sprintf(`
resource "aws_vpn_gateway" "vpn_gateway" {
tags {
Name = "vpn_gateway"
}
}
resource "aws_vpn_gateway" "vpn_gateway" {
tags {
Name = "vpn_gateway"
}
}
resource "aws_customer_gateway" "customer_gateway" {
bgp_asn = %d
ip_address = "178.0.0.1"
type = "ipsec.1"
tags {
Name = "main-customer-gateway"
}
}
resource "aws_customer_gateway" "customer_gateway" {
bgp_asn = %d
ip_address = "178.0.0.1"
type = "ipsec.1"
tags {
Name = "main-customer-gateway"
}
}
resource "aws_vpn_connection" "foo" {
vpn_gateway_id = "${aws_vpn_gateway.vpn_gateway.id}"
customer_gateway_id = "${aws_customer_gateway.customer_gateway.id}"
type = "ipsec.1"
static_routes_only = false
resource "aws_vpn_connection" "foo" {
vpn_gateway_id = "${aws_vpn_gateway.vpn_gateway.id}"
customer_gateway_id = "${aws_customer_gateway.customer_gateway.id}"
type = "ipsec.1"
static_routes_only = false
tunnel1_inside_cidr = "169.254.8.0/30"
tunnel1_preshared_key = "lookatmethisisaprivatekey1"
tunnel1_inside_cidr = "%s"
tunnel1_preshared_key = "%s"
}
`, rBgpAsn, tunnelCidr, psk)
}

tunnel2_inside_cidr = "169.254.9.0/30"
tunnel2_preshared_key = "lookatmethisisaprivatekey2"
}
`, rBgpAsn)
func testAccAwsVpnConnectionConfigTunnelOptions(rBgpAsn int, psk string, tunnelCidr string, psk2 string, tunnelCidr2 string) string {
return fmt.Sprintf(`
resource "aws_vpn_gateway" "vpn_gateway" {
tags {
Name = "vpn_gateway"
}
}
resource "aws_customer_gateway" "customer_gateway" {
bgp_asn = %d
ip_address = "178.0.0.1"
type = "ipsec.1"
tags {
Name = "main-customer-gateway"
}
}
resource "aws_vpn_connection" "foo" {
vpn_gateway_id = "${aws_vpn_gateway.vpn_gateway.id}"
customer_gateway_id = "${aws_customer_gateway.customer_gateway.id}"
type = "ipsec.1"
static_routes_only = false
tunnel1_inside_cidr = "%s"
tunnel1_preshared_key = "%s"
tunnel2_inside_cidr = "%s"
tunnel2_preshared_key = "%s"
}
`, rBgpAsn, tunnelCidr, psk, tunnelCidr2, psk2)
}

// Test our VPN tunnel config XML parsing
7 changes: 7 additions & 0 deletions website/docs/r/vpn_connection.html.markdown
Original file line number Diff line number Diff line change
@@ -14,6 +14,9 @@ Provides a VPN connection connected to a VPC. These objects can be connected to
~> **Note:** All arguments including `tunnel1_preshared_key` and `tunnel2_preshared_key` will be stored in the raw state as plain-text.
[Read more about sensitive data in state](/docs/state/sensitive-data.html).

~> **Note:** The CIDR blocks in the arguments `tunnel1_inside_cidr` and `tunnel2_inside_cidr` must have a prefix of /30 and be a part of a specific range.
[Read more about this in the AWS documentation](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_VpnTunnelOptionsSpecification.html).

## Example Usage

```hcl
@@ -48,6 +51,10 @@ The following arguments are supported:
* `tags` - (Optional) Tags to apply to the connection.
* `type` - (Required) The type of VPN connection. The only type AWS supports at this time is "ipsec.1".
* `vpn_gateway_id` - (Required) The ID of the virtual private gateway.
* `tunnel1_inside_cidr` - (Optional) The CIDR block of the inside IP addresses for the first VPN tunnel.
* `tunnel2_inside_cidr` - (Optional) The CIDR block of the second IP addresses for the first VPN tunnel.
* `tunnel1_preshared_key` - (Optional) The preshared key of the first VPN tunnel.
* `tunnel2_preshared_key` - (Optional) The preshared key of the second VPN tunnel.

## Attribute Reference

0 comments on commit da3bce1

Please sign in to comment.