diff --git a/aws/resource_aws_vpn_connection.go b/aws/resource_aws_vpn_connection.go index 552c1d90f0a..7445e4bed41 100644 --- a/aws/resource_aws_vpn_connection.go +++ b/aws/resource_aws_vpn_connection.go @@ -105,6 +105,44 @@ func resourceAwsVpnConnection() *schema.Resource { ForceNew: true, }, + "enable_acceleration": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + ForceNew: true, + RequiredWith: []string{"transit_gateway_id"}, + }, + + "local_ipv4_network_cidr": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateLocalIpv4NetworkCidr(), + }, + + "local_ipv6_network_cidr": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateLocalIpv6NetworkCidr(), + RequiredWith: []string{"transit_gateway_id"}, + }, + + "remote_ipv4_network_cidr": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateLocalIpv4NetworkCidr(), + }, + + "remote_ipv6_network_cidr": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validateLocalIpv6NetworkCidr(), + RequiredWith: []string{"transit_gateway_id"}, + }, + "static_routes_only": { Type: schema.TypeBool, Optional: true, @@ -112,6 +150,104 @@ func resourceAwsVpnConnection() *schema.Resource { ForceNew: true, }, + "tunnel_inside_ip_version": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validateTunnelInsideIPVersion(), + }, + + "tunnel1_dpd_timeout_action": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateVpnConnectionTunnelDpdTimeoutAction(), + }, + + "tunnel1_dpd_timeout_seconds": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validateVpnConnectionTunnelDpdTimeoutSeconds(), + }, + + "tunnel1_ike_versions": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "tunnel1_phase1_dh_group_numbers": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeInt}, + }, + + "tunnel1_phase1_encryption_algorithms": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "tunnel1_phase1_integrity_algorithms": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "tunnel1_phase1_lifetime_seconds": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validateVpnConnectionTunnelPhase1LifetimeSeconds(), + }, + + "tunnel1_phase2_dh_group_numbers": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeInt}, + }, + + "tunnel1_phase2_encryption_algorithms": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "tunnel1_phase2_integrity_algorithms": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "tunnel1_phase2_lifetime_seconds": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validateVpnConnectionTunnelPhase2LifetimeSeconds(), + }, + + "tunnel1_rekey_fuzz_percentage": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validateVpnConnectionTunnelRekeyFuzzPercentage(), + }, + + "tunnel1_rekey_margin_time_seconds": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validateVpnConnectionTunnelRekeyMarginTimeSeconds(), + }, + + "tunnel1_replay_window_size": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validateVpnConnectionTunnelReplayWindowSize(), + }, + + "tunnel1_startup_action": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateVpnConnectionTunnelStartupAction(), + }, + "tunnel1_inside_cidr": { Type: schema.TypeString, Optional: true, @@ -120,6 +256,15 @@ func resourceAwsVpnConnection() *schema.Resource { ValidateFunc: validateVpnConnectionTunnelInsideCIDR(), }, + "tunnel1_inside_ipv6_cidr": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validateVpnConnectionTunnelInsideIpv6CIDR(), + RequiredWith: []string{"transit_gateway_id"}, + }, + "tunnel1_preshared_key": { Type: schema.TypeString, Optional: true, @@ -129,6 +274,96 @@ func resourceAwsVpnConnection() *schema.Resource { ValidateFunc: validateVpnConnectionTunnelPreSharedKey(), }, + "tunnel2_dpd_timeout_action": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateVpnConnectionTunnelDpdTimeoutAction(), + }, + + "tunnel2_dpd_timeout_seconds": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validateVpnConnectionTunnelDpdTimeoutSeconds(), + }, + + "tunnel2_ike_versions": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "tunnel2_phase1_dh_group_numbers": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeInt}, + }, + + "tunnel2_phase1_encryption_algorithms": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "tunnel2_phase1_integrity_algorithms": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "tunnel2_phase1_lifetime_seconds": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validateVpnConnectionTunnelPhase1LifetimeSeconds(), + }, + + "tunnel2_phase2_dh_group_numbers": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeInt}, + }, + + "tunnel2_phase2_encryption_algorithms": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "tunnel2_phase2_integrity_algorithms": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + + "tunnel2_phase2_lifetime_seconds": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validateVpnConnectionTunnelPhase2LifetimeSeconds(), + }, + + "tunnel2_rekey_fuzz_percentage": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validateVpnConnectionTunnelRekeyFuzzPercentage(), + }, + + "tunnel2_rekey_margin_time_seconds": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validateVpnConnectionTunnelRekeyMarginTimeSeconds(), + }, + + "tunnel2_replay_window_size": { + Type: schema.TypeInt, + Optional: true, + ValidateFunc: validateVpnConnectionTunnelReplayWindowSize(), + }, + + "tunnel2_startup_action": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateVpnConnectionTunnelStartupAction(), + }, + "tunnel2_inside_cidr": { Type: schema.TypeString, Optional: true, @@ -137,6 +372,15 @@ func resourceAwsVpnConnection() *schema.Resource { ValidateFunc: validateVpnConnectionTunnelInsideCIDR(), }, + "tunnel2_inside_ipv6_cidr": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validateVpnConnectionTunnelInsideIpv6CIDR(), + RequiredWith: []string{"transit_gateway_id"}, + }, + "tunnel2_preshared_key": { Type: schema.TypeString, Optional: true, @@ -272,31 +516,8 @@ func resourceAwsVpnConnection() *schema.Resource { func resourceAwsVpnConnectionCreate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ec2conn - // Fill the tunnel options for the EC2 API - options := []*ec2.VpnTunnelOptionsSpecification{ - {}, {}, - } - - if v, ok := d.GetOk("tunnel1_inside_cidr"); ok { - options[0].TunnelInsideCidr = aws.String(v.(string)) - } - - if v, ok := d.GetOk("tunnel2_inside_cidr"); ok { - options[1].TunnelInsideCidr = aws.String(v.(string)) - } - - if v, ok := d.GetOk("tunnel1_preshared_key"); ok { - options[0].PreSharedKey = aws.String(v.(string)) - } - - if v, ok := d.GetOk("tunnel2_preshared_key"); ok { - options[1].PreSharedKey = aws.String(v.(string)) - } - - connectOpts := &ec2.VpnConnectionOptionsSpecification{ - StaticRoutesOnly: aws.Bool(d.Get("static_routes_only").(bool)), - TunnelOptions: options, - } + // Fill the connection options for the EC2 API + connectOpts := expandVpnConnectionOptions(d) createOpts := &ec2.CreateVpnConnectionInput{ CustomerGatewayId: aws.String(d.Get("customer_gateway_id").(string)), @@ -436,12 +657,46 @@ func resourceAwsVpnConnectionRead(d *schema.ResourceData, meta interface{}) erro } if vpnConnection.Options != nil { + if err := d.Set("enable_acceleration", vpnConnection.Options.EnableAcceleration); err != nil { + return err + } + + if err := d.Set("local_ipv4_network_cidr", vpnConnection.Options.LocalIpv4NetworkCidr); err != nil { + return err + } + + if err := d.Set("local_ipv6_network_cidr", vpnConnection.Options.LocalIpv6NetworkCidr); err != nil { + return err + } + + if err := d.Set("remote_ipv4_network_cidr", vpnConnection.Options.RemoteIpv4NetworkCidr); err != nil { + return err + } + + if err := d.Set("remote_ipv6_network_cidr", vpnConnection.Options.RemoteIpv6NetworkCidr); err != nil { + return err + } + if err := d.Set("static_routes_only", vpnConnection.Options.StaticRoutesOnly); err != nil { return err } + + if err := d.Set("tunnel_inside_ip_version", vpnConnection.Options.TunnelInsideIpVersion); err != nil { + return err + } + if err := flattenTunnelOptions(d, vpnConnection); err != nil { + return err + } + } else { - //If there no Options on the connection then we do not support *static_routes* + //If there no Options on the connection then we do not support it + d.Set("enable_acceleration", false) + d.Set("local_ipv4_network_cidr", "") + d.Set("local_ipv6_network_cidr", "") + d.Set("remote_ipv4_network_cidr", "") + d.Set("remote_ipv6_network_cidr", "") d.Set("static_routes_only", false) + d.Set("tunnel_inside_ip_version", "") } // Set read only attributes. @@ -487,52 +742,508 @@ func resourceAwsVpnConnectionRead(d *schema.ResourceData, meta interface{}) erro return nil } -func resourceAwsVpnConnectionUpdate(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*AWSClient).ec2conn +func flattenTunnelOptions(d *schema.ResourceData, vpnConnection *ec2.VpnConnection) error { + if len(vpnConnection.Options.TunnelOptions) >= 1 { + if err := d.Set("tunnel1_dpd_timeout_action", vpnConnection.Options.TunnelOptions[0].DpdTimeoutAction); err != nil { + return err + } - if d.HasChange("tags") { - o, n := d.GetChange("tags") + if err := d.Set("tunnel1_dpd_timeout_seconds", vpnConnection.Options.TunnelOptions[0].DpdTimeoutSeconds); err != nil { + return err + } - if err := keyvaluetags.Ec2UpdateTags(conn, d.Id(), o, n); err != nil { - return fmt.Errorf("error updating EC2 VPN Connection (%s) tags: %s", d.Id(), err) + ikeVersions := []string{} + for _, ikeVersion := range vpnConnection.Options.TunnelOptions[0].IkeVersions { + ikeVersions = append(ikeVersions, *ikeVersion.Value) + } + if err := d.Set("tunnel1_ike_versions", ikeVersions); err != nil { + return err } - } - return resourceAwsVpnConnectionRead(d, meta) -} + phase1DHGroupNumbers := []int64{} + for _, phase1DHGroupNumber := range vpnConnection.Options.TunnelOptions[0].Phase1DHGroupNumbers { + phase1DHGroupNumbers = append(phase1DHGroupNumbers, *phase1DHGroupNumber.Value) + } + if err := d.Set("tunnel1_phase1_dh_group_numbers", phase1DHGroupNumbers); err != nil { + return err + } -func resourceAwsVpnConnectionDelete(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*AWSClient).ec2conn + phase1EncAlgorithms := []string{} + for _, phase1EncAlgorithm := range vpnConnection.Options.TunnelOptions[0].Phase1EncryptionAlgorithms { + phase1EncAlgorithms = append(phase1EncAlgorithms, *phase1EncAlgorithm.Value) + } + if err := d.Set("tunnel1_phase1_encryption_algorithms", phase1EncAlgorithms); err != nil { + return err + } - _, err := conn.DeleteVpnConnection(&ec2.DeleteVpnConnectionInput{ - VpnConnectionId: aws.String(d.Id()), - }) + phase1IntegrityAlgorithms := []string{} + for _, phase1IntegrityAlgorithm := range vpnConnection.Options.TunnelOptions[0].Phase1IntegrityAlgorithms { + phase1IntegrityAlgorithms = append(phase1IntegrityAlgorithms, *phase1IntegrityAlgorithm.Value) + } + if err := d.Set("tunnel1_phase1_integrity_algorithms", phase1IntegrityAlgorithms); err != nil { + return err + } - if isAWSErr(err, "InvalidVpnConnectionID.NotFound", "") { - return nil - } + if err := d.Set("tunnel1_phase1_lifetime_seconds", vpnConnection.Options.TunnelOptions[0].Phase1LifetimeSeconds); err != nil { + return err + } - if err != nil { - return fmt.Errorf("error deleting VPN Connection (%s): %s", d.Id(), err) - } + phase2DHGroupNumbers := []int64{} + for _, phase2DHGroupNumber := range vpnConnection.Options.TunnelOptions[0].Phase2DHGroupNumbers { + phase2DHGroupNumbers = append(phase2DHGroupNumbers, *phase2DHGroupNumber.Value) + } + if err := d.Set("tunnel1_phase2_dh_group_numbers", phase2DHGroupNumbers); err != nil { + return err + } - if err := waitForEc2VpnConnectionDeletion(conn, d.Id()); err != nil { - return fmt.Errorf("error waiting for VPN connection (%s) to delete: %s", d.Id(), err) - } + phase2EncAlgorithms := []string{} + for _, phase2EncAlgorithm := range vpnConnection.Options.TunnelOptions[0].Phase2EncryptionAlgorithms { + phase2EncAlgorithms = append(phase2EncAlgorithms, *phase2EncAlgorithm.Value) + } + if err := d.Set("tunnel1_phase2_encryption_algorithms", phase2EncAlgorithms); err != nil { + return err + } - return nil -} + phase2IntegrityAlgorithms := []string{} + for _, phase2IntegrityAlgorithm := range vpnConnection.Options.TunnelOptions[0].Phase2IntegrityAlgorithms { + phase2IntegrityAlgorithms = append(phase2IntegrityAlgorithms, *phase2IntegrityAlgorithm.Value) + } + if err := d.Set("tunnel1_phase2_integrity_algorithms", phase2IntegrityAlgorithms); err != nil { + return err + } -// routesToMapList turns the list of routes into a list of maps. -func routesToMapList(routes []*ec2.VpnStaticRoute) []map[string]interface{} { - result := make([]map[string]interface{}, 0, len(routes)) - for _, r := range routes { - staticRoute := make(map[string]interface{}) - staticRoute["destination_cidr_block"] = aws.StringValue(r.DestinationCidrBlock) - staticRoute["state"] = aws.StringValue(r.State) + if err := d.Set("tunnel1_phase2_lifetime_seconds", vpnConnection.Options.TunnelOptions[0].Phase2LifetimeSeconds); err != nil { + return err + } - if r.Source != nil { - staticRoute["source"] = aws.StringValue(r.Source) + if err := d.Set("tunnel1_rekey_fuzz_percentage", vpnConnection.Options.TunnelOptions[0].RekeyFuzzPercentage); err != nil { + return err + } + + if err := d.Set("tunnel1_rekey_margin_time_seconds", vpnConnection.Options.TunnelOptions[0].RekeyMarginTimeSeconds); err != nil { + return err + } + + if err := d.Set("tunnel1_replay_window_size", vpnConnection.Options.TunnelOptions[0].ReplayWindowSize); err != nil { + return err + } + + if err := d.Set("tunnel1_startup_action", vpnConnection.Options.TunnelOptions[0].StartupAction); err != nil { + return err + } + + if err := d.Set("tunnel1_inside_cidr", vpnConnection.Options.TunnelOptions[0].TunnelInsideCidr); err != nil { + return err + } + + if err := d.Set("tunnel1_inside_ipv6_cidr", vpnConnection.Options.TunnelOptions[0].TunnelInsideIpv6Cidr); err != nil { + return err + } + } + if len(vpnConnection.Options.TunnelOptions) >= 2 { + if err := d.Set("tunnel2_dpd_timeout_action", vpnConnection.Options.TunnelOptions[1].DpdTimeoutAction); err != nil { + return err + } + + if err := d.Set("tunnel2_dpd_timeout_seconds", vpnConnection.Options.TunnelOptions[1].DpdTimeoutSeconds); err != nil { + return err + } + + ikeVersions := []string{} + for _, ikeVersion := range vpnConnection.Options.TunnelOptions[1].IkeVersions { + ikeVersions = append(ikeVersions, *ikeVersion.Value) + } + if err := d.Set("tunnel2_ike_versions", ikeVersions); err != nil { + return err + } + + phase1DHGroupNumbers := []int64{} + for _, phase1DHGroupNumber := range vpnConnection.Options.TunnelOptions[1].Phase1DHGroupNumbers { + phase1DHGroupNumbers = append(phase1DHGroupNumbers, *phase1DHGroupNumber.Value) + } + if err := d.Set("tunnel2_phase1_dh_group_numbers", phase1DHGroupNumbers); err != nil { + return err + } + + phase1EncAlgorithms := []string{} + for _, phase1EncAlgorithm := range vpnConnection.Options.TunnelOptions[1].Phase1EncryptionAlgorithms { + phase1EncAlgorithms = append(phase1EncAlgorithms, *phase1EncAlgorithm.Value) + } + + if err := d.Set("tunnel2_phase1_encryption_algorithms", phase1EncAlgorithms); err != nil { + return err + } + + phase1IntegrityAlgorithms := []string{} + for _, phase1IntegrityAlgorithm := range vpnConnection.Options.TunnelOptions[1].Phase1IntegrityAlgorithms { + phase1IntegrityAlgorithms = append(phase1IntegrityAlgorithms, *phase1IntegrityAlgorithm.Value) + } + if err := d.Set("tunnel2_phase1_integrity_algorithms", phase1IntegrityAlgorithms); err != nil { + return err + } + + if err := d.Set("tunnel2_phase1_lifetime_seconds", vpnConnection.Options.TunnelOptions[1].Phase1LifetimeSeconds); err != nil { + return err + } + + phase2DHGroupNumbers := []int64{} + for _, phase2DHGroupNumber := range vpnConnection.Options.TunnelOptions[1].Phase2DHGroupNumbers { + phase2DHGroupNumbers = append(phase2DHGroupNumbers, *phase2DHGroupNumber.Value) + } + if err := d.Set("tunnel2_phase2_dh_group_numbers", phase2DHGroupNumbers); err != nil { + return err + } + + phase2EncAlgorithms := []string{} + for _, phase2EncAlgorithm := range vpnConnection.Options.TunnelOptions[1].Phase2EncryptionAlgorithms { + phase2EncAlgorithms = append(phase2EncAlgorithms, *phase2EncAlgorithm.Value) + } + + if err := d.Set("tunnel2_phase2_encryption_algorithms", phase2EncAlgorithms); err != nil { + return err + } + + phase2IntegrityAlgorithms := []string{} + for _, phase2IntegrityAlgorithm := range vpnConnection.Options.TunnelOptions[1].Phase2IntegrityAlgorithms { + phase2IntegrityAlgorithms = append(phase2IntegrityAlgorithms, *phase2IntegrityAlgorithm.Value) + } + if err := d.Set("tunnel2_phase2_integrity_algorithms", phase2IntegrityAlgorithms); err != nil { + return err + } + + if err := d.Set("tunnel2_phase2_lifetime_seconds", vpnConnection.Options.TunnelOptions[1].Phase2LifetimeSeconds); err != nil { + return err + } + + if err := d.Set("tunnel2_rekey_fuzz_percentage", vpnConnection.Options.TunnelOptions[1].RekeyFuzzPercentage); err != nil { + return err + } + + if err := d.Set("tunnel2_rekey_margin_time_seconds", vpnConnection.Options.TunnelOptions[1].RekeyMarginTimeSeconds); err != nil { + return err + } + + if err := d.Set("tunnel2_replay_window_size", vpnConnection.Options.TunnelOptions[1].ReplayWindowSize); err != nil { + return err + } + + if err := d.Set("tunnel2_startup_action", vpnConnection.Options.TunnelOptions[1].StartupAction); err != nil { + return err + } + + if err := d.Set("tunnel2_inside_cidr", vpnConnection.Options.TunnelOptions[1].TunnelInsideCidr); err != nil { + return err + } + + if err := d.Set("tunnel2_inside_ipv6_cidr", vpnConnection.Options.TunnelOptions[1].TunnelInsideIpv6Cidr); err != nil { + return err + } + } + return nil +} + +func resourceAwsVpnConnectionUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + if err := modifyVpnConnectionOptions(d, conn); err != nil { + return err + } + + if err := modifyVpnTunnels(d, conn); err != nil { + return err + } + + if d.HasChange("tags") { + o, n := d.GetChange("tags") + vpnConnectionID := d.Id() + + if err := keyvaluetags.Ec2UpdateTags(conn, vpnConnectionID, o, n); err != nil { + return fmt.Errorf("error updating EC2 VPN Connection (%s) tags: %s", d.Id(), err) + } + } + + return resourceAwsVpnConnectionRead(d, meta) +} + +func resourceAwsVpnConnectionDelete(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + + _, err := conn.DeleteVpnConnection(&ec2.DeleteVpnConnectionInput{ + VpnConnectionId: aws.String(d.Id()), + }) + + if isAWSErr(err, "InvalidVpnConnectionID.NotFound", "") { + return nil + } + + if err != nil { + return fmt.Errorf("error deleting VPN Connection (%s): %s", d.Id(), err) + } + + if err := waitForEc2VpnConnectionDeletion(conn, d.Id()); err != nil { + return fmt.Errorf("error waiting for VPN connection (%s) to delete: %s", d.Id(), err) + } + + return nil +} + +func expandVpnConnectionOptions(d *schema.ResourceData) *ec2.VpnConnectionOptionsSpecification { + var connectOpts *ec2.VpnConnectionOptionsSpecification = new(ec2.VpnConnectionOptionsSpecification) + ipv := d.Get("tunnel_inside_ip_version").(string) + if ipv == "ipv6" { + if v, ok := d.GetOk("local_ipv6_network_cidr"); ok { + connectOpts.LocalIpv6NetworkCidr = aws.String(v.(string)) + } + + if v, ok := d.GetOk("remote_ipv6_network_cidr"); ok { + connectOpts.RemoteIpv6NetworkCidr = aws.String(v.(string)) + } + + connectOpts.TunnelInsideIpVersion = aws.String(ipv) + } else { + if v, ok := d.GetOk("local_ipv4_network_cidr"); ok { + connectOpts.LocalIpv4NetworkCidr = aws.String(v.(string)) + } + + if v, ok := d.GetOk("remote_ipv4_network_cidr"); ok { + connectOpts.RemoteIpv4NetworkCidr = aws.String(v.(string)) + } + + connectOpts.TunnelInsideIpVersion = aws.String("ipv4") + } + + if v, ok := d.GetOk("enable_acceleration"); ok { + connectOpts.EnableAcceleration = aws.Bool(v.(bool)) + } + + if v, ok := d.GetOk("static_routes_only"); ok { + connectOpts.StaticRoutesOnly = aws.Bool(v.(bool)) + } + + // Fill the tunnel options for the EC2 API + connectOpts.TunnelOptions = expandVpnTunnelOptions(d) + + return connectOpts +} + +func expandVpnTunnelOptions(d *schema.ResourceData) []*ec2.VpnTunnelOptionsSpecification { + options := []*ec2.VpnTunnelOptionsSpecification{ + {}, {}, + } + + if v, ok := d.GetOk("tunnel1_dpd_timeout_action"); ok { + options[0].DPDTimeoutAction = aws.String(v.(string)) + } + + if v, ok := d.GetOk("tunnel2_dpd_timeout_action"); ok { + options[1].DPDTimeoutAction = aws.String(v.(string)) + } + + if v, ok := d.GetOk("tunnel1_dpd_timeout_seconds"); ok { + options[0].DPDTimeoutSeconds = aws.Int64(int64(v.(int))) + } + + if v, ok := d.GetOk("tunnel2_dpd_timeout_seconds"); ok { + options[1].DPDTimeoutSeconds = aws.Int64(int64(v.(int))) + } + + if v, ok := d.GetOk("tunnel1_ike_versions"); ok { + l := []*ec2.IKEVersionsRequestListValue{} + for _, s := range v.(*schema.Set).List() { + l = append(l, &ec2.IKEVersionsRequestListValue{Value: aws.String(s.(string))}) + } + options[0].IKEVersions = l + } + + if v, ok := d.GetOk("tunnel2_ike_versions"); ok { + l := []*ec2.IKEVersionsRequestListValue{} + for _, s := range v.(*schema.Set).List() { + l = append(l, &ec2.IKEVersionsRequestListValue{Value: aws.String(s.(string))}) + } + options[1].IKEVersions = l + } + + if v, ok := d.GetOk("tunnel1_phase1_dh_group_numbers"); ok { + l := []*ec2.Phase1DHGroupNumbersRequestListValue{} + for _, s := range v.(*schema.Set).List() { + l = append(l, &ec2.Phase1DHGroupNumbersRequestListValue{Value: aws.Int64(int64(s.(int)))}) + } + options[0].Phase1DHGroupNumbers = l + } + + if v, ok := d.GetOk("tunnel2_phase1_dh_group_numbers"); ok { + l := []*ec2.Phase1DHGroupNumbersRequestListValue{} + for _, s := range v.(*schema.Set).List() { + l = append(l, &ec2.Phase1DHGroupNumbersRequestListValue{Value: aws.Int64(int64(s.(int)))}) + } + options[1].Phase1DHGroupNumbers = l + } + + if v, ok := d.GetOk("tunnel1_phase1_encryption_algorithms"); ok { + l := []*ec2.Phase1EncryptionAlgorithmsRequestListValue{} + for _, s := range v.(*schema.Set).List() { + l = append(l, &ec2.Phase1EncryptionAlgorithmsRequestListValue{Value: aws.String(s.(string))}) + } + options[0].Phase1EncryptionAlgorithms = l + } + + if v, ok := d.GetOk("tunnel2_phase1_encryption_algorithms"); ok { + l := []*ec2.Phase1EncryptionAlgorithmsRequestListValue{} + for _, s := range v.(*schema.Set).List() { + l = append(l, &ec2.Phase1EncryptionAlgorithmsRequestListValue{Value: aws.String(s.(string))}) + } + options[1].Phase1EncryptionAlgorithms = l + } + + if v, ok := d.GetOk("tunnel1_phase1_integrity_algorithms"); ok { + l := []*ec2.Phase1IntegrityAlgorithmsRequestListValue{} + for _, s := range v.(*schema.Set).List() { + l = append(l, &ec2.Phase1IntegrityAlgorithmsRequestListValue{Value: aws.String(s.(string))}) + } + options[0].Phase1IntegrityAlgorithms = l + } + + if v, ok := d.GetOk("tunnel2_phase1_integrity_algorithms"); ok { + l := []*ec2.Phase1IntegrityAlgorithmsRequestListValue{} + for _, s := range v.(*schema.Set).List() { + l = append(l, &ec2.Phase1IntegrityAlgorithmsRequestListValue{Value: aws.String(s.(string))}) + } + options[1].Phase1IntegrityAlgorithms = l + } + + if v, ok := d.GetOk("tunnel1_phase1_lifetime_seconds"); ok { + options[0].Phase1LifetimeSeconds = aws.Int64(int64(v.(int))) + } + + if v, ok := d.GetOk("tunnel2_phase1_lifetime_seconds"); ok { + options[1].Phase1LifetimeSeconds = aws.Int64(int64(v.(int))) + } + + if v, ok := d.GetOk("tunnel1_phase2_dh_group_numbers"); ok { + l := []*ec2.Phase2DHGroupNumbersRequestListValue{} + for _, s := range v.(*schema.Set).List() { + l = append(l, &ec2.Phase2DHGroupNumbersRequestListValue{Value: aws.Int64(int64(s.(int)))}) + } + options[0].Phase2DHGroupNumbers = l + } + + if v, ok := d.GetOk("tunnel2_phase2_dh_group_numbers"); ok { + l := []*ec2.Phase2DHGroupNumbersRequestListValue{} + for _, s := range v.(*schema.Set).List() { + l = append(l, &ec2.Phase2DHGroupNumbersRequestListValue{Value: aws.Int64(int64(s.(int)))}) + } + options[1].Phase2DHGroupNumbers = l + } + + if v, ok := d.GetOk("tunnel1_phase2_encryption_algorithms"); ok { + l := []*ec2.Phase2EncryptionAlgorithmsRequestListValue{} + for _, s := range v.(*schema.Set).List() { + l = append(l, &ec2.Phase2EncryptionAlgorithmsRequestListValue{Value: aws.String(s.(string))}) + } + options[0].Phase2EncryptionAlgorithms = l + } + + if v, ok := d.GetOk("tunnel2_phase2_encryption_algorithms"); ok { + l := []*ec2.Phase2EncryptionAlgorithmsRequestListValue{} + for _, s := range v.(*schema.Set).List() { + l = append(l, &ec2.Phase2EncryptionAlgorithmsRequestListValue{Value: aws.String(s.(string))}) + } + options[1].Phase2EncryptionAlgorithms = l + } + + if v, ok := d.GetOk("tunnel1_phase2_integrity_algorithms"); ok { + l := []*ec2.Phase2IntegrityAlgorithmsRequestListValue{} + for _, s := range v.(*schema.Set).List() { + l = append(l, &ec2.Phase2IntegrityAlgorithmsRequestListValue{Value: aws.String(s.(string))}) + } + options[0].Phase2IntegrityAlgorithms = l + } + + if v, ok := d.GetOk("tunnel2_phase2_integrity_algorithms"); ok { + l := []*ec2.Phase2IntegrityAlgorithmsRequestListValue{} + for _, s := range v.(*schema.Set).List() { + l = append(l, &ec2.Phase2IntegrityAlgorithmsRequestListValue{Value: aws.String(s.(string))}) + } + options[1].Phase2IntegrityAlgorithms = l + } + + if v, ok := d.GetOk("tunnel1_phase2_lifetime_seconds"); ok { + options[0].Phase2LifetimeSeconds = aws.Int64(int64(v.(int))) + } + + if v, ok := d.GetOk("tunnel2_phase2_lifetime_seconds"); ok { + options[1].Phase2LifetimeSeconds = aws.Int64(int64(v.(int))) + } + + if v, ok := d.GetOk("tunnel1_rekey_fuzz_percentage"); ok { + options[0].RekeyFuzzPercentage = aws.Int64(int64(v.(int))) + } + + if v, ok := d.GetOk("tunnel2_rekey_fuzz_percentage"); ok { + options[1].RekeyFuzzPercentage = aws.Int64(int64(v.(int))) + } + + if v, ok := d.GetOk("tunnel1_rekey_margin_time_seconds"); ok { + options[0].RekeyMarginTimeSeconds = aws.Int64(int64(v.(int))) + } + + if v, ok := d.GetOk("tunnel2_rekey_margin_time_seconds"); ok { + options[1].RekeyMarginTimeSeconds = aws.Int64(int64(v.(int))) + } + + if v, ok := d.GetOk("tunnel1_replay_window_size"); ok { + options[0].ReplayWindowSize = aws.Int64(int64(v.(int))) + } + + if v, ok := d.GetOk("tunnel2_replay_window_size"); ok { + options[1].ReplayWindowSize = aws.Int64(int64(v.(int))) + } + + if v, ok := d.GetOk("tunnel1_startup_action"); ok { + options[0].StartupAction = aws.String(v.(string)) + } + + if v, ok := d.GetOk("tunnel2_startup_action"); ok { + options[1].StartupAction = aws.String(v.(string)) + } + + if v, ok := d.GetOk("tunnel1_inside_cidr"); ok { + options[0].TunnelInsideCidr = aws.String(v.(string)) + } + + if v, ok := d.GetOk("tunnel2_inside_cidr"); ok { + options[1].TunnelInsideCidr = aws.String(v.(string)) + } + + if v, ok := d.GetOk("tunnel1_inside_ipv6_cidr"); ok { + options[0].TunnelInsideIpv6Cidr = aws.String(v.(string)) + } + + if v, ok := d.GetOk("tunnel2_inside_ipv6_cidr"); ok { + options[1].TunnelInsideIpv6Cidr = aws.String(v.(string)) + } + + if v, ok := d.GetOk("tunnel1_preshared_key"); ok { + options[0].PreSharedKey = aws.String(v.(string)) + } + + if v, ok := d.GetOk("tunnel2_preshared_key"); ok { + options[1].PreSharedKey = aws.String(v.(string)) + } + + return options +} + +// routesToMapList turns the list of routes into a list of maps. +func routesToMapList(routes []*ec2.VpnStaticRoute) []map[string]interface{} { + result := make([]map[string]interface{}, 0, len(routes)) + for _, r := range routes { + staticRoute := make(map[string]interface{}) + staticRoute["destination_cidr_block"] = aws.StringValue(r.DestinationCidrBlock) + staticRoute["state"] = aws.StringValue(r.State) + + if r.Source != nil { + staticRoute["source"] = aws.StringValue(r.Source) } result = append(result, staticRoute) @@ -560,6 +1271,302 @@ func telemetryToMapList(telemetry []*ec2.VgwTelemetry) []map[string]interface{} return result } +func modifyVpnConnectionOptions(d *schema.ResourceData, conn *ec2.EC2) error { + var connOpts *ec2.ModifyVpnConnectionOptionsInput = new(ec2.ModifyVpnConnectionOptionsInput) + connChanged := false + + vpnConnectionID := d.Id() + + if d.HasChange("local_ipv4_network_cidr") { + connChanged = true + connOpts.LocalIpv4NetworkCidr = aws.String(d.Get("local_ipv4_network_cidr").(string)) + } + + if d.HasChange("local_ipv6_network_cidr") { + connChanged = true + connOpts.LocalIpv6NetworkCidr = aws.String(d.Get("local_ipv6_network_cidr").(string)) + } + + if d.HasChange("remote_ipv4_network_cidr") { + connChanged = true + connOpts.RemoteIpv4NetworkCidr = aws.String(d.Get("remote_ipv4_network_cidr").(string)) + } + + if d.HasChange("remote_ipv6_network_cidr") { + connChanged = true + connOpts.RemoteIpv6NetworkCidr = aws.String(d.Get("remote_ipv6_network_cidr").(string)) + } + + if connChanged { + connOpts.VpnConnectionId = aws.String(vpnConnectionID) + _, err := conn.ModifyVpnConnectionOptions(connOpts) + if err != nil { + return fmt.Errorf("Error modifying vpn connection options: %s", err) + } + + if err := waitForEc2VpnConnectionAvailableWhenModifying(conn, vpnConnectionID); err != nil { + return fmt.Errorf("error waiting for VPN connection (%s) to become available: %s", vpnConnectionID, err) + } + } + + return nil +} + +func modifyVpnTunnels(d *schema.ResourceData, conn *ec2.EC2) error { + tun1Changed := false + tun2Changed := false + vgwTelemetryTun1Index := 0 + vgwTelemetryTun2Index := 1 + options := []*ec2.ModifyVpnTunnelOptionsSpecification{ + {}, {}, + } + + vpnConnectionID := d.Id() + + if d.HasChange("tunnel1_dpd_timeout_action") { + tun1Changed = true + options[0].DPDTimeoutAction = aws.String(d.Get("tunnel1_dpd_timeout_action").(string)) + } + + if d.HasChange("tunnel2_dpd_timeout_action") { + tun2Changed = true + options[1].DPDTimeoutAction = aws.String(d.Get("tunnel2_dpd_timeout_action").(string)) + } + + if d.HasChange("tunnel1_dpd_timeout_seconds") { + tun1Changed = true + options[0].DPDTimeoutSeconds = aws.Int64(int64(d.Get("tunnel1_dpd_timeout_seconds").(int))) + } + + if d.HasChange("tunnel2_dpd_timeout_seconds") { + tun2Changed = true + options[1].DPDTimeoutSeconds = aws.Int64(int64(d.Get("tunnel2_dpd_timeout_seconds").(int))) + } + + if d.HasChange("tunnel1_ike_versions") { + tun1Changed = true + l := []*ec2.IKEVersionsRequestListValue{} + for _, s := range d.Get("tunnel1_ike_versions").(*schema.Set).List() { + l = append(l, &ec2.IKEVersionsRequestListValue{Value: aws.String(s.(string))}) + } + options[0].IKEVersions = l + } + + if d.HasChange("tunnel2_ike_versions") { + tun2Changed = true + l := []*ec2.IKEVersionsRequestListValue{} + for _, s := range d.Get("tunnel2_ike_versions").(*schema.Set).List() { + l = append(l, &ec2.IKEVersionsRequestListValue{Value: aws.String(s.(string))}) + } + options[1].IKEVersions = l + } + + if d.HasChange("tunnel1_phase1_dh_group_numbers") { + tun1Changed = true + l := []*ec2.Phase1DHGroupNumbersRequestListValue{} + for _, s := range d.Get("tunnel1_phase1_dh_group_numbers").(*schema.Set).List() { + l = append(l, &ec2.Phase1DHGroupNumbersRequestListValue{Value: aws.Int64(int64(s.(int)))}) + } + options[0].Phase1DHGroupNumbers = l + } + + if d.HasChange("tunnel2_phase1_dh_group_numbers") { + tun2Changed = true + l := []*ec2.Phase1DHGroupNumbersRequestListValue{} + for _, s := range d.Get("tunnel2_phase1_dh_group_numbers").(*schema.Set).List() { + l = append(l, &ec2.Phase1DHGroupNumbersRequestListValue{Value: aws.Int64(int64(s.(int)))}) + } + options[1].Phase1DHGroupNumbers = l + } + + if d.HasChange("tunnel1_phase1_encryption_algorithms") { + tun1Changed = true + l := []*ec2.Phase1EncryptionAlgorithmsRequestListValue{} + for _, s := range d.Get("tunnel1_phase1_encryption_algorithms").(*schema.Set).List() { + l = append(l, &ec2.Phase1EncryptionAlgorithmsRequestListValue{Value: aws.String(s.(string))}) + } + options[0].Phase1EncryptionAlgorithms = l + } + + if d.HasChange("tunnel2_phase1_encryption_algorithms") { + tun2Changed = true + l := []*ec2.Phase1EncryptionAlgorithmsRequestListValue{} + for _, s := range d.Get("tunnel2_phase1_encryption_algorithms").(*schema.Set).List() { + l = append(l, &ec2.Phase1EncryptionAlgorithmsRequestListValue{Value: aws.String(s.(string))}) + } + options[1].Phase1EncryptionAlgorithms = l + } + + if d.HasChange("tunnel1_phase1_integrity_algorithms") { + tun1Changed = true + l := []*ec2.Phase1IntegrityAlgorithmsRequestListValue{} + for _, s := range d.Get("tunnel1_phase1_integrity_algorithms").(*schema.Set).List() { + l = append(l, &ec2.Phase1IntegrityAlgorithmsRequestListValue{Value: aws.String(s.(string))}) + } + options[0].Phase1IntegrityAlgorithms = l + } + + if d.HasChange("tunnel2_phase1_integrity_algorithms") { + tun2Changed = true + l := []*ec2.Phase1IntegrityAlgorithmsRequestListValue{} + for _, s := range d.Get("tunnel2_phase1_integrity_algorithms").(*schema.Set).List() { + l = append(l, &ec2.Phase1IntegrityAlgorithmsRequestListValue{Value: aws.String(s.(string))}) + } + options[1].Phase1IntegrityAlgorithms = l + } + + if d.HasChange("tunnel1_phase1_lifetime_seconds") { + tun1Changed = true + options[0].Phase1LifetimeSeconds = aws.Int64(int64(d.Get("tunnel1_phase1_lifetime_seconds").(int))) + } + + if d.HasChange("tunnel2_phase1_lifetime_seconds") { + tun2Changed = true + options[1].Phase1LifetimeSeconds = aws.Int64(int64(d.Get("tunnel2_phase1_lifetime_seconds").(int))) + } + + if d.HasChange("tunnel1_phase2_dh_group_numbers") { + tun1Changed = true + l := []*ec2.Phase2DHGroupNumbersRequestListValue{} + for _, s := range d.Get("tunnel1_phase2_dh_group_numbers").(*schema.Set).List() { + l = append(l, &ec2.Phase2DHGroupNumbersRequestListValue{Value: aws.Int64(int64(s.(int)))}) + } + options[0].Phase2DHGroupNumbers = l + } + + if d.HasChange("tunnel2_phase2_dh_group_numbers") { + tun2Changed = true + l := []*ec2.Phase2DHGroupNumbersRequestListValue{} + for _, s := range d.Get("tunnel2_phase2_dh_group_numbers").(*schema.Set).List() { + l = append(l, &ec2.Phase2DHGroupNumbersRequestListValue{Value: aws.Int64(int64(s.(int)))}) + } + options[1].Phase2DHGroupNumbers = l + } + + if d.HasChange("tunnel1_phase2_encryption_algorithms") { + tun1Changed = true + l := []*ec2.Phase2EncryptionAlgorithmsRequestListValue{} + for _, s := range d.Get("tunnel1_phase2_encryption_algorithms").(*schema.Set).List() { + l = append(l, &ec2.Phase2EncryptionAlgorithmsRequestListValue{Value: aws.String(s.(string))}) + } + options[0].Phase2EncryptionAlgorithms = l + } + + if d.HasChange("tunnel2_phase2_encryption_algorithms") { + tun2Changed = true + l := []*ec2.Phase2EncryptionAlgorithmsRequestListValue{} + for _, s := range d.Get("tunnel2_phase2_encryption_algorithms").(*schema.Set).List() { + l = append(l, &ec2.Phase2EncryptionAlgorithmsRequestListValue{Value: aws.String(s.(string))}) + } + options[1].Phase2EncryptionAlgorithms = l + } + + if d.HasChange("tunnel1_phase2_integrity_algorithms") { + tun1Changed = true + l := []*ec2.Phase2IntegrityAlgorithmsRequestListValue{} + for _, s := range d.Get("tunnel1_phase2_integrity_algorithms").(*schema.Set).List() { + l = append(l, &ec2.Phase2IntegrityAlgorithmsRequestListValue{Value: aws.String(s.(string))}) + } + options[0].Phase2IntegrityAlgorithms = l + } + + if d.HasChange("tunnel2_phase2_integrity_algorithms") { + tun2Changed = true + l := []*ec2.Phase2IntegrityAlgorithmsRequestListValue{} + for _, s := range d.Get("tunnel2_phase2_integrity_algorithms").(*schema.Set).List() { + l = append(l, &ec2.Phase2IntegrityAlgorithmsRequestListValue{Value: aws.String(s.(string))}) + } + options[1].Phase2IntegrityAlgorithms = l + } + + if d.HasChange("tunnel1_phase2_lifetime_seconds") { + tun1Changed = true + options[0].Phase2LifetimeSeconds = aws.Int64(int64(d.Get("tunnel1_phase2_lifetime_seconds").(int))) + } + + if d.HasChange("tunnel2_phase2_lifetime_seconds") { + tun2Changed = true + options[1].Phase2LifetimeSeconds = aws.Int64(int64(d.Get("tunnel2_phase2_lifetime_seconds").(int))) + } + + if d.HasChange("tunnel1_rekey_fuzz_percentage") { + tun1Changed = true + options[0].RekeyFuzzPercentage = aws.Int64(int64(d.Get("tunnel1_rekey_fuzz_percentage").(int))) + } + + if d.HasChange("tunnel2_rekey_fuzz_percentage") { + tun2Changed = true + options[1].RekeyFuzzPercentage = aws.Int64(int64(d.Get("tunnel2_rekey_fuzz_percentage").(int))) + } + + if d.HasChange("tunnel1_rekey_margin_time_seconds") { + tun1Changed = true + options[0].RekeyMarginTimeSeconds = aws.Int64(int64(d.Get("tunnel1_rekey_margin_time_seconds").(int))) + } + + if d.HasChange("tunnel2_rekey_margin_time_seconds") { + tun2Changed = true + options[1].RekeyMarginTimeSeconds = aws.Int64(int64(d.Get("tunnel2_rekey_margin_time_seconds").(int))) + } + + if d.HasChange("tunnel1_replay_window_size") { + tun1Changed = true + options[0].ReplayWindowSize = aws.Int64(int64(d.Get("tunnel1_replay_window_size").(int))) + } + + if d.HasChange("tunnel2_replay_window_size") { + tun2Changed = true + options[1].ReplayWindowSize = aws.Int64(int64(d.Get("tunnel2_replay_window_size").(int))) + } + + if d.HasChange("tunnel1_startup_action") { + tun1Changed = true + options[0].StartupAction = aws.String(d.Get("tunnel1_startup_action").(string)) + } + + if d.HasChange("tunnel2_startup_action") { + tun2Changed = true + options[1].StartupAction = aws.String(d.Get("tunnel2_startup_action").(string)) + } + + if tun1Changed { + if err := modifyVpnTunnelOptions(conn, d.Get("vgw_telemetry").(*schema.Set), vpnConnectionID, vgwTelemetryTun1Index, options[0]); err != nil { + return err + } + } + + if tun2Changed { + if err := modifyVpnTunnelOptions(conn, d.Get("vgw_telemetry").(*schema.Set), vpnConnectionID, vgwTelemetryTun2Index, options[1]); err != nil { + return err + } + } + + return nil +} + +func modifyVpnTunnelOptions(conn *ec2.EC2, vgwTelemetry *schema.Set, vpnConnectionID string, vgwTelemetryTunIndex int, optionsTun *ec2.ModifyVpnTunnelOptionsSpecification) error { + if v := vgwTelemetry; v.Len() > 0 { + vpnTunnelOutsideIPAddress := v.List()[vgwTelemetryTunIndex].(map[string]interface{})["outside_ip_address"].(string) + + o := &ec2.ModifyVpnTunnelOptionsInput{ + VpnConnectionId: aws.String(vpnConnectionID), + VpnTunnelOutsideIpAddress: aws.String(vpnTunnelOutsideIPAddress), + TunnelOptions: optionsTun, + } + + _, err := conn.ModifyVpnTunnelOptions(o) + if err != nil { + return fmt.Errorf("Error modifying vpn tunnel options: %s", err) + } + + if err := waitForEc2VpnConnectionAvailableWhenModifying(conn, vpnConnectionID); err != nil { + return fmt.Errorf("error waiting for VPN connection (%s) to become available: %s", vpnConnectionID, err) + } + } + + return nil +} + func waitForEc2VpnConnectionAvailable(conn *ec2.EC2, id string) error { // Wait for the connection to become available. This has an obscenely // high default timeout because AWS VPN connections are notoriously @@ -579,6 +1586,25 @@ func waitForEc2VpnConnectionAvailable(conn *ec2.EC2, id string) error { return err } +func waitForEc2VpnConnectionAvailableWhenModifying(conn *ec2.EC2, id string) error { + // Wait for the connection to become available. This has an obscenely + // high default timeout because AWS VPN connections are notoriously + // slow at coming up or going down. There's also no point in checking + // more frequently than every ten seconds. + stateConf := &resource.StateChangeConf{ + Pending: []string{"modifying"}, // VPN state modifying const is not available in SDK + Target: []string{ec2.VpnStateAvailable}, + Refresh: vpnConnectionRefreshFunc(conn, id), + Timeout: 40 * time.Minute, + Delay: 10 * time.Second, + MinTimeout: 10 * time.Second, + } + + _, err := stateConf.WaitForState() + + return err +} + func waitForEc2VpnConnectionDeletion(conn *ec2.EC2, id string) error { // These things can take quite a while to tear themselves down and any // attempt to modify resources they reference (e.g. CustomerGateways or @@ -636,6 +1662,7 @@ func validateVpnConnectionTunnelPreSharedKey() schema.SchemaValidateFunc { } // https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_VpnTunnelOptionsSpecification.html +// https://docs.aws.amazon.com/vpn/latest/s2svpn/VPNTunnels.html func validateVpnConnectionTunnelInsideCIDR() schema.SchemaValidateFunc { disallowedCidrs := []string{ "169.254.0.0/30", @@ -653,3 +1680,93 @@ func validateVpnConnectionTunnelInsideCIDR() schema.SchemaValidateFunc { validation.StringNotInSlice(disallowedCidrs, false), ) } + +func validateVpnConnectionTunnelInsideIpv6CIDR() schema.SchemaValidateFunc { + return validation.All( + validation.IsCIDRNetwork(126, 126), + validation.StringMatch(regexp.MustCompile(`^fd00:`), "must be within fd00::/8"), + ) +} + +func validateLocalIpv4NetworkCidr() schema.SchemaValidateFunc { + return validation.All( + validation.IsCIDRNetwork(32, 32), + ) +} + +func validateLocalIpv6NetworkCidr() schema.SchemaValidateFunc { + return validation.All( + validation.IsCIDRNetwork(128, 128), + ) +} + +func validateVpnConnectionTunnelDpdTimeoutAction() schema.SchemaValidateFunc { + allowedDpdTimeoutActions := []string{ + "clear", + "none", + "restart", + } + + return validation.All( + validation.StringInSlice(allowedDpdTimeoutActions, false), + ) +} + +func validateTunnelInsideIPVersion() schema.SchemaValidateFunc { + allowedIPVersions := []string{ + "ipv4", + "ipv6", + } + + return validation.All( + validation.StringInSlice(allowedIPVersions, false), + ) +} + +func validateVpnConnectionTunnelDpdTimeoutSeconds() schema.SchemaValidateFunc { + return validation.All( + //validation.IntBetween(0, 30) + validation.IntAtLeast(30), // Must be 30 or higher + ) +} + +func validateVpnConnectionTunnelPhase1LifetimeSeconds() schema.SchemaValidateFunc { + return validation.All( + validation.IntBetween(900, 28800), + ) +} + +func validateVpnConnectionTunnelPhase2LifetimeSeconds() schema.SchemaValidateFunc { + return validation.All( + validation.IntBetween(900, 3600), + ) +} + +func validateVpnConnectionTunnelRekeyFuzzPercentage() schema.SchemaValidateFunc { + return validation.All( + validation.IntBetween(0, 100), + ) +} + +func validateVpnConnectionTunnelRekeyMarginTimeSeconds() schema.SchemaValidateFunc { + return validation.All( + validation.IntBetween(60, 1800), + ) +} + +func validateVpnConnectionTunnelReplayWindowSize() schema.SchemaValidateFunc { + return validation.All( + validation.IntBetween(64, 2048), + ) +} + +func validateVpnConnectionTunnelStartupAction() schema.SchemaValidateFunc { + allowedStartupAction := []string{ + "add", + "start", + } + + return validation.All( + validation.StringInSlice(allowedStartupAction, false), + ) +} diff --git a/aws/resource_aws_vpn_connection_test.go b/aws/resource_aws_vpn_connection_test.go index 3648fe7e64c..e94ba820444 100644 --- a/aws/resource_aws_vpn_connection_test.go +++ b/aws/resource_aws_vpn_connection_test.go @@ -14,6 +14,26 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) +type TunnelOptions struct { + psk string + tunnelCidr string + dpdTimeoutAction string + dpdTimeoutSeconds int + ikeVersions string + phase1DhGroupNumbers string + phase1EncryptionAlgorithms string + phase1IntegrityAlgorithms string + phase1LifetimeSeconds int + phase2DhGroupNumbers string + phase2EncryptionAlgorithms string + phase2IntegrityAlgorithms string + phase2LifetimeSeconds int + rekeyFuzzPercentage int + rekeyMarginTimeSeconds int + replayWindowSize int + startupAction string +} + func init() { resource.AddTestSweepers("aws_vpn_connection", &resource.Sweeper{ Name: "aws_vpn_connection", @@ -88,6 +108,7 @@ func TestAccAWSVpnConnection_basic(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccAwsVpnConnectionExists(resourceName, &vpn), resource.TestCheckResourceAttr(resourceName, "transit_gateway_attachment_id", ""), + resource.TestCheckResourceAttr(resourceName, "enable_acceleration", "false"), testAccMatchResourceAttrRegionalARN(resourceName, "arn", "ec2", regexp.MustCompile(`vpn-connection/vpn-.+`)), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), ), @@ -144,6 +165,46 @@ func TestAccAWSVpnConnection_tunnelOptions(t *testing.T) { resourceName := "aws_vpn_connection.test" var vpn ec2.VpnConnection + tunnel1 := TunnelOptions{ + psk: "12345678", + tunnelCidr: "169.254.8.0/30", + dpdTimeoutAction: "clear", + dpdTimeoutSeconds: 30, + ikeVersions: "\"ikev1\", \"ikev2\"", + phase1DhGroupNumbers: "2, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24", + phase1EncryptionAlgorithms: "\"AES128\", \"AES256\", \"AES128-GCM-16\", \"AES256-GCM-16\"", + phase1IntegrityAlgorithms: "\"SHA1\", \"SHA2-256\", \"SHA2-384\", \"SHA2-512\"", + phase1LifetimeSeconds: 28800, + phase2DhGroupNumbers: "2, 5, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24", + phase2EncryptionAlgorithms: "\"AES128\", \"AES256\", \"AES128-GCM-16\", \"AES256-GCM-16\"", + phase2IntegrityAlgorithms: "\"SHA1\", \"SHA2-256\", \"SHA2-384\", \"SHA2-512\"", + phase2LifetimeSeconds: 3600, + rekeyFuzzPercentage: 100, + rekeyMarginTimeSeconds: 540, + replayWindowSize: 1024, + startupAction: "add", + } + + tunnel2 := TunnelOptions{ + psk: "abcdefgh", + tunnelCidr: "169.254.9.0/30", + dpdTimeoutAction: "clear", + dpdTimeoutSeconds: 30, + ikeVersions: "\"ikev1\", \"ikev2\"", + phase1DhGroupNumbers: "2, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24", + phase1EncryptionAlgorithms: "\"AES128\", \"AES256\", \"AES128-GCM-16\", \"AES256-GCM-16\"", + phase1IntegrityAlgorithms: "\"SHA1\", \"SHA2-256\", \"SHA2-384\", \"SHA2-512\"", + phase1LifetimeSeconds: 28800, + phase2DhGroupNumbers: "2, 5, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24", + phase2EncryptionAlgorithms: "\"AES128\", \"AES256\", \"AES128-GCM-16\", \"AES256-GCM-16\"", + phase2IntegrityAlgorithms: "\"SHA1\", \"SHA2-256\", \"SHA2-384\", \"SHA2-512\"", + phase2LifetimeSeconds: 3600, + rekeyFuzzPercentage: 100, + rekeyMarginTimeSeconds: 540, + replayWindowSize: 1024, + startupAction: "add", + } + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, IDRefreshName: resourceName, @@ -210,9 +271,26 @@ func TestAccAWSVpnConnection_tunnelOptions(t *testing.T) { ExpectError: regexp.MustCompile(`can only contain alphanumeric, period and underscore characters`), }, + // Should pre-check: + // - local_ipv4_network_cidr + // - local_ipv6_network_cidr + // - remote_ipv4_network_cidr + // - remote_ipv6_network_cidr + // - tunnel_inside_ip_version + // - tunnel1_dpd_timeout_action + // - tunnel1_dpd_timeout_seconds + // - tunnel1_phase1_lifetime_seconds + // - tunnel1_phase2_lifetime_seconds + // - tunnel1_rekey_fuzz_percentage + // - tunnel1_rekey_margin_time_seconds + // - tunnel1_replay_window_size + // - tunnel1_startup_action + // - tunnel1_inside_cidr + // - tunnel1_inside_ipv6_cidr + //Try actual building { - Config: testAccAwsVpnConnectionConfigTunnelOptions(rBgpAsn, "12345678", "169.254.8.0/30", "abcdefgh", "169.254.9.0/30"), + Config: testAccAwsVpnConnectionConfigTunnelOptions(rBgpAsn, "192.168.1.1/32", "192.168.1.2/32", tunnel1, tunnel2), Check: resource.ComposeTestCheckFunc( testAccAwsVpnConnectionExists(resourceName, &vpn), resource.TestCheckResourceAttr(resourceName, "static_routes_only", "false"), @@ -246,6 +324,60 @@ func TestAccAWSVpnConnection_withoutStaticRoutes(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccAwsVpnConnectionExists(resourceName, &vpn), resource.TestCheckResourceAttr(resourceName, "static_routes_only", "false"), + resource.TestCheckResourceAttr(resourceName, "enable_acceleration", "false"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSVpnConnection_withEnableAcceleration(t *testing.T) { + rBgpAsn := acctest.RandIntRange(64512, 65534) + resourceName := "aws_vpn_connection.test" + var vpn ec2.VpnConnection + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + IDRefreshName: resourceName, + Providers: testAccProviders, + CheckDestroy: testAccAwsVpnConnectionDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsVpnConnectionConfigEnableAcceleration(rBgpAsn), + Check: resource.ComposeTestCheckFunc( + testAccAwsVpnConnectionExists(resourceName, &vpn), + resource.TestCheckResourceAttr(resourceName, "enable_acceleration", "true"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSVpnConnection_withIpv6(t *testing.T) { + rBgpAsn := acctest.RandIntRange(64512, 65534) + resourceName := "aws_vpn_connection.test" + var vpn ec2.VpnConnection + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + IDRefreshName: resourceName, + Providers: testAccProviders, + CheckDestroy: testAccAwsVpnConnectionDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAwsVpnConnectionConfigIpv6(rBgpAsn, "fd00:2001:db8:2:2d1:81ff:fe41:d201/128", "fd00:2001:db8:2:2d1:81ff:fe41:d202/128", "fd00:2001:db8:2:2d1:81ff:fe41:d200/126", "fd00:2001:db8:2:2d1:81ff:fe41:d204/126"), + Check: resource.ComposeTestCheckFunc( + testAccAwsVpnConnectionExists(resourceName, &vpn), ), }, { @@ -497,6 +629,55 @@ resource "aws_vpn_connection" "test" { `, rBgpAsn, rInt) } +func testAccAwsVpnConnectionConfigEnableAcceleration(rBgpAsn int) string { + return fmt.Sprintf(` +resource "aws_ec2_transit_gateway" "test" {} +resource "aws_customer_gateway" "customer_gateway" { + bgp_asn = %d + ip_address = "178.0.0.1" + type = "ipsec.1" + tags = { + Name = "tf-acc-test-ec2-vpn-connection-enable-acceleration" + } +} +resource "aws_vpn_connection" "test" { + customer_gateway_id = aws_customer_gateway.customer_gateway.id + transit_gateway_id = aws_ec2_transit_gateway.test.id + type = "ipsec.1" + static_routes_only = false + enable_acceleration = true +} +`, rBgpAsn) +} + +func testAccAwsVpnConnectionConfigIpv6(rBgpAsn int, localIpv6NetworkCidr string, remoteIpv6NetworkCidr string, tunnel1InsideIpv6Cidr string, tunnel2InsideIpv6Cidr string) string { + return fmt.Sprintf(` +resource "aws_ec2_transit_gateway" "test" {} +resource "aws_customer_gateway" "customer_gateway" { + bgp_asn = %d + ip_address = "178.0.0.1" + type = "ipsec.1" + tags = { + Name = "tf-acc-test-ec2-vpn-connection-enable-acceleration" + } +} +resource "aws_vpn_connection" "test" { + customer_gateway_id = aws_customer_gateway.customer_gateway.id + transit_gateway_id = aws_ec2_transit_gateway.test.id + type = "ipsec.1" + static_routes_only = false + enable_acceleration = false + + local_ipv6_network_cidr = %[2]q + remote_ipv6_network_cidr = %[3]q + tunnel_inside_ip_version = "ipv6" + + tunnel1_inside_ipv6_cidr = %[4]q + tunnel2_inside_ipv6_cidr = %[5]q +} +`, rBgpAsn, localIpv6NetworkCidr, remoteIpv6NetworkCidr, tunnel1InsideIpv6Cidr, tunnel2InsideIpv6Cidr) +} + func testAccAwsVpnConnectionConfigSingleTunnelOptions(rBgpAsn int, psk string, tunnelCidr string) string { return fmt.Sprintf(` resource "aws_vpn_gateway" "vpn_gateway" { @@ -549,7 +730,13 @@ resource "aws_vpn_connection" "test" { `, rBgpAsn) } -func testAccAwsVpnConnectionConfigTunnelOptions(rBgpAsn int, psk string, tunnelCidr string, psk2 string, tunnelCidr2 string) string { +func testAccAwsVpnConnectionConfigTunnelOptions( + rBgpAsn int, + localIpv4NetworkCidr string, + remoteIpv4NetworkCidr string, + tunnel1 TunnelOptions, + tunnel2 TunnelOptions, +) string { return fmt.Sprintf(` resource "aws_vpn_gateway" "vpn_gateway" { tags = { @@ -573,13 +760,83 @@ resource "aws_vpn_connection" "test" { 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) + local_ipv4_network_cidr = %[2]q + remote_ipv4_network_cidr = %[3]q + + tunnel1_inside_cidr = %[4]q + tunnel1_preshared_key = %[5]q + tunnel1_dpd_timeout_action = %[6]q + tunnel1_dpd_timeout_seconds = %[7]d + tunnel1_ike_versions = [%[8]s] + tunnel1_phase1_dh_group_numbers = [%[9]s] + tunnel1_phase1_encryption_algorithms = [%[10]s] + tunnel1_phase1_integrity_algorithms = [%[11]s] + tunnel1_phase1_lifetime_seconds = %[12]d + tunnel1_phase2_dh_group_numbers = [%[13]s] + tunnel1_phase2_encryption_algorithms = [%[14]s] + tunnel1_phase2_integrity_algorithms = [%[15]s] + tunnel1_phase2_lifetime_seconds = %[16]d + tunnel1_rekey_fuzz_percentage = %[17]d + tunnel1_rekey_margin_time_seconds = %[18]d + tunnel1_replay_window_size = %[19]d + tunnel1_startup_action = %[20]q + + tunnel2_inside_cidr = %[21]q + tunnel2_preshared_key = %[22]q + tunnel2_dpd_timeout_action = %[23]q + tunnel2_dpd_timeout_seconds = %[24]d + tunnel2_ike_versions = [%[25]s] + tunnel2_phase1_dh_group_numbers = [%[26]s] + tunnel2_phase1_encryption_algorithms = [%[27]s] + tunnel2_phase1_integrity_algorithms = [%[28]s] + tunnel2_phase1_lifetime_seconds = %[29]d + tunnel2_phase2_dh_group_numbers = [%[30]s] + tunnel2_phase2_encryption_algorithms = [%[31]s] + tunnel2_phase2_integrity_algorithms = [%[32]s] + tunnel2_phase2_lifetime_seconds = %[33]d + tunnel2_rekey_fuzz_percentage = %[34]d + tunnel2_rekey_margin_time_seconds = %[35]d + tunnel2_replay_window_size = %[36]d + tunnel2_startup_action = %[37]q +} +`, + rBgpAsn, + localIpv4NetworkCidr, + remoteIpv4NetworkCidr, + tunnel1.tunnelCidr, + tunnel1.psk, + tunnel1.dpdTimeoutAction, + tunnel1.dpdTimeoutSeconds, + tunnel1.ikeVersions, + tunnel1.phase1DhGroupNumbers, + tunnel1.phase1EncryptionAlgorithms, + tunnel1.phase1IntegrityAlgorithms, + tunnel1.phase1LifetimeSeconds, + tunnel1.phase2DhGroupNumbers, + tunnel1.phase2EncryptionAlgorithms, + tunnel1.phase2IntegrityAlgorithms, + tunnel1.phase2LifetimeSeconds, + tunnel1.rekeyFuzzPercentage, + tunnel1.rekeyMarginTimeSeconds, + tunnel1.replayWindowSize, + tunnel1.startupAction, + tunnel2.tunnelCidr, + tunnel2.psk, + tunnel2.dpdTimeoutAction, + tunnel2.dpdTimeoutSeconds, + tunnel2.ikeVersions, + tunnel2.phase1DhGroupNumbers, + tunnel2.phase1EncryptionAlgorithms, + tunnel2.phase1IntegrityAlgorithms, + tunnel2.phase1LifetimeSeconds, + tunnel2.phase2DhGroupNumbers, + tunnel2.phase2EncryptionAlgorithms, + tunnel2.phase2IntegrityAlgorithms, + tunnel2.phase2LifetimeSeconds, + tunnel2.rekeyFuzzPercentage, + tunnel2.rekeyMarginTimeSeconds, + tunnel2.replayWindowSize, + tunnel2.startupAction) } func testAccAwsVpnConnectionConfigTags1(rBgpAsn int, tagKey1, tagValue1 string) string { diff --git a/website/docs/r/vpn_connection.html.markdown b/website/docs/r/vpn_connection.html.markdown index d74d406744b..c9b046e07cb 100644 --- a/website/docs/r/vpn_connection.html.markdown +++ b/website/docs/r/vpn_connection.html.markdown @@ -76,13 +76,49 @@ One of the following arguments is required: Other arguments: * `static_routes_only` - (Optional, Default `false`) Whether the VPN connection uses static routes exclusively. Static routes must be used for devices that don't support BGP. +* `enable_acceleration` - (Optional, Default `false`) Indicate whether to enable acceleration for the VPN connection. Supports only EC2 Transit Gateway. * `tags` - (Optional) Tags to apply to the connection. -* `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 inside IP addresses for the second 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. - -~> **Note:** The preshared key must be between 8 and 64 characters in length and cannot start with zero(0). Allowed characters are alphanumeric characters, periods(.) and underscores(_). +* `local_ipv4_network_cidr` - (Optional, Default `0.0.0.0/0`) The IPv4 CIDR on the customer gateway (on-premises) side of the VPN connection. +* `local_ipv6_network_cidr` - (Optional, Default `::/0`) The IPv6 CIDR on the customer gateway (on-premises) side of the VPN connection. +* `remote_ipv4_network_cidr` - (Optional, Default `0.0.0.0/0`) The IPv4 CIDR on the AWS side of the VPN connection. +* `remote_ipv6_network_cidr` - (Optional, Default `::/0`) The IPv6 CIDR on the customer gateway (on-premises) side of the VPN connection. +* `tunnel_inside_ip_version` - (Optional, Default `ipv4`) Indicate whether the VPN tunnels process IPv4 or IPv6 traffic. Valid values are `ipv4 | ipv6`. `ipv6` Supports only EC2 Transit Gateway. +* `tunnel1_inside_cidr` - (Optional) The CIDR block of the inside IP addresses for the first VPN tunnel. Valid value is a size /30 CIDR block from the 169.254.0.0/16 range. +* `tunnel2_inside_cidr` - (Optional) The CIDR block of the inside IP addresses for the second VPN tunnel. Valid value is a size /30 CIDR block from the 169.254.0.0/16 range. +* `tunnel1_inside_ipv6_cidr` - (Optional) The range of inside IPv6 addresses for the first VPN tunnel. Supports only EC2 Transit Gateway. Valid value is a size /126 CIDR block from the local fd00::/8 range. +* `tunnel2_inside_ipv6_cidr` - (Optional) The range of inside IPv6 addresses for the second VPN tunnel. Supports only EC2 Transit Gateway. Valid value is a size /126 CIDR block from the local fd00::/8 range. +* `tunnel1_preshared_key` - (Optional) The preshared key of the first VPN tunnel. The preshared key must be between 8 and 64 characters in length and cannot start with zero(0). Allowed characters are alphanumeric characters, periods(.) and underscores(_). +* `tunnel2_preshared_key` - (Optional) The preshared key of the second VPN tunnel. The preshared key must be between 8 and 64 characters in length and cannot start with zero(0). Allowed characters are alphanumeric characters, periods(.) and underscores(_). +* `tunnel1_dpd_timeout_action` - (Optional, Default `clear`) The action to take after DPD timeout occurs for the first VPN tunnel. Specify restart to restart the IKE initiation. Specify clear to end the IKE session. Valid values are `clear | none | restart`. +* `tunnel2_dpd_timeout_action` - (Optional, Default `clear`) The action to take after DPD timeout occurs for the second VPN tunnel. Specify restart to restart the IKE initiation. Specify clear to end the IKE session. Valid values are `clear | none | restart`. +* `tunnel1_dpd_timeout_seconds` - (Optional, Default `30`) The number of seconds after which a DPD timeout occurs for the first VPN tunnel. Valid value is equal or higher than `30`. +* `tunnel2_dpd_timeout_seconds` - (Optional, Default `30`) The number of seconds after which a DPD timeout occurs for the second VPN tunnel. Valid value is equal or higher than `30`. +* `tunnel1_ike_versions` - (Optional) The IKE versions that are permitted for the first VPN tunnel. Valid values are `ikev1 | ikev2`. +* `tunnel2_ike_versions` - (Optional) The IKE versions that are permitted for the second VPN tunnel. Valid values are `ikev1 | ikev2`. +* `tunnel1_phase1_dh_group_numbers` - (Optional) List of one or more Diffie-Hellman group numbers that are permitted for the first VPN tunnel for phase 1 IKE negotiations. Valid values are ` 2 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24`. +* `tunnel2_phase1_dh_group_numbers` - (Optional) List of one or more Diffie-Hellman group numbers that are permitted for the second VPN tunnel for phase 1 IKE negotiations. Valid values are ` 2 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24`. +* `tunnel1_phase1_encryption_algorithms` - (Optional) List of one or more encryption algorithms that are permitted for the first VPN tunnel for phase 1 IKE negotiations. Valid values are `AES128 | AES256 | AES128-GCM-16 | AES256-GCM-16`. +* `tunnel2_phase1_encryption_algorithms` - (Optional) List of one or more encryption algorithms that are permitted for the second VPN tunnel for phase 1 IKE negotiations. Valid values are `AES128 | AES256 | AES128-GCM-16 | AES256-GCM-16`. +* `tunnel1_phase1_integrity_algorithms` - (Optional) One or more integrity algorithms that are permitted for the first VPN tunnel for phase 1 IKE negotiations. Valid values are `SHA1 | SHA2-256 | SHA2-384 | SHA2-512`. +* `tunnel2_phase1_integrity_algorithms` - (Optional) One or more integrity algorithms that are permitted for the second VPN tunnel for phase 1 IKE negotiations. Valid values are `SHA1 | SHA2-256 | SHA2-384 | SHA2-512`. +* `tunnel1_phase1_lifetime_seconds` - (Optional, Default `28800`) The lifetime for phase 1 of the IKE negotiation for the first VPN tunnel, in seconds. Valid value is between `900` and `28800`. +* `tunnel2_phase1_lifetime_seconds` - (Optional, Default `28800`) The lifetime for phase 1 of the IKE negotiation for the second VPN tunnel, in seconds. Valid value is between `900` and `28800`. +* `tunnel1_phase2_dh_group_numbers` - (Optional) List of one or more Diffie-Hellman group numbers that are permitted for the first VPN tunnel for phase 2 IKE negotiations. Valid values are `2 | 5 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24`. +* `tunnel2_phase2_dh_group_numbers` - (Optional) List of one or more Diffie-Hellman group numbers that are permitted for the second VPN tunnel for phase 2 IKE negotiations. Valid values are `2 | 5 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24`. +* `tunnel1_phase2_encryption_algorithms` - (Optional) List of one or more encryption algorithms that are permitted for the first VPN tunnel for phase 2 IKE negotiations. Valid values are `AES128 | AES256 | AES128-GCM-16 | AES256-GCM-16`. +* `tunnel2_phase2_encryption_algorithms` - (Optional) List of one or more encryption algorithms that are permitted for the second VPN tunnel for phase 2 IKE negotiations. Valid values are `AES128 | AES256 | AES128-GCM-16 | AES256-GCM-16`. +* `tunnel1_phase2_integrity_algorithms` - (Optional) List of one or more integrity algorithms that are permitted for the first VPN tunnel for phase 2 IKE negotiations. Valid values are `SHA1 | SHA2-256 | SHA2-384 | SHA2-512`. +* `tunnel2_phase2_integrity_algorithms` - (Optional) List of one or more integrity algorithms that are permitted for the second VPN tunnel for phase 2 IKE negotiations. Valid values are `SHA1 | SHA2-256 | SHA2-384 | SHA2-512`. +* `tunnel1_phase2_lifetime_seconds` - (Optional, Default `3600`) The lifetime for phase 2 of the IKE negotiation for the first VPN tunnel, in seconds. Valid value is between `900` and `3600`. +* `tunnel2_phase2_lifetime_seconds` - (Optional, Default `3600`) The lifetime for phase 2 of the IKE negotiation for the second VPN tunnel, in seconds. Valid value is between `900` and `3600`. +* `tunnel1_rekey_fuzz_percentage` - (Optional, Default `100`) The percentage of the rekey window for the first VPN tunnel (determined by `tunnel1_rekey_margin_time_seconds`) during which the rekey time is randomly selected. Valid value is between `0` and `100`. +* `tunnel2_rekey_fuzz_percentage` - (Optional, Default `100`) The percentage of the rekey window for the second VPN tunnel (determined by `tunnel2_rekey_margin_time_seconds`) during which the rekey time is randomly selected. Valid value is between `0` and `100`. +* `tunnel1_rekey_margin_time_seconds` - (Optional, Default `540`) The margin time, in seconds, before the phase 2 lifetime expires, during which the AWS side of the first VPN connection performs an IKE rekey. The exact time of the rekey is randomly selected based on the value for `tunnel1_rekey_fuzz_percentage`. Valid value is between `60` and half of `tunnel1_phase2_lifetime_seconds`. +* `tunnel2_rekey_margin_time_seconds` - (Optional, Default `540`) The margin time, in seconds, before the phase 2 lifetime expires, during which the AWS side of the second VPN connection performs an IKE rekey. The exact time of the rekey is randomly selected based on the value for `tunnel2_rekey_fuzz_percentage`. Valid value is between `60` and half of `tunnel2_phase2_lifetime_seconds`. +* `tunnel1_replay_window_size` - (Optional, Default `1024`) The number of packets in an IKE replay window for the first VPN tunnel. Valid value is between `64` and `2048`. +* `tunnel2_replay_window_size` - (Optional, Default `1024`) The number of packets in an IKE replay window for the second VPN tunnel. Valid value is between `64` and `2048`. +* `tunnel1_startup_action` - (Optional, Default `add`) The action to take when the establishing the tunnel for the first VPN connection. By default, your customer gateway device must initiate the IKE negotiation and bring up the tunnel. Specify start for AWS to initiate the IKE negotiation. Valid values are `add | start`. +* `tunnel2_startup_action` - (Optional, Default `add`) The action to take when the establishing the tunnel for the second VPN connection. By default, your customer gateway device must initiate the IKE negotiation and bring up the tunnel. Specify start for AWS to initiate the IKE negotiation. Valid values are `add | start`. ## Attributes Reference