From 3fada75cc71b19681d70863cc710c533f4c74de9 Mon Sep 17 00:00:00 2001 From: Marco Rinalducci Date: Tue, 18 Aug 2020 16:22:21 +0200 Subject: [PATCH 1/3] Add vpn options to aws_vpn_connection resource --- aws/resource_aws_vpn_connection.go | 893 +++++++++++++++++++- aws/resource_aws_vpn_connection_test.go | 285 ++++++- website/docs/r/vpn_connection.html.markdown | 48 +- 3 files changed, 1206 insertions(+), 20 deletions(-) diff --git a/aws/resource_aws_vpn_connection.go b/aws/resource_aws_vpn_connection.go index 552c1d90f0a..8ba13db3e22 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, @@ -277,6 +521,182 @@ func resourceAwsVpnConnectionCreate(d *schema.ResourceData, meta interface{}) er {}, {}, } + 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)) } @@ -285,6 +705,14 @@ func resourceAwsVpnConnectionCreate(d *schema.ResourceData, meta interface{}) er 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)) } @@ -293,11 +721,40 @@ func resourceAwsVpnConnectionCreate(d *schema.ResourceData, meta interface{}) er options[1].PreSharedKey = aws.String(v.(string)) } - connectOpts := &ec2.VpnConnectionOptionsSpecification{ - StaticRoutesOnly: aws.Bool(d.Get("static_routes_only").(bool)), - TunnelOptions: options, + 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)) + } + + connectOpts.TunnelOptions = options + createOpts := &ec2.CreateVpnConnectionInput{ CustomerGatewayId: aws.String(d.Get("customer_gateway_id").(string)), Options: connectOpts, @@ -436,12 +893,42 @@ 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 + } } 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. @@ -490,10 +977,273 @@ func resourceAwsVpnConnectionRead(d *schema.ResourceData, meta interface{}) erro func resourceAwsVpnConnectionUpdate(d *schema.ResourceData, meta interface{}) error { conn := meta.(*AWSClient).ec2conn + tun1Changed := false + tun2Changed := false + vgwTelemetryTun1Index := 0 + vgwTelemetryTun2Index := 1 + options := []*ec2.ModifyVpnTunnelOptionsSpecification{ + {}, {}, + } + + 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) + } + } + + 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 + } + } + if d.HasChange("tags") { o, n := d.GetChange("tags") - if err := keyvaluetags.Ec2UpdateTags(conn, d.Id(), o, n); err != nil { + if err := keyvaluetags.Ec2UpdateTags(conn, vpnConnectionID, o, n); err != nil { return fmt.Errorf("error updating EC2 VPN Connection (%s) tags: %s", d.Id(), err) } } @@ -560,6 +1310,29 @@ func telemetryToMapList(telemetry []*ec2.VgwTelemetry) []map[string]interface{} return result } +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 +1352,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 +1428,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 +1446,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..0f4977f4190 100644 --- a/aws/resource_aws_vpn_connection_test.go +++ b/aws/resource_aws_vpn_connection_test.go @@ -88,6 +88,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"), ), @@ -210,9 +211,63 @@ 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", + "12345678", + "169.254.8.0/30", + "clear", + 30, + "\"ikev1\", \"ikev2\"", + "2, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24", + "\"AES128\", \"AES256\", \"AES128-GCM-16\", \"AES256-GCM-16\"", + "\"SHA1\", \"SHA2-256\", \"SHA2-384\", \"SHA2-512\"", + 28800, + "2, 5, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24", + "\"AES128\", \"AES256\", \"AES128-GCM-16\", \"AES256-GCM-16\"", + "\"SHA1\", \"SHA2-256\", \"SHA2-384\", \"SHA2-512\"", + 3600, + 100, + 540, + 1024, + "add", + "abcdefgh", + "169.254.9.0/30", + "clear", + 30, + "\"ikev1\", \"ikev2\"", + "2, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24", + "\"AES128\", \"AES256\", \"AES128-GCM-16\", \"AES256-GCM-16\"", + "\"SHA1\", \"SHA2-256\", \"SHA2-384\", \"SHA2-512\"", + 28800, + "2, 5, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24", + "\"AES128\", \"AES256\", \"AES128-GCM-16\", \"AES256-GCM-16\"", + "\"SHA1\", \"SHA2-256\", \"SHA2-384\", \"SHA2-512\"", + 3600, + 100, + 540, + 1024, + "add"), Check: resource.ComposeTestCheckFunc( testAccAwsVpnConnectionExists(resourceName, &vpn), resource.TestCheckResourceAttr(resourceName, "static_routes_only", "false"), @@ -246,6 +301,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), ), }, { @@ -493,10 +602,60 @@ resource "aws_vpn_connection" "test" { customer_gateway_id = aws_customer_gateway.customer_gateway.id type = "ipsec.1" static_routes_only = false + enable_acceleration = false } `, 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 = "%s" + remote_ipv6_network_cidr = "%s" + tunnel_inside_ip_version = "ipv6" + + tunnel1_inside_ipv6_cidr = "%s" + tunnel2_inside_ipv6_cidr = "%s" +} +`, 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 +708,45 @@ 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, + 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, + psk2 string, + tunnelCidr2 string, + dpdTimeoutAction2 string, + dpdTimeoutSeconds2 int, + ikeVersions2 string, + phase1DhGroupNumbers2 string, + phase1EncryptionAlgorithms2 string, + phase1IntegrityAlgorithms2 string, + phase1LifetimeSeconds2 int, + phase2DhGroupNumbers2 string, + phase2EncryptionAlgorithms2 string, + phase2IntegrityAlgorithms2 string, + phase2LifetimeSeconds2 int, + rekeyFuzzPercentage2 int, + rekeyMarginTimeSeconds2 int, + replayWindowSize2 int, + startupAction2 string, +) string { return fmt.Sprintf(` resource "aws_vpn_gateway" "vpn_gateway" { tags = { @@ -573,13 +770,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 = "%s" + remote_ipv4_network_cidr = "%s" + + tunnel1_inside_cidr = "%s" + tunnel1_preshared_key = "%s" + tunnel1_dpd_timeout_action = "%s" + tunnel1_dpd_timeout_seconds = %d + tunnel1_ike_versions = [%s] + tunnel1_phase1_dh_group_numbers = [%s] + tunnel1_phase1_encryption_algorithms = [%s] + tunnel1_phase1_integrity_algorithms = [%s] + tunnel1_phase1_lifetime_seconds = %d + tunnel1_phase2_dh_group_numbers = [%s] + tunnel1_phase2_encryption_algorithms = [%s] + tunnel1_phase2_integrity_algorithms = [%s] + tunnel1_phase2_lifetime_seconds = %d + tunnel1_rekey_fuzz_percentage = %d + tunnel1_rekey_margin_time_seconds = %d + tunnel1_replay_window_size = %d + tunnel1_startup_action = "%s" + + tunnel2_inside_cidr = "%s" + tunnel2_preshared_key = "%s" + tunnel2_dpd_timeout_action = "%s" + tunnel2_dpd_timeout_seconds = %d + tunnel2_ike_versions = [%s] + tunnel2_phase1_dh_group_numbers = [%s] + tunnel2_phase1_encryption_algorithms = [%s] + tunnel2_phase1_integrity_algorithms = [%s] + tunnel2_phase1_lifetime_seconds = %d + tunnel2_phase2_dh_group_numbers = [%s] + tunnel2_phase2_encryption_algorithms = [%s] + tunnel2_phase2_integrity_algorithms = [%s] + tunnel2_phase2_lifetime_seconds = %d + tunnel2_rekey_fuzz_percentage = %d + tunnel2_rekey_margin_time_seconds = %d + tunnel2_replay_window_size = %d + tunnel2_startup_action = "%s" +} +`, + rBgpAsn, + localIpv4NetworkCidr, + remoteIpv4NetworkCidr, + tunnelCidr, + psk, + dpdTimeoutAction, + dpdTimeoutSeconds, + ikeVersions, + phase1DhGroupNumbers, + phase1EncryptionAlgorithms, + phase1IntegrityAlgorithms, + phase1LifetimeSeconds, + phase2DhGroupNumbers, + phase2EncryptionAlgorithms, + phase2IntegrityAlgorithms, + phase2LifetimeSeconds, + rekeyFuzzPercentage, + rekeyMarginTimeSeconds, + replayWindowSize, + startupAction, + tunnelCidr2, + psk2, + dpdTimeoutAction2, + dpdTimeoutSeconds2, + ikeVersions2, + phase1DhGroupNumbers2, + phase1EncryptionAlgorithms2, + phase1IntegrityAlgorithms2, + phase1LifetimeSeconds2, + phase2DhGroupNumbers2, + phase2EncryptionAlgorithms2, + phase2IntegrityAlgorithms2, + phase2LifetimeSeconds2, + rekeyFuzzPercentage2, + rekeyMarginTimeSeconds2, + replayWindowSize2, + startupAction2) } 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 From de14cad5c045dfaa94cbb25c94946b6a3541d6cd Mon Sep 17 00:00:00 2001 From: Marco Rinalducci Date: Sat, 12 Dec 2020 17:32:44 +0100 Subject: [PATCH 2/3] Code better broken up into functions and added struct for testing --- aws/resource_aws_vpn_connection.go | 772 ++++++++++++------------ aws/resource_aws_vpn_connection_test.go | 287 +++++---- 2 files changed, 540 insertions(+), 519 deletions(-) diff --git a/aws/resource_aws_vpn_connection.go b/aws/resource_aws_vpn_connection.go index 8ba13db3e22..669ec37a13c 100644 --- a/aws/resource_aws_vpn_connection.go +++ b/aws/resource_aws_vpn_connection.go @@ -516,211 +516,274 @@ 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{ - {}, {}, + // Fill the connection options for the EC2 API + connectOpts := expandVpnConnectionOptions(d) + + createOpts := &ec2.CreateVpnConnectionInput{ + CustomerGatewayId: aws.String(d.Get("customer_gateway_id").(string)), + Options: connectOpts, + Type: aws.String(d.Get("type").(string)), + TagSpecifications: ec2TagSpecificationsFromMap(d.Get("tags").(map[string]interface{}), ec2.ResourceTypeVpnConnection), } - if v, ok := d.GetOk("tunnel1_dpd_timeout_action"); ok { - options[0].DPDTimeoutAction = aws.String(v.(string)) + if v, ok := d.GetOk("transit_gateway_id"); ok { + createOpts.TransitGatewayId = 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("vpn_gateway_id"); ok { + createOpts.VpnGatewayId = aws.String(v.(string)) } - if v, ok := d.GetOk("tunnel1_dpd_timeout_seconds"); ok { - options[0].DPDTimeoutSeconds = aws.Int64(int64(v.(int))) + // Create the VPN Connection + log.Printf("[DEBUG] Creating vpn connection") + resp, err := conn.CreateVpnConnection(createOpts) + if err != nil { + return fmt.Errorf("Error creating vpn connection: %s", err) } - if v, ok := d.GetOk("tunnel2_dpd_timeout_seconds"); ok { - options[1].DPDTimeoutSeconds = aws.Int64(int64(v.(int))) + d.SetId(aws.StringValue(resp.VpnConnection.VpnConnectionId)) + + if err := waitForEc2VpnConnectionAvailable(conn, d.Id()); err != nil { + return fmt.Errorf("error waiting for VPN connection (%s) to become available: %s", d.Id(), err) } - 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))}) + // Read off the API to populate our RO fields. + return resourceAwsVpnConnectionRead(d, meta) +} + +func vpnConnectionRefreshFunc(conn *ec2.EC2, connectionId string) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + resp, err := conn.DescribeVpnConnections(&ec2.DescribeVpnConnectionsInput{ + VpnConnectionIds: []*string{aws.String(connectionId)}, + }) + + if err != nil { + if isAWSErr(err, "InvalidVpnConnectionID.NotFound", "") { + resp = nil + } else { + log.Printf("Error on VPNConnectionRefresh: %s", err) + return nil, "", err + } } - 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))}) + if resp == nil || len(resp.VpnConnections) == 0 { + return nil, "", nil } - options[1].IKEVersions = l + + connection := resp.VpnConnections[0] + return connection, aws.StringValue(connection.State), nil } +} - 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 +func resourceAwsVpnConnectionRead(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn + ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig + + resp, err := conn.DescribeVpnConnections(&ec2.DescribeVpnConnectionsInput{ + VpnConnectionIds: []*string{aws.String(d.Id())}, + }) + + if isAWSErr(err, "InvalidVpnConnectionID.NotFound", "") { + log.Printf("[WARN] EC2 VPN Connection (%s) not found, removing from state", d.Id()) + d.SetId("") + return nil } - 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 err != nil { + return fmt.Errorf("error reading EC2 VPN Connection (%s): %s", d.Id(), err) } - 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 resp == nil || len(resp.VpnConnections) == 0 || resp.VpnConnections[0] == nil { + return fmt.Errorf("error reading EC2 VPN Connection (%s): empty response", d.Id()) } - 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 len(resp.VpnConnections) > 1 { + return fmt.Errorf("error reading EC2 VPN Connection (%s): multiple responses", d.Id()) } - 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 + vpnConnection := resp.VpnConnections[0] + + if aws.StringValue(vpnConnection.State) == ec2.VpnStateDeleted { + log.Printf("[WARN] EC2 VPN Connection (%s) already deleted, removing from state", d.Id()) + d.SetId("") + return nil } - 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))}) + var transitGatewayAttachmentID string + if vpnConnection.TransitGatewayId != nil { + input := &ec2.DescribeTransitGatewayAttachmentsInput{ + Filters: []*ec2.Filter{ + { + Name: aws.String("resource-id"), + Values: []*string{vpnConnection.VpnConnectionId}, + }, + { + Name: aws.String("resource-type"), + Values: []*string{aws.String(ec2.TransitGatewayAttachmentResourceTypeVpn)}, + }, + { + Name: aws.String("transit-gateway-id"), + Values: []*string{vpnConnection.TransitGatewayId}, + }, + }, } - options[1].Phase1IntegrityAlgorithms = l - } - if v, ok := d.GetOk("tunnel1_phase1_lifetime_seconds"); ok { - options[0].Phase1LifetimeSeconds = aws.Int64(int64(v.(int))) + log.Printf("[DEBUG] Finding EC2 VPN Connection Transit Gateway Attachment: %s", input) + output, err := conn.DescribeTransitGatewayAttachments(input) + + if err != nil { + return fmt.Errorf("error finding EC2 VPN Connection (%s) Transit Gateway Attachment: %s", d.Id(), err) + } + + if output == nil || len(output.TransitGatewayAttachments) == 0 || output.TransitGatewayAttachments[0] == nil { + return fmt.Errorf("error finding EC2 VPN Connection (%s) Transit Gateway Attachment: empty response", d.Id()) + } + + if len(output.TransitGatewayAttachments) > 1 { + return fmt.Errorf("error reading EC2 VPN Connection (%s) Transit Gateway Attachment: multiple responses", d.Id()) + } + + transitGatewayAttachmentID = aws.StringValue(output.TransitGatewayAttachments[0].TransitGatewayAttachmentId) } - if v, ok := d.GetOk("tunnel2_phase1_lifetime_seconds"); ok { - options[1].Phase1LifetimeSeconds = aws.Int64(int64(v.(int))) + // Set attributes under the user's control. + d.Set("vpn_gateway_id", vpnConnection.VpnGatewayId) + d.Set("customer_gateway_id", vpnConnection.CustomerGatewayId) + d.Set("transit_gateway_id", vpnConnection.TransitGatewayId) + d.Set("type", vpnConnection.Type) + + if err := d.Set("tags", keyvaluetags.Ec2KeyValueTags(vpnConnection.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { + return fmt.Errorf("error setting tags: %s", err) } - 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)))}) + if vpnConnection.Options != nil { + if err := d.Set("enable_acceleration", vpnConnection.Options.EnableAcceleration); err != nil { + return err } - 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)))}) + if err := d.Set("local_ipv4_network_cidr", vpnConnection.Options.LocalIpv4NetworkCidr); err != nil { + return err } - 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))}) + if err := d.Set("local_ipv6_network_cidr", vpnConnection.Options.LocalIpv6NetworkCidr); err != nil { + return err } - 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))}) + if err := d.Set("remote_ipv4_network_cidr", vpnConnection.Options.RemoteIpv4NetworkCidr); err != nil { + return err } - 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))}) + if err := d.Set("remote_ipv6_network_cidr", vpnConnection.Options.RemoteIpv6NetworkCidr); err != nil { + return err } - 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))}) + if err := d.Set("static_routes_only", vpnConnection.Options.StaticRoutesOnly); err != nil { + return err } - options[1].Phase2IntegrityAlgorithms = l - } - if v, ok := d.GetOk("tunnel1_phase2_lifetime_seconds"); ok { - options[0].Phase2LifetimeSeconds = aws.Int64(int64(v.(int))) + if err := d.Set("tunnel_inside_ip_version", vpnConnection.Options.TunnelInsideIpVersion); err != nil { + return err + } + } else { + //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", "") } - if v, ok := d.GetOk("tunnel2_phase2_lifetime_seconds"); ok { - options[1].Phase2LifetimeSeconds = aws.Int64(int64(v.(int))) - } + // Set read only attributes. + d.Set("customer_gateway_configuration", vpnConnection.CustomerGatewayConfiguration) + d.Set("transit_gateway_attachment_id", transitGatewayAttachmentID) - if v, ok := d.GetOk("tunnel1_rekey_fuzz_percentage"); ok { - options[0].RekeyFuzzPercentage = aws.Int64(int64(v.(int))) + if vpnConnection.CustomerGatewayConfiguration != nil { + if tunnelInfo, err := xmlConfigToTunnelInfo(*vpnConnection.CustomerGatewayConfiguration); err != nil { + log.Printf("[ERR] Error unmarshaling XML configuration for (%s): %s", d.Id(), err) + } else { + d.Set("tunnel1_address", tunnelInfo.Tunnel1Address) + d.Set("tunnel1_cgw_inside_address", tunnelInfo.Tunnel1CgwInsideAddress) + d.Set("tunnel1_vgw_inside_address", tunnelInfo.Tunnel1VgwInsideAddress) + d.Set("tunnel1_preshared_key", tunnelInfo.Tunnel1PreSharedKey) + d.Set("tunnel1_bgp_asn", tunnelInfo.Tunnel1BGPASN) + d.Set("tunnel1_bgp_holdtime", tunnelInfo.Tunnel1BGPHoldTime) + d.Set("tunnel2_address", tunnelInfo.Tunnel2Address) + d.Set("tunnel2_preshared_key", tunnelInfo.Tunnel2PreSharedKey) + d.Set("tunnel2_cgw_inside_address", tunnelInfo.Tunnel2CgwInsideAddress) + d.Set("tunnel2_vgw_inside_address", tunnelInfo.Tunnel2VgwInsideAddress) + d.Set("tunnel2_bgp_asn", tunnelInfo.Tunnel2BGPASN) + d.Set("tunnel2_bgp_holdtime", tunnelInfo.Tunnel2BGPHoldTime) + } } - if v, ok := d.GetOk("tunnel2_rekey_fuzz_percentage"); ok { - options[1].RekeyFuzzPercentage = aws.Int64(int64(v.(int))) + if err := d.Set("vgw_telemetry", telemetryToMapList(vpnConnection.VgwTelemetry)); err != nil { + return err } - - if v, ok := d.GetOk("tunnel1_rekey_margin_time_seconds"); ok { - options[0].RekeyMarginTimeSeconds = aws.Int64(int64(v.(int))) + if err := d.Set("routes", routesToMapList(vpnConnection.Routes)); err != nil { + return err } - if v, ok := d.GetOk("tunnel2_rekey_margin_time_seconds"); ok { - options[1].RekeyMarginTimeSeconds = aws.Int64(int64(v.(int))) - } + arn := arn.ARN{ + Partition: meta.(*AWSClient).partition, + Service: "ec2", + Region: meta.(*AWSClient).region, + AccountID: meta.(*AWSClient).accountid, + Resource: fmt.Sprintf("vpn-connection/%s", d.Id()), + }.String() - if v, ok := d.GetOk("tunnel1_replay_window_size"); ok { - options[0].ReplayWindowSize = aws.Int64(int64(v.(int))) - } + d.Set("arn", arn) - if v, ok := d.GetOk("tunnel2_replay_window_size"); ok { - options[1].ReplayWindowSize = aws.Int64(int64(v.(int))) - } + return nil +} - if v, ok := d.GetOk("tunnel1_startup_action"); ok { - options[0].StartupAction = aws.String(v.(string)) - } +func resourceAwsVpnConnectionUpdate(d *schema.ResourceData, meta interface{}) error { + conn := meta.(*AWSClient).ec2conn - if v, ok := d.GetOk("tunnel2_startup_action"); ok { - options[1].StartupAction = aws.String(v.(string)) + if err := modifyVpnConnectionOptions(d, conn); err != nil { + return err } - if v, ok := d.GetOk("tunnel1_inside_cidr"); ok { - options[0].TunnelInsideCidr = aws.String(v.(string)) + if err := modifyVpnTunnels(d, conn); err != nil { + return err } - if v, ok := d.GetOk("tunnel2_inside_cidr"); ok { - options[1].TunnelInsideCidr = aws.String(v.(string)) - } + if d.HasChange("tags") { + o, n := d.GetChange("tags") + vpnConnectionID := d.Id() - if v, ok := d.GetOk("tunnel1_inside_ipv6_cidr"); ok { - options[0].TunnelInsideIpv6Cidr = aws.String(v.(string)) + if err := keyvaluetags.Ec2UpdateTags(conn, vpnConnectionID, o, n); err != nil { + return fmt.Errorf("error updating EC2 VPN Connection (%s) tags: %s", d.Id(), err) + } } - if v, ok := d.GetOk("tunnel2_inside_ipv6_cidr"); ok { - options[1].TunnelInsideIpv6Cidr = aws.String(v.(string)) + 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 v, ok := d.GetOk("tunnel1_preshared_key"); ok { - options[0].PreSharedKey = aws.String(v.(string)) + if err != nil { + return fmt.Errorf("error deleting VPN Connection (%s): %s", d.Id(), err) } - if v, ok := d.GetOk("tunnel2_preshared_key"); ok { - options[1].PreSharedKey = aws.String(v.(string)) + 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" { @@ -753,238 +816,258 @@ func resourceAwsVpnConnectionCreate(d *schema.ResourceData, meta interface{}) er connectOpts.StaticRoutesOnly = aws.Bool(v.(bool)) } - connectOpts.TunnelOptions = options + // Fill the tunnel options for the EC2 API + connectOpts.TunnelOptions = expandVpnTunnelOptions(d) - createOpts := &ec2.CreateVpnConnectionInput{ - CustomerGatewayId: aws.String(d.Get("customer_gateway_id").(string)), - Options: connectOpts, - Type: aws.String(d.Get("type").(string)), - TagSpecifications: ec2TagSpecificationsFromMap(d.Get("tags").(map[string]interface{}), ec2.ResourceTypeVpnConnection), - } + return connectOpts +} - if v, ok := d.GetOk("transit_gateway_id"); ok { - createOpts.TransitGatewayId = aws.String(v.(string)) +func expandVpnTunnelOptions(d *schema.ResourceData) []*ec2.VpnTunnelOptionsSpecification { + options := []*ec2.VpnTunnelOptionsSpecification{ + {}, {}, } - if v, ok := d.GetOk("vpn_gateway_id"); ok { - createOpts.VpnGatewayId = aws.String(v.(string)) + if v, ok := d.GetOk("tunnel1_dpd_timeout_action"); ok { + options[0].DPDTimeoutAction = aws.String(v.(string)) } - // Create the VPN Connection - log.Printf("[DEBUG] Creating vpn connection") - resp, err := conn.CreateVpnConnection(createOpts) - if err != nil { - return fmt.Errorf("Error creating vpn connection: %s", err) + if v, ok := d.GetOk("tunnel2_dpd_timeout_action"); ok { + options[1].DPDTimeoutAction = aws.String(v.(string)) } - d.SetId(aws.StringValue(resp.VpnConnection.VpnConnectionId)) - - if err := waitForEc2VpnConnectionAvailable(conn, d.Id()); err != nil { - return fmt.Errorf("error waiting for VPN connection (%s) to become available: %s", d.Id(), err) + if v, ok := d.GetOk("tunnel1_dpd_timeout_seconds"); ok { + options[0].DPDTimeoutSeconds = aws.Int64(int64(v.(int))) } - // Read off the API to populate our RO fields. - return resourceAwsVpnConnectionRead(d, meta) -} - -func vpnConnectionRefreshFunc(conn *ec2.EC2, connectionId string) resource.StateRefreshFunc { - return func() (interface{}, string, error) { - resp, err := conn.DescribeVpnConnections(&ec2.DescribeVpnConnectionsInput{ - VpnConnectionIds: []*string{aws.String(connectionId)}, - }) + if v, ok := d.GetOk("tunnel2_dpd_timeout_seconds"); ok { + options[1].DPDTimeoutSeconds = aws.Int64(int64(v.(int))) + } - if err != nil { - if isAWSErr(err, "InvalidVpnConnectionID.NotFound", "") { - resp = nil - } else { - log.Printf("Error on VPNConnectionRefresh: %s", err) - return nil, "", err - } + 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 resp == nil || len(resp.VpnConnections) == 0 { - return nil, "", nil + 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 + } - connection := resp.VpnConnections[0] - return connection, aws.StringValue(connection.State), nil + 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 } -} -func resourceAwsVpnConnectionRead(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*AWSClient).ec2conn - ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig + 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 + } - resp, err := conn.DescribeVpnConnections(&ec2.DescribeVpnConnectionsInput{ - VpnConnectionIds: []*string{aws.String(d.Id())}, - }) + 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 isAWSErr(err, "InvalidVpnConnectionID.NotFound", "") { - log.Printf("[WARN] EC2 VPN Connection (%s) not found, removing from state", d.Id()) - d.SetId("") - return nil + 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 err != nil { - return fmt.Errorf("error reading EC2 VPN Connection (%s): %s", d.Id(), err) + 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 resp == nil || len(resp.VpnConnections) == 0 || resp.VpnConnections[0] == nil { - return fmt.Errorf("error reading EC2 VPN Connection (%s): empty response", d.Id()) + 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 len(resp.VpnConnections) > 1 { - return fmt.Errorf("error reading EC2 VPN Connection (%s): multiple responses", d.Id()) + if v, ok := d.GetOk("tunnel1_phase1_lifetime_seconds"); ok { + options[0].Phase1LifetimeSeconds = aws.Int64(int64(v.(int))) } - vpnConnection := resp.VpnConnections[0] + if v, ok := d.GetOk("tunnel2_phase1_lifetime_seconds"); ok { + options[1].Phase1LifetimeSeconds = aws.Int64(int64(v.(int))) + } - if aws.StringValue(vpnConnection.State) == ec2.VpnStateDeleted { - log.Printf("[WARN] EC2 VPN Connection (%s) already deleted, removing from state", d.Id()) - d.SetId("") - return nil + 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 } - var transitGatewayAttachmentID string - if vpnConnection.TransitGatewayId != nil { - input := &ec2.DescribeTransitGatewayAttachmentsInput{ - Filters: []*ec2.Filter{ - { - Name: aws.String("resource-id"), - Values: []*string{vpnConnection.VpnConnectionId}, - }, - { - Name: aws.String("resource-type"), - Values: []*string{aws.String(ec2.TransitGatewayAttachmentResourceTypeVpn)}, - }, - { - Name: aws.String("transit-gateway-id"), - Values: []*string{vpnConnection.TransitGatewayId}, - }, - }, + 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 + } - log.Printf("[DEBUG] Finding EC2 VPN Connection Transit Gateway Attachment: %s", input) - output, err := conn.DescribeTransitGatewayAttachments(input) + 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 err != nil { - return fmt.Errorf("error finding EC2 VPN Connection (%s) Transit Gateway Attachment: %s", d.Id(), err) + 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 output == nil || len(output.TransitGatewayAttachments) == 0 || output.TransitGatewayAttachments[0] == nil { - return fmt.Errorf("error finding EC2 VPN Connection (%s) Transit Gateway Attachment: empty response", d.Id()) + 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 len(output.TransitGatewayAttachments) > 1 { - return fmt.Errorf("error reading EC2 VPN Connection (%s) Transit Gateway Attachment: multiple responses", d.Id()) + 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 + } - transitGatewayAttachmentID = aws.StringValue(output.TransitGatewayAttachments[0].TransitGatewayAttachmentId) + if v, ok := d.GetOk("tunnel1_phase2_lifetime_seconds"); ok { + options[0].Phase2LifetimeSeconds = aws.Int64(int64(v.(int))) } - // Set attributes under the user's control. - d.Set("vpn_gateway_id", vpnConnection.VpnGatewayId) - d.Set("customer_gateway_id", vpnConnection.CustomerGatewayId) - d.Set("transit_gateway_id", vpnConnection.TransitGatewayId) - d.Set("type", vpnConnection.Type) + if v, ok := d.GetOk("tunnel2_phase2_lifetime_seconds"); ok { + options[1].Phase2LifetimeSeconds = aws.Int64(int64(v.(int))) + } - if err := d.Set("tags", keyvaluetags.Ec2KeyValueTags(vpnConnection.Tags).IgnoreAws().IgnoreConfig(ignoreTagsConfig).Map()); err != nil { - return fmt.Errorf("error setting tags: %s", err) + if v, ok := d.GetOk("tunnel1_rekey_fuzz_percentage"); ok { + options[0].RekeyFuzzPercentage = aws.Int64(int64(v.(int))) } - if vpnConnection.Options != nil { - if err := d.Set("enable_acceleration", vpnConnection.Options.EnableAcceleration); err != nil { - return err - } + if v, ok := d.GetOk("tunnel2_rekey_fuzz_percentage"); ok { + options[1].RekeyFuzzPercentage = aws.Int64(int64(v.(int))) + } - if err := d.Set("local_ipv4_network_cidr", vpnConnection.Options.LocalIpv4NetworkCidr); err != nil { - return err - } + if v, ok := d.GetOk("tunnel1_rekey_margin_time_seconds"); ok { + options[0].RekeyMarginTimeSeconds = aws.Int64(int64(v.(int))) + } - if err := d.Set("local_ipv6_network_cidr", vpnConnection.Options.LocalIpv6NetworkCidr); err != nil { - return err - } + if v, ok := d.GetOk("tunnel2_rekey_margin_time_seconds"); ok { + options[1].RekeyMarginTimeSeconds = aws.Int64(int64(v.(int))) + } - if err := d.Set("remote_ipv4_network_cidr", vpnConnection.Options.RemoteIpv4NetworkCidr); err != nil { - return err - } + if v, ok := d.GetOk("tunnel1_replay_window_size"); ok { + options[0].ReplayWindowSize = aws.Int64(int64(v.(int))) + } - if err := d.Set("remote_ipv6_network_cidr", vpnConnection.Options.RemoteIpv6NetworkCidr); err != nil { - return err - } + if v, ok := d.GetOk("tunnel2_replay_window_size"); ok { + options[1].ReplayWindowSize = aws.Int64(int64(v.(int))) + } - if err := d.Set("static_routes_only", vpnConnection.Options.StaticRoutesOnly); err != nil { - return err - } + if v, ok := d.GetOk("tunnel1_startup_action"); ok { + options[0].StartupAction = aws.String(v.(string)) + } - if err := d.Set("tunnel_inside_ip_version", vpnConnection.Options.TunnelInsideIpVersion); err != nil { - return err - } - } else { - //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", "") + if v, ok := d.GetOk("tunnel2_startup_action"); ok { + options[1].StartupAction = aws.String(v.(string)) } - // Set read only attributes. - d.Set("customer_gateway_configuration", vpnConnection.CustomerGatewayConfiguration) - d.Set("transit_gateway_attachment_id", transitGatewayAttachmentID) + if v, ok := d.GetOk("tunnel1_inside_cidr"); ok { + options[0].TunnelInsideCidr = aws.String(v.(string)) + } - if vpnConnection.CustomerGatewayConfiguration != nil { - if tunnelInfo, err := xmlConfigToTunnelInfo(*vpnConnection.CustomerGatewayConfiguration); err != nil { - log.Printf("[ERR] Error unmarshaling XML configuration for (%s): %s", d.Id(), err) - } else { - d.Set("tunnel1_address", tunnelInfo.Tunnel1Address) - d.Set("tunnel1_cgw_inside_address", tunnelInfo.Tunnel1CgwInsideAddress) - d.Set("tunnel1_vgw_inside_address", tunnelInfo.Tunnel1VgwInsideAddress) - d.Set("tunnel1_preshared_key", tunnelInfo.Tunnel1PreSharedKey) - d.Set("tunnel1_bgp_asn", tunnelInfo.Tunnel1BGPASN) - d.Set("tunnel1_bgp_holdtime", tunnelInfo.Tunnel1BGPHoldTime) - d.Set("tunnel2_address", tunnelInfo.Tunnel2Address) - d.Set("tunnel2_preshared_key", tunnelInfo.Tunnel2PreSharedKey) - d.Set("tunnel2_cgw_inside_address", tunnelInfo.Tunnel2CgwInsideAddress) - d.Set("tunnel2_vgw_inside_address", tunnelInfo.Tunnel2VgwInsideAddress) - d.Set("tunnel2_bgp_asn", tunnelInfo.Tunnel2BGPASN) - d.Set("tunnel2_bgp_holdtime", tunnelInfo.Tunnel2BGPHoldTime) - } + if v, ok := d.GetOk("tunnel2_inside_cidr"); ok { + options[1].TunnelInsideCidr = aws.String(v.(string)) } - if err := d.Set("vgw_telemetry", telemetryToMapList(vpnConnection.VgwTelemetry)); err != nil { - return err + if v, ok := d.GetOk("tunnel1_inside_ipv6_cidr"); ok { + options[0].TunnelInsideIpv6Cidr = aws.String(v.(string)) } - if err := d.Set("routes", routesToMapList(vpnConnection.Routes)); err != nil { - return err + + if v, ok := d.GetOk("tunnel2_inside_ipv6_cidr"); ok { + options[1].TunnelInsideIpv6Cidr = aws.String(v.(string)) } - arn := arn.ARN{ - Partition: meta.(*AWSClient).partition, - Service: "ec2", - Region: meta.(*AWSClient).region, - AccountID: meta.(*AWSClient).accountid, - Resource: fmt.Sprintf("vpn-connection/%s", d.Id()), - }.String() + if v, ok := d.GetOk("tunnel1_preshared_key"); ok { + options[0].PreSharedKey = aws.String(v.(string)) + } - d.Set("arn", arn) + if v, ok := d.GetOk("tunnel2_preshared_key"); ok { + options[1].PreSharedKey = aws.String(v.(string)) + } - return nil + return options } -func resourceAwsVpnConnectionUpdate(d *schema.ResourceData, meta interface{}) error { - conn := meta.(*AWSClient).ec2conn +// 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) - tun1Changed := false - tun2Changed := false - vgwTelemetryTun1Index := 0 - vgwTelemetryTun2Index := 1 - options := []*ec2.ModifyVpnTunnelOptionsSpecification{ - {}, {}, + if r.Source != nil { + staticRoute["source"] = aws.StringValue(r.Source) + } + + result = append(result, staticRoute) + } + + return result +} + +// telemetryToMapList turns the VGW telemetry into a list of maps. +func telemetryToMapList(telemetry []*ec2.VgwTelemetry) []map[string]interface{} { + result := make([]map[string]interface{}, 0, len(telemetry)) + for _, t := range telemetry { + vgw := make(map[string]interface{}) + vgw["accepted_route_count"] = aws.Int64Value(t.AcceptedRouteCount) + vgw["outside_ip_address"] = aws.StringValue(t.OutsideIpAddress) + vgw["status"] = aws.StringValue(t.Status) + vgw["status_message"] = aws.StringValue(t.StatusMessage) + + // LastStatusChange is a time.Time(). Convert it into a string + // so it can be handled by schema's type system. + vgw["last_status_change"] = t.LastStatusChange.Format(time.RFC3339) + result = append(result, vgw) } + return result +} + +func modifyVpnConnectionOptions(d *schema.ResourceData, conn *ec2.EC2) error { var connOpts *ec2.ModifyVpnConnectionOptionsInput = new(ec2.ModifyVpnConnectionOptionsInput) connChanged := false @@ -1022,6 +1105,20 @@ func resourceAwsVpnConnectionUpdate(d *schema.ResourceData, meta interface{}) er } } + 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)) @@ -1240,76 +1337,9 @@ func resourceAwsVpnConnectionUpdate(d *schema.ResourceData, meta interface{}) er } } - if d.HasChange("tags") { - o, n := d.GetChange("tags") - - 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 } -// 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) - } - - return result -} - -// telemetryToMapList turns the VGW telemetry into a list of maps. -func telemetryToMapList(telemetry []*ec2.VgwTelemetry) []map[string]interface{} { - result := make([]map[string]interface{}, 0, len(telemetry)) - for _, t := range telemetry { - vgw := make(map[string]interface{}) - vgw["accepted_route_count"] = aws.Int64Value(t.AcceptedRouteCount) - vgw["outside_ip_address"] = aws.StringValue(t.OutsideIpAddress) - vgw["status"] = aws.StringValue(t.Status) - vgw["status_message"] = aws.StringValue(t.StatusMessage) - - // LastStatusChange is a time.Time(). Convert it into a string - // so it can be handled by schema's type system. - vgw["last_status_change"] = t.LastStatusChange.Format(time.RFC3339) - result = append(result, vgw) - } - - return result -} - 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) diff --git a/aws/resource_aws_vpn_connection_test.go b/aws/resource_aws_vpn_connection_test.go index 0f4977f4190..3b91a0b2256 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", @@ -145,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, @@ -230,44 +290,7 @@ func TestAccAWSVpnConnection_tunnelOptions(t *testing.T) { //Try actual building { - Config: testAccAwsVpnConnectionConfigTunnelOptions( - rBgpAsn, - "192.168.1.1/32", - "192.168.1.2/32", - "12345678", - "169.254.8.0/30", - "clear", - 30, - "\"ikev1\", \"ikev2\"", - "2, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24", - "\"AES128\", \"AES256\", \"AES128-GCM-16\", \"AES256-GCM-16\"", - "\"SHA1\", \"SHA2-256\", \"SHA2-384\", \"SHA2-512\"", - 28800, - "2, 5, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24", - "\"AES128\", \"AES256\", \"AES128-GCM-16\", \"AES256-GCM-16\"", - "\"SHA1\", \"SHA2-256\", \"SHA2-384\", \"SHA2-512\"", - 3600, - 100, - 540, - 1024, - "add", - "abcdefgh", - "169.254.9.0/30", - "clear", - 30, - "\"ikev1\", \"ikev2\"", - "2, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24", - "\"AES128\", \"AES256\", \"AES128-GCM-16\", \"AES256-GCM-16\"", - "\"SHA1\", \"SHA2-256\", \"SHA2-384\", \"SHA2-512\"", - 28800, - "2, 5, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24", - "\"AES128\", \"AES256\", \"AES128-GCM-16\", \"AES256-GCM-16\"", - "\"SHA1\", \"SHA2-256\", \"SHA2-384\", \"SHA2-512\"", - 3600, - 100, - 540, - 1024, - "add"), + 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"), @@ -646,12 +669,12 @@ resource "aws_vpn_connection" "test" { static_routes_only = false enable_acceleration = false - local_ipv6_network_cidr = "%s" - remote_ipv6_network_cidr = "%s" + local_ipv6_network_cidr = %[2]q + remote_ipv6_network_cidr = %[3]q tunnel_inside_ip_version = "ipv6" - tunnel1_inside_ipv6_cidr = "%s" - tunnel2_inside_ipv6_cidr = "%s" + tunnel1_inside_ipv6_cidr = %[4]q + tunnel2_inside_ipv6_cidr = %[5]q } `, rBgpAsn, localIpv6NetworkCidr, remoteIpv6NetworkCidr, tunnel1InsideIpv6Cidr, tunnel2InsideIpv6Cidr) } @@ -712,40 +735,8 @@ func testAccAwsVpnConnectionConfigTunnelOptions( rBgpAsn int, localIpv4NetworkCidr string, remoteIpv4NetworkCidr string, - 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, - psk2 string, - tunnelCidr2 string, - dpdTimeoutAction2 string, - dpdTimeoutSeconds2 int, - ikeVersions2 string, - phase1DhGroupNumbers2 string, - phase1EncryptionAlgorithms2 string, - phase1IntegrityAlgorithms2 string, - phase1LifetimeSeconds2 int, - phase2DhGroupNumbers2 string, - phase2EncryptionAlgorithms2 string, - phase2IntegrityAlgorithms2 string, - phase2LifetimeSeconds2 int, - rekeyFuzzPercentage2 int, - rekeyMarginTimeSeconds2 int, - replayWindowSize2 int, - startupAction2 string, + tunnel1 TunnelOptions, + tunnel2 TunnelOptions, ) string { return fmt.Sprintf(` resource "aws_vpn_gateway" "vpn_gateway" { @@ -770,83 +761,83 @@ resource "aws_vpn_connection" "test" { type = "ipsec.1" static_routes_only = false - local_ipv4_network_cidr = "%s" - remote_ipv4_network_cidr = "%s" - - tunnel1_inside_cidr = "%s" - tunnel1_preshared_key = "%s" - tunnel1_dpd_timeout_action = "%s" - tunnel1_dpd_timeout_seconds = %d - tunnel1_ike_versions = [%s] - tunnel1_phase1_dh_group_numbers = [%s] - tunnel1_phase1_encryption_algorithms = [%s] - tunnel1_phase1_integrity_algorithms = [%s] - tunnel1_phase1_lifetime_seconds = %d - tunnel1_phase2_dh_group_numbers = [%s] - tunnel1_phase2_encryption_algorithms = [%s] - tunnel1_phase2_integrity_algorithms = [%s] - tunnel1_phase2_lifetime_seconds = %d - tunnel1_rekey_fuzz_percentage = %d - tunnel1_rekey_margin_time_seconds = %d - tunnel1_replay_window_size = %d - tunnel1_startup_action = "%s" - - tunnel2_inside_cidr = "%s" - tunnel2_preshared_key = "%s" - tunnel2_dpd_timeout_action = "%s" - tunnel2_dpd_timeout_seconds = %d - tunnel2_ike_versions = [%s] - tunnel2_phase1_dh_group_numbers = [%s] - tunnel2_phase1_encryption_algorithms = [%s] - tunnel2_phase1_integrity_algorithms = [%s] - tunnel2_phase1_lifetime_seconds = %d - tunnel2_phase2_dh_group_numbers = [%s] - tunnel2_phase2_encryption_algorithms = [%s] - tunnel2_phase2_integrity_algorithms = [%s] - tunnel2_phase2_lifetime_seconds = %d - tunnel2_rekey_fuzz_percentage = %d - tunnel2_rekey_margin_time_seconds = %d - tunnel2_replay_window_size = %d - tunnel2_startup_action = "%s" + 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, - tunnelCidr, - psk, - dpdTimeoutAction, - dpdTimeoutSeconds, - ikeVersions, - phase1DhGroupNumbers, - phase1EncryptionAlgorithms, - phase1IntegrityAlgorithms, - phase1LifetimeSeconds, - phase2DhGroupNumbers, - phase2EncryptionAlgorithms, - phase2IntegrityAlgorithms, - phase2LifetimeSeconds, - rekeyFuzzPercentage, - rekeyMarginTimeSeconds, - replayWindowSize, - startupAction, - tunnelCidr2, - psk2, - dpdTimeoutAction2, - dpdTimeoutSeconds2, - ikeVersions2, - phase1DhGroupNumbers2, - phase1EncryptionAlgorithms2, - phase1IntegrityAlgorithms2, - phase1LifetimeSeconds2, - phase2DhGroupNumbers2, - phase2EncryptionAlgorithms2, - phase2IntegrityAlgorithms2, - phase2LifetimeSeconds2, - rekeyFuzzPercentage2, - rekeyMarginTimeSeconds2, - replayWindowSize2, - startupAction2) + 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 { From 4acc6d8ee1223857a448cd898f370ae1ec35e2d3 Mon Sep 17 00:00:00 2001 From: Bill Rich Date: Wed, 16 Dec 2020 09:20:43 -0800 Subject: [PATCH 3/3] Read all atrributes in tunnel options. --- aws/resource_aws_vpn_connection.go | 204 ++++++++++++++++++++++++ aws/resource_aws_vpn_connection_test.go | 1 - 2 files changed, 204 insertions(+), 1 deletion(-) diff --git a/aws/resource_aws_vpn_connection.go b/aws/resource_aws_vpn_connection.go index 669ec37a13c..7445e4bed41 100644 --- a/aws/resource_aws_vpn_connection.go +++ b/aws/resource_aws_vpn_connection.go @@ -684,6 +684,10 @@ func resourceAwsVpnConnectionRead(d *schema.ResourceData, meta interface{}) erro 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 it d.Set("enable_acceleration", false) @@ -738,6 +742,206 @@ func resourceAwsVpnConnectionRead(d *schema.ResourceData, meta interface{}) erro return nil } +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 err := d.Set("tunnel1_dpd_timeout_seconds", vpnConnection.Options.TunnelOptions[0].DpdTimeoutSeconds); err != nil { + return 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 + } + + 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 + } + + 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 + } + + 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 err := d.Set("tunnel1_phase1_lifetime_seconds", vpnConnection.Options.TunnelOptions[0].Phase1LifetimeSeconds); err != nil { + return 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 + } + + 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 + } + + 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 + } + + if err := d.Set("tunnel1_phase2_lifetime_seconds", vpnConnection.Options.TunnelOptions[0].Phase2LifetimeSeconds); err != nil { + return err + } + + 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 diff --git a/aws/resource_aws_vpn_connection_test.go b/aws/resource_aws_vpn_connection_test.go index 3b91a0b2256..e94ba820444 100644 --- a/aws/resource_aws_vpn_connection_test.go +++ b/aws/resource_aws_vpn_connection_test.go @@ -625,7 +625,6 @@ resource "aws_vpn_connection" "test" { customer_gateway_id = aws_customer_gateway.customer_gateway.id type = "ipsec.1" static_routes_only = false - enable_acceleration = false } `, rBgpAsn, rInt) }