diff --git a/CHANGELOG.md b/CHANGELOG.md index 93cee64f8f..d6dbb36a28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ FEATURES: +* **New Data Source**: `tencentcloud_vpn_gateways` +* **New Data Source**: `tencentcloud_customer_gateways` +* **New Data Source**: `tencentcloud_vpn_connections` +* **New Resource**: `tencentcloud_vpn_gateway` +* **New Resource**: `tencentcloud_customer_gateway` +* **New Resource**: `tencentcloud_vpn_connection` * **New Data Source**: `tencentcloud_images` * **Provider TencentCloud**: add `security_token` argument @@ -31,6 +37,7 @@ FEATURES: * **New Resource**: `tencentcloud_scf_function` * **New Resource**: `tencentcloud_scf_namespace` + ## 1.21.2 (October 29, 2019) BUG FIXES: diff --git a/examples/tencentcloud-vpn/main.tf b/examples/tencentcloud-vpn/main.tf new file mode 100644 index 0000000000..b82bfc506c --- /dev/null +++ b/examples/tencentcloud-vpn/main.tf @@ -0,0 +1,65 @@ + +resource "tencentcloud_vpn_customer_gateway" "example" { + name = "example" + public_ip_address = "3.3.3.3" + + tags = { + test = "example" + } +} + +data "tencentcloud_vpc_instances" "example" { + name = "Default-VPC" +} + +resource "tencentcloud_vpn_gateway" "example" { + name = "example" + vpc_id = "${data.tencentcloud_vpc_instances.example.instance_list.0.vpc_id}" + bandwidth = 5 + zone = "${var.availability_zone}" + + tags = { + test = "test" + } +} + +resource "tencentcloud_vpn_connection" "example" { + name = "example" + vpc_id = "${data.tencentcloud_vpc_instances.example.instance_list.0.vpc_id}" + vpn_gateway_id = "${tencentcloud_vpn_gateway.example.id}" + customer_gateway_id = "${tencentcloud_vpn_customer_gateway.example.id}" + pre_share_key = "test" + ike_proto_encry_algorithm = "3DES-CBC" + ike_proto_authen_algorithm = "MD5" + ike_local_identity = "ADDRESS" + ike_local_address = "${tencentcloud_vpn_gateway.example.public_ip_address}" + ike_remote_identity = "ADDRESS" + ike_remote_address = "${tencentcloud_vpn_customer_gateway.example.public_ip_address}" + ike_dh_group_name = "GROUP1" + ike_sa_lifetime_seconds = 86400 + ipsec_encrypt_algorithm = "3DES-CBC" + ipsec_integrity_algorithm = "MD5" + ipsec_sa_lifetime_seconds = 3600 + ipsec_pfs_dh_group = "DH-GROUP1" + ipsec_sa_lifetime_traffic = 2560 + + security_group_policy { + local_cidr_block = "172.16.0.0/16" + remote_cidr_block = ["3.3.3.0/32", ] + } + tags = { + test = "test" + } +} + +data "tencentcloud_vpn_customer_gateways" "example" { + id = "${tencentcloud_vpn_customer_gateway.example.id}" +} + +data "tencentcloud_vpn_gateways" "example" { + id = "${tencentcloud_vpn_gateway.example.id}" +} + +data "tencentcloud_vpn_connections" "example" { + id = "${tencentcloud_vpn_connection.example.id}" +} diff --git a/examples/tencentcloud-vpn/variables.tf b/examples/tencentcloud-vpn/variables.tf new file mode 100644 index 0000000000..300ecfbb76 --- /dev/null +++ b/examples/tencentcloud-vpn/variables.tf @@ -0,0 +1,3 @@ +variable "availability_zone" { + default = "ap-guangzhou-3" +} \ No newline at end of file diff --git a/go.sum b/go.sum index ad8a427ce9..c9e007d05c 100644 --- a/go.sum +++ b/go.sum @@ -327,6 +327,8 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= +github.com/keegancsmith/rpc v1.1.0 h1:bXVRk3EzbtrEegTGKxNTc+St1lR7t/Z1PAO8misBnCc= +github.com/keegancsmith/rpc v1.1.0/go.mod h1:Xow74TKX34OPPiPCdz6x1o9c0SCxRqGxDuKGk7ZOo8s= github.com/keybase/go-crypto v0.0.0-20161004153544-93f5b35093ba/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= @@ -583,6 +585,8 @@ golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392 h1:ACG4HJsFiNMf47Y4PeRoebLNy/2lXT9EtprMuTFWt1M= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3 h1:x/bBzNauLQAlE3fLku/xy92Y8QwKX5HZymrMz2IiKFc= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= diff --git a/tencentcloud/data_source_tc_nat_gateways.go b/tencentcloud/data_source_tc_nat_gateways.go index 654fbd7c2a..5b47405ade 100644 --- a/tencentcloud/data_source_tc_nat_gateways.go +++ b/tencentcloud/data_source_tc_nat_gateways.go @@ -34,12 +34,12 @@ func dataSourceTencentCloudNatGateways() *schema.Resource { "name": { Type: schema.TypeString, Optional: true, - Description: "Name of the nat gateway.", + Description: "Name of the NAT gateway.", }, "id": { Type: schema.TypeString, Optional: true, - Description: "ID of the nat gateway.", + Description: "ID of the NAT gateway.", }, "result_output_file": { Type: schema.TypeString, @@ -51,13 +51,13 @@ func dataSourceTencentCloudNatGateways() *schema.Resource { "nats": { Type: schema.TypeList, Computed: true, - Description: "Information list of the dedicated tunnels.", + Description: "Information list of the dedicated nats.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ "id": { Type: schema.TypeString, Computed: true, - Description: "ID of the nat gateway.", + Description: "ID of the NAT gateway.", }, "vpc_id": { Type: schema.TypeString, @@ -67,22 +67,22 @@ func dataSourceTencentCloudNatGateways() *schema.Resource { "name": { Type: schema.TypeString, Computed: true, - Description: "Name of the nat gateway.", + Description: "Name of the NAT gateway.", }, "state": { Type: schema.TypeInt, Computed: true, - Description: "State of the nat gateway.", + Description: "State of the NAT gateway.", }, "max_concurrent": { Type: schema.TypeInt, Computed: true, - Description: "The upper limit of concurrent connection of nat gateway, the available values include: 1000000,3000000,10000000. Default is 1000000.", + Description: "The upper limit of concurrent connection of NAT gateway, the available values include: 1000000,3000000,10000000. Default is 1000000.", }, "bandwidth": { Type: schema.TypeInt, Computed: true, - Description: "The maximum public network output bandwidth of nat gateway (unit: Mbps), the available values include: 20,50,100,200,500,1000,2000,5000. Default is 100.", + Description: "The maximum public network output bandwidth of NAT gateway (unit: Mbps), the available values include: 20,50,100,200,500,1000,2000,5000. Default is 100.", }, "assigned_eip_set": { Type: schema.TypeList, @@ -93,7 +93,7 @@ func dataSourceTencentCloudNatGateways() *schema.Resource { "create_time": { Type: schema.TypeString, Computed: true, - Description: "Create time of the nat gateway.", + Description: "Create time of the NAT gateway.", }, }, }, @@ -126,24 +126,38 @@ func dataSourceTencentCloudNatGatewaysRead(d *schema.ResourceData, meta interfac } request.Filters = append(request.Filters, filter) } - var response *vpc.DescribeNatGatewaysResponse - err := resource.Retry(readRetryTimeout, func() *resource.RetryError { - result, e := meta.(*TencentCloudClient).apiV3Conn.UseVpcClient().DescribeNatGateways(request) - if e != nil { - log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", - logId, request.GetAction(), request.ToJsonString(), e.Error()) - return retryError(e) + offset := uint64(0) + request.Offset = &offset + result := make([]*vpc.NatGateway, 0) + limit := uint64(NAT_DESCRIBE_LIMIT) + for { + var response *vpc.DescribeNatGatewaysResponse + err := resource.Retry(readRetryTimeout, func() *resource.RetryError { + result, e := meta.(*TencentCloudClient).apiV3Conn.UseVpcClient().DescribeNatGateways(request) + if e != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return retryError(e) + } + response = result + return nil + }) + if err != nil { + log.Printf("[CRITAL]%s read NAT gateway failed, reason:%s\n ", logId, err.Error()) + return err + } else { + result = append(result, response.Response.NatGatewaySet...) + if len(response.Response.NatGatewaySet) < NAT_DESCRIBE_LIMIT { + break + } else { + offset = offset + limit + request.Offset = &offset + } } - response = result - return nil - }) - if err != nil { - log.Printf("[CRITAL]%s read nat gateway failed, reason:%s\n ", logId, err.Error()) - return err } - ids := make([]string, 0, len(response.Response.NatGatewaySet)) - natList := make([]map[string]interface{}, 0, len(response.Response.NatGatewaySet)) - for _, nat := range response.Response.NatGatewaySet { + ids := make([]string, 0, len(result)) + natList := make([]map[string]interface{}, 0, len(result)) + for _, nat := range result { mapping := map[string]interface{}{ "id": *nat.NatGatewayId, "vpc_id": *nat.VpcId, @@ -159,7 +173,7 @@ func dataSourceTencentCloudNatGatewaysRead(d *schema.ResourceData, meta interfac } d.SetId(dataResourceIdsHash(ids)) if e := d.Set("nats", natList); e != nil { - log.Printf("[CRITAL]%s provider set clb list fail, reason:%s\n ", logId, e.Error()) + log.Printf("[CRITAL]%s provider set NAT list fail, reason:%s\n ", logId, e.Error()) return e } diff --git a/tencentcloud/data_source_tc_vpn_connections.go b/tencentcloud/data_source_tc_vpn_connections.go new file mode 100644 index 0000000000..6f0ac9c8cf --- /dev/null +++ b/tencentcloud/data_source_tc_vpn_connections.go @@ -0,0 +1,387 @@ +/* +Use this data source to query detailed information of VPN connections. + +Example Usage + +```hcl +data "tencentcloud_vpn_connections" "foo" { + name = "main" + id = "vpnx-xfqag" + vpn_gateway_id = "vpngw-8ccsnclt" + vpc_id = "cgw-xfqag" + customer_gateway_id = "" + tags = { + test = "tf" + } +} +``` +*/ +package tencentcloud + +import ( + "context" + "log" + "reflect" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + vpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312" +) + +func dataSourceTencentCloudVpnConnections() *schema.Resource { + return &schema.Resource{ + Read: dataSourceTencentCloudVpnConnectionsRead, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateStringLengthInRange(1, 60), + Description: "Name of the VPN connection. The length of character is limited to 1-60.", + }, + "id": { + Type: schema.TypeString, + Optional: true, + Description: "ID of the VPN connection.", + }, + "vpn_gateway_id": { + Type: schema.TypeString, + Optional: true, + Description: "VPN gateway ID of the VPN connection.", + }, + "customer_gateway_id": { + Type: schema.TypeString, + Optional: true, + Description: "Customer gateway ID of the VPN connection.", + }, + "vpc_id": { + Type: schema.TypeString, + Optional: true, + Description: "ID of the VPC.", + }, + "tags": { + Type: schema.TypeMap, + Optional: true, + Description: "Tags of the VPN connection to be queried.", + }, + "result_output_file": { + Type: schema.TypeString, + Optional: true, + Description: "Used to save results.", + }, + + // Computed values + "connection_list": { + Type: schema.TypeList, + Computed: true, + Description: "Information list of the dedicated connections.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + Description: "ID of the VPN connection.", + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: "Name of the VPN connection.", + }, + "vpc_id": { + Type: schema.TypeString, + Computed: true, + Description: "ID of the VPC.", + }, + "customer_gateway_id": { + Type: schema.TypeString, + Computed: true, + Description: "ID of the customer gateway.", + }, + "vpn_gateway_id": { + Type: schema.TypeString, + Computed: true, + Description: "ID of the VPN gateway.", + }, + "pre_share_key": { + Type: schema.TypeString, + Computed: true, + Description: "Pre-shared key of the VPN connection.", + }, + "security_group_policy": { + Type: schema.TypeList, + Computed: true, + Description: "Security group policy of the VPN connection.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "local_cidr_block": { + Type: schema.TypeString, + Computed: true, + Description: "Local cidr block.", + }, + "remote_cidr_block": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: "Remote cidr block list.", + }, + }, + }, + }, + "ike_proto_encry_algorithm": { + Type: schema.TypeString, + Computed: true, + Description: "Proto encrypt algorithm of the IKE operation specification.", + }, + "ike_proto_authen_algorithm": { + Type: schema.TypeString, + Computed: true, + Description: "Proto authenticate algorithm of the IKE operation specification.", + }, + "ike_exchange_mode": { + Type: schema.TypeString, + Computed: true, + Description: "Exchange mode of the IKE operation specification.", + }, + "ike_local_identity": { + Type: schema.TypeString, + Computed: true, + Description: "Local identity of the IKE operation specification.", + }, + "ike_remote_identity": { + Type: schema.TypeString, + Computed: true, + Description: "Remote identity of the IKE operation specification.", + }, + "ike_local_address": { + Type: schema.TypeString, + Computed: true, + Description: "Local address of the IKE operation specification.", + }, + "ike_remote_address": { + Type: schema.TypeString, + Computed: true, + Description: "Remote address of the IKE operation specification.", + }, + "ike_local_fqdn_name": { + Type: schema.TypeString, + Computed: true, + Description: "Local FQDN name of the IKE operation specification.", + }, + "ike_remote_fqdn_name": { + Type: schema.TypeString, + Computed: true, + Description: "Remote FQDN name of the IKE operation specification.", + }, + "ike_dh_group_name": { + Type: schema.TypeString, + Computed: true, + Description: "DH group name of the IKE operation specification.", + }, + "ike_sa_lifetime_seconds": { + Type: schema.TypeInt, + Computed: true, + Description: "SA lifetime of the IKE operation specification, unit is `second`.", + }, + "ike_version": { + Type: schema.TypeString, + Computed: true, + Description: "Version of the IKE operation specification.", + }, + "ipsec_encrypt_algorithm": { + Type: schema.TypeString, + Computed: true, + Description: "Encrypt algorithm of the IPSEC operation specification.", + }, + "ipsec_integrity_algorithm": { + Type: schema.TypeString, + Computed: true, + Description: "Integrity algorithm of the IPSEC operation specification.", + }, + "ipsec_sa_lifetime_seconds": { + Type: schema.TypeInt, + Computed: true, + Description: "SA lifetime of the IPSEC operation specification, unit is `second`.", + }, + "ipsec_pfs_dh_group": { + Type: schema.TypeString, + Computed: true, + Description: "PFS DH group name of the IPSEC operation specification.", + }, + "ipsec_sa_lifetime_traffic": { + Type: schema.TypeInt, + Computed: true, + Description: "SA lifetime traffic of the IPSEC operation specification, unit is `KB`.", + }, + "tags": { + Type: schema.TypeMap, + Computed: true, + Description: "A list of tags used to associate different resources.", + }, + "create_time": { + Type: schema.TypeString, + Computed: true, + Description: "Create time of the VPN connection.", + }, + "vpn_proto": { + Type: schema.TypeString, + Computed: true, + Description: "Vpn proto of the VPN connection.", + }, + "encrypt_proto": { + Type: schema.TypeString, + Computed: true, + Description: "Encrypt proto of the VPN connection.", + }, + "route_type": { + Type: schema.TypeString, + Computed: true, + Description: "Route type of the VPN connection.", + }, + "state": { + Type: schema.TypeString, + Computed: true, + Description: "State of the VPN connection.", + }, + "net_status": { + Type: schema.TypeString, + Computed: true, + Description: "Net status of the VPN connection.", + }, + }, + }, + }, + }, + } +} + +func dataSourceTencentCloudVpnConnectionsRead(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("data_source.tencentcloud_vpn_connections.read")() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), "logId", logId) + + tagService := TagService{client: meta.(*TencentCloudClient).apiV3Conn} + region := meta.(*TencentCloudClient).apiV3Conn.Region + + request := vpc.NewDescribeVpnConnectionsRequest() + + params := make(map[string]string) + if v, ok := d.GetOk("id"); ok { + params["vpn-connection-id"] = v.(string) + } + if v, ok := d.GetOk("name"); ok { + params["vpn-connection-name"] = v.(string) + } + if v, ok := d.GetOk("vpn_gateway_id"); ok { + params["vpn-gateway-id"] = v.(string) + } + if v, ok := d.GetOk("vpc_id"); ok { + params["vpc-id"] = v.(string) + } + if v, ok := d.GetOk("customer_gateway_id"); ok { + params["customer-gateway-id"] = v.(string) + } + + tags := getTags(d, "tags") + + request.Filters = make([]*vpc.Filter, 0, len(params)) + for k, v := range params { + filter := &vpc.Filter{ + Name: stringToPointer(k), + Values: []*string{stringToPointer(v)}, + } + request.Filters = append(request.Filters, filter) + } + offset := uint64(0) + request.Offset = &offset + result := make([]*vpc.VpnConnection, 0) + limit := uint64(VPN_DESCRIBE_LIMIT) + for { + var response *vpc.DescribeVpnConnectionsResponse + err := resource.Retry(readRetryTimeout, func() *resource.RetryError { + result, e := meta.(*TencentCloudClient).apiV3Conn.UseVpcClient().DescribeVpnConnections(request) + if e != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return retryError(e) + } + response = result + return nil + }) + if err != nil { + log.Printf("[CRITAL]%s read VPN connection failed, reason:%s\n", logId, err.Error()) + return err + } else { + result = append(result, response.Response.VpnConnectionSet...) + if len(response.Response.VpnConnectionSet) < VPN_DESCRIBE_LIMIT { + break + } else { + offset = offset + limit + request.Offset = &offset + } + } + } + ids := make([]string, 0, len(result)) + connectionList := make([]map[string]interface{}, 0, len(result)) + for _, connection := range result { + //tags + respTags, err := tagService.DescribeResourceTags(ctx, "vpc", "vpnx", region, *connection.VpnConnectionId) + if err != nil { + return err + } + if len(tags) > 0 { + if !reflect.DeepEqual(respTags, tags) { + continue + } + } + + mapping := map[string]interface{}{ + "id": *connection.VpnConnectionId, + "name": *connection.VpnConnectionName, + "vpn_gateway_id": *connection.VpnGatewayId, + "customer_gateway_id": *connection.CustomerGatewayId, + "ike_proto_authen_algorithm": *connection.IKEOptionsSpecification.PropoAuthenAlgorithm, + "ike_proto_encry_algorithm": *connection.IKEOptionsSpecification.PropoEncryAlgorithm, + "ike_exchange_mode": *connection.IKEOptionsSpecification.ExchangeMode, + "ike_dh_group_name": *connection.IKEOptionsSpecification.DhGroupName, + "ike_sa_lifetime_seconds": int(*connection.IKEOptionsSpecification.IKESaLifetimeSeconds), + "ike_version": *connection.IKEOptionsSpecification.IKEVersion, + "ike_local_identity": *connection.IKEOptionsSpecification.LocalIdentity, + "ike_local_address": *connection.IKEOptionsSpecification.LocalAddress, + "ike_local_fqdn_name": *connection.IKEOptionsSpecification.LocalFqdnName, + "ike_remote_identity": *connection.IKEOptionsSpecification.RemoteIdentity, + "ike_remote_address": *connection.IKEOptionsSpecification.RemoteAddress, + "ike_remote_fqdn_name": *connection.IKEOptionsSpecification.RemoteFqdnName, + "ipsec_sa_lifetime_seconds": int(*connection.IPSECOptionsSpecification.IPSECSaLifetimeSeconds), + "ipsec_encrypt_algorithm": *connection.IPSECOptionsSpecification.EncryptAlgorithm, + "ipsec_integrity_algorithm": *connection.IPSECOptionsSpecification.IntegrityAlgorith, + "ipsec_pfs_dh_group": *connection.IPSECOptionsSpecification.PfsDhGroup, + "ipsec_sa_lifetime_traffic": int(*connection.IPSECOptionsSpecification.IPSECSaLifetimeTraffic), + "security_group_policy": flattenVpnSPDList(connection.SecurityPolicyDatabaseSet), + "net_status": *connection.NetStatus, + "state": *connection.State, + "create_time": *connection.CreatedTime, + "vpn_proto": *connection.VpnProto, + "encrypt_proto": *connection.EncryptProto, + "route_type": *connection.RouteType, + "tags": respTags, + } + connectionList = append(connectionList, mapping) + ids = append(ids, *connection.VpnConnectionId) + } + d.SetId(dataResourceIdsHash(ids)) + if e := d.Set("connection_list", connectionList); e != nil { + log.Printf("[CRITAL]%s provider set connection list fail, reason:%s\n", logId, e.Error()) + return e + } + + output, ok := d.GetOk("result_output_file") + if ok && output.(string) != "" { + if e := writeToFile(output.(string), connectionList); e != nil { + return e + } + } + + return nil + +} diff --git a/tencentcloud/data_source_tc_vpn_connections_test.go b/tencentcloud/data_source_tc_vpn_connections_test.go new file mode 100644 index 0000000000..3ffc2bd111 --- /dev/null +++ b/tencentcloud/data_source_tc_vpn_connections_test.go @@ -0,0 +1,83 @@ +package tencentcloud + +import ( + "testing" + + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccTencentCloudVpnConnectionsDataSource(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccTencentCloudVpnConnectionsDataSourceConfig_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckTencentCloudDataSourceID("data.tencentcloud_vpn_connections.connections"), + resource.TestCheckResourceAttr("data.tencentcloud_vpn_connections.connections", "connection_list.#", "1"), + resource.TestCheckResourceAttr("data.tencentcloud_vpn_connections.connections", "connection_list.0.name", "vpn_connection_test"), + resource.TestCheckResourceAttr("data.tencentcloud_vpn_connections.connections", "connection_list.0.ike_proto_authen_algorithm", "MD5"), + resource.TestCheckResourceAttr("data.tencentcloud_vpn_connections.connections", "connection_list.0.tags.test", "test"), + resource.TestCheckResourceAttr("data.tencentcloud_vpn_connections.connections", "connection_list.0.ipsec_sa_lifetime_traffic", "2560"), + ), + }, + }, + }) +} + +const testAccTencentCloudVpnConnectionsDataSourceConfig_basic = ` +resource "tencentcloud_vpn_customer_gateway" "cgw" { + name = "terraform_test" + public_ip_address = "3.3.3.3" + +} + +# Create VPC and Subnet +data "tencentcloud_vpc_instances" "foo" { + name = "Default-VPC" +} + +resource "tencentcloud_vpn_gateway" "vpn" { + name = "terraform_update" + vpc_id = "${data.tencentcloud_vpc_instances.foo.instance_list.0.vpc_id}" + bandwidth = 5 + zone = "ap-guangzhou-3" + + tags = { + test = "test" + } +} +resource "tencentcloud_vpn_connection" "connection" { + name = "vpn_connection_test" + vpc_id = "${data.tencentcloud_vpc_instances.foo.instance_list.0.vpc_id}" + vpn_gateway_id = "${tencentcloud_vpn_gateway.vpn.id}" + customer_gateway_id = "${tencentcloud_vpn_customer_gateway.cgw.id}" + pre_share_key = "test" + ike_proto_encry_algorithm = "3DES-CBC" + ike_proto_authen_algorithm = "MD5" + ike_local_identity = "ADDRESS" + ike_local_address = "${tencentcloud_vpn_gateway.vpn.public_ip_address}" + ike_remote_identity = "ADDRESS" + ike_remote_address = "${tencentcloud_vpn_customer_gateway.cgw.public_ip_address}" + ike_dh_group_name = "GROUP1" + ike_sa_lifetime_seconds = 86400 + ipsec_encrypt_algorithm = "3DES-CBC" + ipsec_integrity_algorithm = "MD5" + ipsec_sa_lifetime_seconds = 3600 + ipsec_pfs_dh_group = "DH-GROUP1" + ipsec_sa_lifetime_traffic = 2560 + + security_group_policy { + local_cidr_block = "172.16.0.0/16" + remote_cidr_block = ["3.3.3.0/32", ] + } + tags = { + test = "test" + } +} + +data "tencentcloud_vpn_connections" "connections" { + id = "${tencentcloud_vpn_connection.connection.id}" +} +` diff --git a/tencentcloud/data_source_tc_vpn_customer_gateways.go b/tencentcloud/data_source_tc_vpn_customer_gateways.go new file mode 100644 index 0000000000..64cf7c0fad --- /dev/null +++ b/tencentcloud/data_source_tc_vpn_customer_gateways.go @@ -0,0 +1,201 @@ +/* +Use this data source to query detailed information of VPN customer gateways. + +Example Usage + +```hcl +data "tencentcloud_nat_gateways" "foo" { + name = "main" + id = "cgw-xfqag" + public_ip_address = "1.1.1.1" + tags = { + test = "tf" + } +} +``` +*/ +package tencentcloud + +import ( + "context" + "log" + "reflect" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + vpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312" +) + +func dataSourceTencentCloudVpnCustomerGateways() *schema.Resource { + return &schema.Resource{ + Read: dataSourceTencentCloudVpnCustomerGatewaysRead, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateStringLengthInRange(1, 60), + Description: "Name of the customer gateway. The length of character is limited to 1-60.", + }, + "id": { + Type: schema.TypeString, + Optional: true, + Description: "ID of the VPN customer gateway.", + }, + "public_ip_address": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateIp, + Description: "Public ip address of the VPN customer gateway.", + }, + "tags": { + Type: schema.TypeMap, + Optional: true, + Description: "Tags of the VPN customer gateway to be queried.", + }, + "result_output_file": { + Type: schema.TypeString, + Optional: true, + Description: "Used to save results.", + }, + + // Computed values + "gateway_list": { + Type: schema.TypeList, + Computed: true, + Description: "Information list of the dedicated gateways.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + Description: "ID of the VPN customer gateway.", + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: "Name of the VPN customer gateway.", + }, + "public_ip_address": { + Type: schema.TypeString, + Computed: true, + Description: "Public ip address of the VPN customer gateway.", + }, + "tags": { + Type: schema.TypeMap, + Computed: true, + Description: "Tags of the VPN customer gateway.", + }, + "create_time": { + Type: schema.TypeString, + Computed: true, + Description: "Create time of the VPN customer gateway.", + }, + }, + }, + }, + }, + } +} + +func dataSourceTencentCloudVpnCustomerGatewaysRead(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("data_source.tencentcloud_nat_gateways.read")() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), "logId", logId) + + tagService := TagService{client: meta.(*TencentCloudClient).apiV3Conn} + region := meta.(*TencentCloudClient).apiV3Conn.Region + + request := vpc.NewDescribeCustomerGatewaysRequest() + + params := make(map[string]string) + if v, ok := d.GetOk("id"); ok { + params["customer-gateway-id"] = v.(string) + } + if v, ok := d.GetOk("name"); ok { + params["customer-gateway-name"] = v.(string) + } + if v, ok := d.GetOk("public_ip_address"); ok { + params["ip-address"] = v.(string) + } + tags := getTags(d, "tags") + + request.Filters = make([]*vpc.Filter, 0, len(params)) + for k, v := range params { + filter := &vpc.Filter{ + Name: stringToPointer(k), + Values: []*string{stringToPointer(v)}, + } + request.Filters = append(request.Filters, filter) + } + offset := uint64(0) + request.Offset = &offset + result := make([]*vpc.CustomerGateway, 0) + limit := uint64(VPN_DESCRIBE_LIMIT) + for { + var response *vpc.DescribeCustomerGatewaysResponse + //add for cycle and add this to nat too + err := resource.Retry(readRetryTimeout, func() *resource.RetryError { + result, e := meta.(*TencentCloudClient).apiV3Conn.UseVpcClient().DescribeCustomerGateways(request) + if e != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return retryError(e) + } + response = result + return nil + }) + if err != nil { + log.Printf("[CRITAL]%s read VPN customer gateway failed, reason:%s\n", logId, err.Error()) + return err + } else { + result = append(result, response.Response.CustomerGatewaySet...) + if len(response.Response.CustomerGatewaySet) < VPN_DESCRIBE_LIMIT { + break + } else { + offset = offset + limit + request.Offset = &offset + } + } + } + ids := make([]string, 0, len(result)) + gatewayList := make([]map[string]interface{}, 0, len(result)) + for _, gateway := range result { + //tags + respTags, err := tagService.DescribeResourceTags(ctx, "vpc", "cgw", region, *gateway.CustomerGatewayId) + if err != nil { + return err + } + if len(tags) > 0 { + if !reflect.DeepEqual(respTags, tags) { + continue + } + } + + mapping := map[string]interface{}{ + "id": *gateway.CustomerGatewayId, + "name": *gateway.CustomerGatewayName, + "public_ip_address": *gateway.IpAddress, + "create_time": *gateway.CreatedTime, + "tags": respTags, + } + gatewayList = append(gatewayList, mapping) + ids = append(ids, *gateway.CustomerGatewayId) + } + d.SetId(dataResourceIdsHash(ids)) + if e := d.Set("gateway_list", gatewayList); e != nil { + log.Printf("[CRITAL]%s provider set gateway list fail, reason:%s\n", logId, e.Error()) + return e + } + + output, ok := d.GetOk("result_output_file") + if ok && output.(string) != "" { + if e := writeToFile(output.(string), gatewayList); e != nil { + return e + } + } + + return nil + +} diff --git a/tencentcloud/data_source_tc_vpn_customer_gateways_test.go b/tencentcloud/data_source_tc_vpn_customer_gateways_test.go new file mode 100644 index 0000000000..0c2350cf19 --- /dev/null +++ b/tencentcloud/data_source_tc_vpn_customer_gateways_test.go @@ -0,0 +1,40 @@ +package tencentcloud + +import ( + "testing" + + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccTencentCloudVpnCustomerGatewaysDataSource(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccTencentCloudVpnCustomerGatewaysDataSourceConfig_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckTencentCloudDataSourceID("data.tencentcloud_vpn_customer_gateways.cgws"), + resource.TestCheckResourceAttr("data.tencentcloud_vpn_customer_gateways.cgws", "gateway_list.#", "1"), + resource.TestCheckResourceAttr("data.tencentcloud_vpn_customer_gateways.cgws", "gateway_list.0.name", "terraform_test"), + resource.TestCheckResourceAttr("data.tencentcloud_vpn_customer_gateways.cgws", "gateway_list.0.public_ip_address", "1.1.1.2"), + resource.TestCheckResourceAttr("data.tencentcloud_vpn_customer_gateways.cgws", "gateway_list.0.tags.test", "tf"), + ), + }, + }, + }) +} + +const testAccTencentCloudVpnCustomerGatewaysDataSourceConfig_basic = ` +resource "tencentcloud_vpn_customer_gateway" "my_cgw" { + name = "terraform_test" + public_ip_address = "1.1.1.2" + tags = { + test = "tf" + } +} + +data "tencentcloud_vpn_customer_gateways" "cgws" { + id = "${tencentcloud_vpn_customer_gateway.my_cgw.id}" +} +` diff --git a/tencentcloud/data_source_tc_vpn_gateways.go b/tencentcloud/data_source_tc_vpn_gateways.go new file mode 100644 index 0000000000..4cb714b0d2 --- /dev/null +++ b/tencentcloud/data_source_tc_vpn_gateways.go @@ -0,0 +1,285 @@ +/* +Use this data source to query detailed information of VPN gateways. + +Example Usage + +```hcl +data "tencentcloud_vpn_gateways" "foo" { + name = "main" + id = "vpngw-8ccsnclt" + public_ip_address = "1.1.1.1" + zone = "ap-guangzhou-3" + vpc_id = "vpc-dk8zmwuf" + tags = { + test = "tf" + } +} +``` +*/ +package tencentcloud + +import ( + "context" + "log" + "reflect" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + vpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312" +) + +func dataSourceTencentCloudVpnGateways() *schema.Resource { + return &schema.Resource{ + Read: dataSourceTencentCloudVpnGatewaysRead, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateStringLengthInRange(1, 60), + Description: "Name of the VPN gateway. The length of character is limited to 1-60.", + }, + "id": { + Type: schema.TypeString, + Optional: true, + Description: "ID of the VPN gateway.", + }, + "public_ip_address": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validateIp, + Description: "Public ip address of the VPN gateway.", + }, + "zone": { + Type: schema.TypeString, + Optional: true, + Description: "Zone of the VPN gateway.", + }, + "vpc_id": { + Type: schema.TypeString, + Optional: true, + Description: "ID of the VPC.", + }, + "tags": { + Type: schema.TypeMap, + Optional: true, + Description: "Tags of the VPN gateway to be queried.", + }, + "result_output_file": { + Type: schema.TypeString, + Optional: true, + Description: "Used to save results.", + }, + + // Computed values + "gateway_list": { + Type: schema.TypeList, + Computed: true, + Description: "Information list of the dedicated gateways.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + Description: "ID of the VPN gateway.", + }, + "name": { + Type: schema.TypeString, + Computed: true, + Description: "Name of the VPN gateway.", + }, + "vpc_id": { + Type: schema.TypeString, + Computed: true, + Description: "ID of the VPC.", + }, + "bandwidth": { + Type: schema.TypeInt, + Computed: true, + Description: "The maximum public network output bandwidth of nat gateway (unit: Mbps), the available values include: 5,10,20,50,100. Default is 5.", + }, + "public_ip_address": { + Type: schema.TypeString, + Computed: true, + Description: "Public ip of the VPN gateway.", + }, + "type": { + Type: schema.TypeString, + Computed: true, + Description: "Type of gateway instance, valid values are `IPSEC`, `SSL`.", + }, + "state": { + Type: schema.TypeString, + Computed: true, + Description: "State of the VPN gateway, valid values are `PENDING`, `DELETING`, `AVAILABLE`.", + }, + "prepaid_renew_flag": { + Type: schema.TypeString, + Computed: true, + Description: "Flag indicates whether to renew or not, valid values are `NOTIFY_AND_RENEW`, `NOTIFY_AND_AUTO_RENEW`, `NOT_NOTIFY_AND_NOT_RENEW`.", + }, + "charge_type": { + Type: schema.TypeString, + Computed: true, + Description: "Charge Type of the VPN gateway, valid values are `PREPAID`, `POSTPAID_BY_HOUR` and default is `POSTPAID_BY_HOUR`.", + }, + "expired_time": { + Type: schema.TypeString, + Computed: true, + Description: "Expired time of the VPN gateway when charge type is `PREPAID`.", + }, + "is_address_blocked": { + Type: schema.TypeBool, + Computed: true, + Description: "Indicates whether ip address is blocked.", + }, + "new_purchase_plan": { + Type: schema.TypeString, + Computed: true, + Description: "The plan of new purchase, valid value is `PREPAID_TO_POSTPAID`.", + }, + "restrict_state": { + Type: schema.TypeString, + Computed: true, + Description: "Restrict state of VPN gateway, valid values are `PRETECIVELY_ISOLATED`, `NORMAL`.", + }, + "zone": { + Type: schema.TypeString, + Computed: true, + Description: "Zone of the VPN gateway.", + }, + "tags": { + Type: schema.TypeMap, + Computed: true, + Description: "A list of tags used to associate different resources.", + }, + "create_time": { + Type: schema.TypeString, + Computed: true, + Description: "Create time of the VPN gateway.", + }, + }, + }, + }, + }, + } +} + +func dataSourceTencentCloudVpnGatewaysRead(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("data_source.tencentcloud_vpn_gateways.read")() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), "logId", logId) + + tagService := TagService{client: meta.(*TencentCloudClient).apiV3Conn} + region := meta.(*TencentCloudClient).apiV3Conn.Region + + request := vpc.NewDescribeVpnGatewaysRequest() + + params := make(map[string]string) + if v, ok := d.GetOk("id"); ok { + params["vpn-gateway-id"] = v.(string) + } + if v, ok := d.GetOk("name"); ok { + params["vpn-gateway-name"] = v.(string) + } + if v, ok := d.GetOk("public_ip_address"); ok { + params["ip-address"] = v.(string) + } + if v, ok := d.GetOk("vpc_id"); ok { + params["vpc-id"] = v.(string) + } + if v, ok := d.GetOk("zone"); ok { + params["zone"] = v.(string) + } + + tags := getTags(d, "tags") + + request.Filters = make([]*vpc.FilterObject, 0, len(params)) + for k, v := range params { + filter := &vpc.FilterObject{ + Name: stringToPointer(k), + Values: []*string{stringToPointer(v)}, + } + request.Filters = append(request.Filters, filter) + } + offset := uint64(0) + request.Offset = &offset + result := make([]*vpc.VpnGateway, 0) + limit := uint64(VPN_DESCRIBE_LIMIT) + for { + var response *vpc.DescribeVpnGatewaysResponse + //add for cycle and add this to nat too + err := resource.Retry(readRetryTimeout, func() *resource.RetryError { + result, e := meta.(*TencentCloudClient).apiV3Conn.UseVpcClient().DescribeVpnGateways(request) + if e != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return retryError(e) + } + response = result + return nil + }) + if err != nil { + log.Printf("[CRITAL]%s read VPN gateway failed, reason:%s\n ", logId, err.Error()) + return err + } else { + result = append(result, response.Response.VpnGatewaySet...) + if len(response.Response.VpnGatewaySet) < VPN_DESCRIBE_LIMIT { + break + } else { + offset = offset + limit + request.Offset = &offset + } + } + } + ids := make([]string, 0, len(result)) + gatewayList := make([]map[string]interface{}, 0, len(result)) + for _, gateway := range result { + //tags + respTags, err := tagService.DescribeResourceTags(ctx, "vpc", "vpngw", region, *gateway.VpnGatewayId) + if err != nil { + return err + } + if len(tags) > 0 { + if !reflect.DeepEqual(respTags, tags) { + continue + } + } + + mapping := map[string]interface{}{ + "id": *gateway.VpnGatewayId, + "name": *gateway.VpnGatewayName, + "public_ip_address": *gateway.PublicIpAddress, + "create_time": *gateway.CreatedTime, + "prepaid_renew_flag": *gateway.RenewFlag, + "state": *gateway.State, + "charge_type": *gateway.InstanceChargeType, + "expired_time": *gateway.ExpiredTime, + "is_address_blocked": *gateway.IsAddressBlocked, + "bandwidth": int(*gateway.InternetMaxBandwidthOut), + "new_purchase_plan": *gateway.NewPurchasePlan, + "restrict_state": *gateway.RestrictState, + "zone": *gateway.Zone, + "type": *gateway.Type, + "tags": respTags, + } + gatewayList = append(gatewayList, mapping) + ids = append(ids, *gateway.VpnGatewayId) + } + d.SetId(dataResourceIdsHash(ids)) + if e := d.Set("gateway_list", gatewayList); e != nil { + log.Printf("[CRITAL]%s provider set gateway list fail, reason:%s\n ", logId, e.Error()) + return e + } + + output, ok := d.GetOk("result_output_file") + if ok && output.(string) != "" { + if e := writeToFile(output.(string), gatewayList); e != nil { + return e + } + } + + return nil + +} diff --git a/tencentcloud/data_source_tc_vpn_gateways_test.go b/tencentcloud/data_source_tc_vpn_gateways_test.go new file mode 100644 index 0000000000..d03b043645 --- /dev/null +++ b/tencentcloud/data_source_tc_vpn_gateways_test.go @@ -0,0 +1,48 @@ +package tencentcloud + +import ( + "testing" + + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccTencentCloudVpnGatewaysDataSource(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccTencentCloudVpnGatewaysDataSourceConfig_basic, + Check: resource.ComposeTestCheckFunc( + testAccCheckTencentCloudDataSourceID("data.tencentcloud_vpn_gateways.cgws"), + resource.TestCheckResourceAttr("data.tencentcloud_vpn_gateways.cgws", "gateway_list.#", "1"), + resource.TestCheckResourceAttr("data.tencentcloud_vpn_gateways.cgws", "gateway_list.0.name", "terraform_test"), + resource.TestCheckResourceAttr("data.tencentcloud_vpn_gateways.cgws", "gateway_list.0.bandwidth", "10"), + resource.TestCheckResourceAttr("data.tencentcloud_vpn_gateways.cgws", "gateway_list.0.tags.test", "tf"), + ), + }, + }, + }) +} + +const testAccTencentCloudVpnGatewaysDataSourceConfig_basic = ` +# Create VPC +data "tencentcloud_vpc_instances" "foo" { + name = "Default-VPC" +} + +resource "tencentcloud_vpn_gateway" "my_cgw" { + name = "terraform_test" + vpc_id = "${data.tencentcloud_vpc_instances.foo.instance_list.0.vpc_id}" + bandwidth = 10 + zone = "ap-guangzhou-3" + + tags = { + test = "tf" + } +} + +data "tencentcloud_vpn_gateways" "cgws" { + id = "${tencentcloud_vpn_gateway.my_cgw.id}" +} +` diff --git a/tencentcloud/extension_vpc.go b/tencentcloud/extension_vpc.go index bb8b6a757f..d2897cec15 100644 --- a/tencentcloud/extension_vpc.go +++ b/tencentcloud/extension_vpc.go @@ -89,6 +89,183 @@ const ( ENI_IP_DELETING = "DELETING" ) +/* +NAT +*/ + +const ( + NAT_DESCRIBE_LIMIT = 100 +) + +/* +VPN +*/ + +const ( + VPN_DESCRIBE_LIMIT = 100 +) + +const ( + VPN_STATE_PENDING = "PENDING" + VPN_STATE_DELETING = "UDP" + VPN_STATE_AVAILABLE = "AVAILABLE" +) + +var VPN_STATE = []string{ + VPN_STATE_PENDING, + VPN_STATE_DELETING, + VPN_STATE_AVAILABLE, +} + +const ( + VPN_PERIOD_PREPAID_RENEW_FLAG_NOTIFY = "NOFITY_AND_RENEW`" + VPN_PERIOD_PREPAID_RENEW_FLAG_AUTO_NOTIFY = "NOTIFY_AND_AUTO_RENEW" + VPN_PERIOD_PREPAID_RENEW_FLAG_NOT = "NOT_NOTIFY_AND_NOT_RENEW`" +) + +var VPN_PERIOD_PREPAID_RENEW_FLAG = []string{ + VPN_PERIOD_PREPAID_RENEW_FLAG_NOTIFY, + VPN_PERIOD_PREPAID_RENEW_FLAG_AUTO_NOTIFY, + VPN_PERIOD_PREPAID_RENEW_FLAG_NOT, +} + +const ( + VPN_CHARGE_TYPE_PREPAID = "PREPAID" + VPN_CHARGE_TYPE_POSTPAID_BY_HOUR = "POSTPAID_BY_HOUR" +) + +var VPN_CHARGE_TYPE = []string{ + VPN_CHARGE_TYPE_PREPAID, + VPN_CHARGE_TYPE_POSTPAID_BY_HOUR, +} + +const ( + VPN_PURCHASE_PLAN_PRE_POST = "PREPAID_TO_POSTPAID" +) + +var VPN_PURCHASE_PLAN = []string{ + VPN_PURCHASE_PLAN_PRE_POST, +} + +const ( + VPN_RESTRICT_STATE_NORMAL = "NORMAL" + VPN_RESTRICT_STATE_ISOLATE = "PRETECIVELY_ISOLATED" +) + +var VPN_RESTRICT_STATE = []string{ + VPN_RESTRICT_STATE_NORMAL, + VPN_RESTRICT_STATE_ISOLATE, +} + +const ( + VPN_IKE_PROPO_ENCRY_ALGORITHM_3DESCBC = "3DES-CBC" + VPN_IKE_PROPO_ENCRY_ALGORITHM_AESCBC128 = "AES-CBC-128" + VPN_IKE_PROPO_ENCRY_ALGORITHM_AESCBC192 = "AES-CBS-192`" + VPN_IKE_PROPO_ENCRY_ALGORITHM_AESCBC256 = "AES-CBC-256" + VPN_IKE_PROPO_ENCRY_ALGORITHM_DESCBC = "DES-CBC" +) + +var VPN_IKE_PROPO_ENCRY_ALGORITHM = []string{ + VPN_IKE_PROPO_ENCRY_ALGORITHM_3DESCBC, + VPN_IKE_PROPO_ENCRY_ALGORITHM_AESCBC128, + VPN_IKE_PROPO_ENCRY_ALGORITHM_AESCBC192, + VPN_IKE_PROPO_ENCRY_ALGORITHM_AESCBC256, + VPN_IKE_PROPO_ENCRY_ALGORITHM_DESCBC, +} + +const ( + VPN_IKE_PROPO_AUTHEN_ALGORITHM_SHA = "SHA" + VPN_IKE_PROPO_AUTHEN_ALGORITHM_MD5 = "MD5" +) + +var VPN_IKE_PROPO_AUTHEN_ALGORITHM = []string{ + VPN_IKE_PROPO_AUTHEN_ALGORITHM_SHA, + VPN_IKE_PROPO_AUTHEN_ALGORITHM_MD5, +} + +const ( + VPN_IPSEC_INTEGRITY_ALGORITHM_SHA1 = "SHA1" + VPN_IPSEC_INTEGRITY_ALGORITHM_MD5 = "MD5" +) + +var VPN_IPSEC_INTEGRITY_ALGORITHM = []string{ + VPN_IPSEC_INTEGRITY_ALGORITHM_SHA1, + VPN_IPSEC_INTEGRITY_ALGORITHM_MD5, +} + +const ( + VPN_IKE_EXCHANGE_MODE_AGGRESSIVE = "AGGRESSIVE" + VPN_IKE_EXCHANGE_MODE_MAIN = "MAIN" +) + +var VPN_IKE_EXCHANGE_MODE = []string{ + VPN_IKE_EXCHANGE_MODE_AGGRESSIVE, + VPN_IKE_EXCHANGE_MODE_MAIN, +} + +const ( + VPN_IKE_IDENTITY_ADDRESS = "ADDRESS" + VPN_IKE_IDENTITY_FQDN = "FQDN" +) + +var VPN_IKE_IDENTITY = []string{ + VPN_IKE_IDENTITY_ADDRESS, + VPN_IKE_IDENTITY_FQDN, +} + +const ( + VPN_IKE_DH_GROUP_NAME_GROUP1 = "GROUP1" + VPN_IKE_DH_GROUP_NAME_GROUP2 = "GROUP2" + VPN_IKE_DH_GROUP_NAME_GROUP5 = "GROUP5" + VPN_IKE_DH_GROUP_NAME_GROUP14 = "GROUP14" + VPN_IKE_DH_GROUP_NAME_GROUP24 = "GROUP24" +) + +var VPN_IKE_DH_GROUP_NAME = []string{ + VPN_IKE_DH_GROUP_NAME_GROUP1, + VPN_IKE_DH_GROUP_NAME_GROUP2, + VPN_IKE_DH_GROUP_NAME_GROUP5, + VPN_IKE_DH_GROUP_NAME_GROUP14, + VPN_IKE_DH_GROUP_NAME_GROUP24, +} + +const ( + VPN_IPSEC_PFS_DH_GROUP_NAME_GROUP1 = "DH-GROUP1" + VPN_IPSEC_PFS_DH_GROUP_NAME_GROUP2 = "DH-GROUP2" + VPN_IPSEC_PFS_DH_GROUP_NAME_GROUP5 = "DH-GROUP5" + VPN_IPSEC_PFS_DH_GROUP_NAME_GROUP14 = "DH-GROUP14" + VPN_IPSEC_PFS_DH_GROUP_NAME_GROUP24 = "DH-GROUP24" + VPN_IPSEC_PFS_DH_GROUP_NAME_NULL = "NULL" +) + +var VPN_IPSEC_PFS_DH_GROUP_NAME = []string{ + VPN_IPSEC_PFS_DH_GROUP_NAME_GROUP1, + VPN_IPSEC_PFS_DH_GROUP_NAME_GROUP2, + VPN_IPSEC_PFS_DH_GROUP_NAME_GROUP5, + VPN_IPSEC_PFS_DH_GROUP_NAME_GROUP14, + VPN_IPSEC_PFS_DH_GROUP_NAME_GROUP24, + VPN_IPSEC_PFS_DH_GROUP_NAME_NULL, +} + +const ( + VPN_IPSEC_ENCRY_ALGORITHM_3DESCBC = "3DES-CBC" + VPN_IPSEC_ENCRY_ALGORITHM_AESCBC128 = "AES-CBC-128" + VPN_IPSEC_ENCRY_ALGORITHM_AESCBC192 = "AES-CBS-192`" + VPN_IPSEC_ENCRY_ALGORITHM_AESCBC256 = "AES-CBC-256" + VPN_IPSEC_ENCRY_ALGORITHM_DESCBC = "DES-CBC" + VPN_IPSEC_ENCRY_ALGORITHM_NULL = "NULL" +) + +var VPN_IPSEC_ENCRY_ALGORITHM = []string{ + VPN_IPSEC_ENCRY_ALGORITHM_3DESCBC, + VPN_IPSEC_ENCRY_ALGORITHM_AESCBC128, + VPN_IPSEC_ENCRY_ALGORITHM_AESCBC192, + VPN_IPSEC_ENCRY_ALGORITHM_AESCBC256, + VPN_IPSEC_ENCRY_ALGORITHM_DESCBC, + VPN_IPSEC_ENCRY_ALGORITHM_NULL, +} + const ( VPCNotFound = "ResourceNotFound" ) + diff --git a/tencentcloud/provider.go b/tencentcloud/provider.go index da39528480..dea79cae89 100644 --- a/tencentcloud/provider.go +++ b/tencentcloud/provider.go @@ -97,6 +97,9 @@ Data Sources tencentcloud_vpc_instances tencentcloud_vpc_route_tables tencentcloud_vpc_subnets + tencentcloud_vpn_customer_gateways + tencentcloud_vpn_gateways + tencentcloud_vpn_connections AS Resources tencentcloud_as_scaling_config @@ -217,6 +220,10 @@ VPC Resources tencentcloud_dnat tencentcloud_nat_gateway +VPN Resources + tencentcloud_vpn_customer_gateway + tencentcloud_vpn_gateway + tencentcloud_vpn_connection */ package tencentcloud @@ -344,6 +351,9 @@ func Provider() terraform.ResourceProvider { "tencentcloud_scf_functions": dataSourceTencentCloudScfFunctions(), "tencentcloud_scf_namespaces": dataSourceTencentCloudScfNamespaces(), "tencentcloud_scf_logs": dataSourceTencentCloudScfLogs(), + "tencentcloud_vpn_customer_gateways": dataSourceTencentCloudVpnCustomerGateways(), + "tencentcloud_vpn_gateways": dataSourceTencentCloudVpnGateways(), + "tencentcloud_vpn_connections": dataSourceTencentCloudVpnConnections(), }, ResourcesMap: map[string]*schema.Resource{ @@ -428,6 +438,9 @@ func Provider() terraform.ResourceProvider { "tencentcloud_cfs_access_rule": resourceTencentCloudCfsAccessRule(), "tencentcloud_scf_function": resourceTencentCloudScfFunction(), "tencentcloud_scf_namespace": resourceTencentCloudScfNamespace(), + "tencentcloud_vpn_customer_gateway": resourceTencentCloudVpnCustomerGateway(), + "tencentcloud_vpn_gateway": resourceTencentCloudVpnGateway(), + "tencentcloud_vpn_connection": resourceTencentCloudVpnConnection(), }, ConfigureFunc: providerConfigure, diff --git a/tencentcloud/resource_tc_vpn_connection.go b/tencentcloud/resource_tc_vpn_connection.go new file mode 100644 index 0000000000..adc87bd01b --- /dev/null +++ b/tencentcloud/resource_tc_vpn_connection.go @@ -0,0 +1,817 @@ +/* +Provides a resource to create a VPN connection. + +Example Usage + +```hcl +resource "tencentcloud_vpn_connection" "foo" { + name = "vpn_connection_test" + vpc_id = "vpc-dk8zmwuf" + vpn_gateway_id = "vpngw-8ccsnclt" + customer_gateway_id = "cgw-xfqag" + pre_share_key = "testt" + ike_proto_encry_algorithm = "3DES-CBC" + ike_proto_authen_algorithm = "SHA" + ike_local_identity = "ADDRESS" + ike_exchange_mode = "AGGRESSIVE" + ike_local_address = "1.1.1.1" + ike_remote_identity = "ADDRESS" + ike_remote_address = "2.2.2.2" + ike_dh_group_name = "GROUP2" + ike_sa_lifetime_seconds = 86401 + ipsec_encrypt_algorithm = "3DES-CBC" + ipsec_integrity_algorithm = "SHA1" + ipsec_sa_lifetime_seconds = 7200 + ipsec_pfs_dh_group = "NULL" + ipsec_sa_lifetime_traffic = 2570 + + security_group_policy { + local_cidr_block = "172.16.0.0/16" + remote_cidr_block = ["2.2.2.0/26", ] + } + tags = { + test = "testt" + } +} +``` + +Import + +VPN connection can be imported using the id, e.g. + +``` +$ terraform import tencentcloud_vpn_connection.foo vpnx-nadifg3s +``` +*/ +package tencentcloud + +import ( + "context" + "fmt" + "log" + "time" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors" + vpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312" +) + +func resourceTencentCloudVpnConnection() *schema.Resource { + return &schema.Resource{ + Create: resourceTencentCloudVpnConnectionCreate, + Read: resourceTencentCloudVpnConnectionRead, + Update: resourceTencentCloudVpnConnectionUpdate, + Delete: resourceTencentCloudVpnConnectionDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateStringLengthInRange(1, 60), + Description: "Name of the VPN connection. The length of character is limited to 1-60.", + }, + "vpc_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "ID of the VPC.", + }, + "customer_gateway_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "ID of the customer gateway.", + }, + "vpn_gateway_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "ID of the VPN gateway.", + }, + "pre_share_key": { + Type: schema.TypeString, + Required: true, + Description: "Pre-shared key of the VPN connection.", + }, + "security_group_policy": { + Type: schema.TypeList, + Required: true, + Description: "Security group policy of the VPN connection.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "local_cidr_block": { + Type: schema.TypeString, + Required: true, + Description: "Local cidr block.", + }, + "remote_cidr_block": { + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: "Remote cidr block list.", + }, + }, + }, + }, + "ike_proto_encry_algorithm": { + Type: schema.TypeString, + Optional: true, + Default: VPN_IKE_PROPO_ENCRY_ALGORITHM_3DESCBC, + ValidateFunc: validateAllowedStringValue(VPN_IKE_PROPO_ENCRY_ALGORITHM), + Description: "Proto encrypt algorithm of the IKE operation specification, valid values are `3DES-CBC`, `AES-CBC-128`, `AES-CBC-128`, `AES-CBC-256`, `DES-CBC`. Default value is `3DES-CBC`.", + }, + "ike_proto_authen_algorithm": { + Type: schema.TypeString, + Optional: true, + Default: VPN_IKE_PROPO_AUTHEN_ALGORITHM_MD5, + ValidateFunc: validateAllowedStringValue(VPN_IKE_PROPO_AUTHEN_ALGORITHM), + Description: "Proto authenticate algorithm of the IKE operation specification, valid values are `MD5`, `SHA`. Default Value is `MD5`.", + }, + "ike_exchange_mode": { + Type: schema.TypeString, + Optional: true, + Default: VPN_IKE_EXCHANGE_MODE_MAIN, + ValidateFunc: validateAllowedStringValue(VPN_IKE_EXCHANGE_MODE), + Description: "Exchange mode of the IKE operation specification, valid values are `AGGRESSIVE`, `MAIN`. Default value is `MAIN`.", + }, + "ike_local_identity": { + Type: schema.TypeString, + Optional: true, + Default: VPN_IKE_IDENTITY_ADDRESS, + ValidateFunc: validateAllowedStringValue(VPN_IKE_IDENTITY), + Description: "Local identity way of IKE operation specification, valid values are `ADDRESS`, `FQDN`. Default value is `ADDRESS`.", + }, + "ike_remote_identity": { + Type: schema.TypeString, + Optional: true, + Default: VPN_IKE_IDENTITY_ADDRESS, + ValidateFunc: validateAllowedStringValue(VPN_IKE_IDENTITY), + Description: "Remote identity way of IKE operation specification, valid values are `ADDRESS`, `FQDN`. Default value is `ADDRESS`.", + }, + "ike_local_address": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"ike_local_fqdn_name"}, + Description: "Local address of IKE operation specification, valid when ike_local_identity is `ADDRESS`, generally the value is public_ip_address of the related VPN gateway.", + }, + "ike_remote_address": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"ike_remote_fqdn_name"}, + Description: "Remote address of IKE operation specification, valid when ike_remote_identity is `ADDRESS`, generally the value is public_ip_address of the related customer gateway.", + }, + "ike_local_fqdn_name": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"ike_local_address"}, + Description: "Local FQDN name of the IKE operation specification.", + }, + "ike_remote_fqdn_name": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"ike_remote_address"}, + Description: "Remote FQDN name of the IKE operation specification.", + }, + "ike_dh_group_name": { + Type: schema.TypeString, + Optional: true, + Default: VPN_IKE_DH_GROUP_NAME_GROUP1, + ValidateFunc: validateAllowedStringValue(VPN_IKE_DH_GROUP_NAME), + Description: "DH group name of the IKE operation specification, valid values are `GROUP1`, `GROUP2`, `GROUP5`, `GROUP14`, `GROUP24`. Default value is `GROUP1`.", + }, + "ike_sa_lifetime_seconds": { + Type: schema.TypeInt, + Optional: true, + Default: 86400, + ValidateFunc: validateIntegerInRange(60, 604800), + Description: "SA lifetime of the IKE operation specification, unit is `second`. The value ranges from 60 to 604800. Default value is 86400 seconds.", + }, + "ike_version": { + Type: schema.TypeString, + Optional: true, + Default: "IKEV1", + Description: "Version of the IKE operation specification. Default value is `IKEV1`.", + }, + "ipsec_encrypt_algorithm": { + Type: schema.TypeString, + Optional: true, + Default: VPN_IPSEC_ENCRY_ALGORITHM_3DESCBC, + ValidateFunc: validateAllowedStringValue(VPN_IPSEC_ENCRY_ALGORITHM), + Description: "Encrypt algorithm of the IPSEC operation specification, valid values are `3DES-CBC`, `AES-CBC-128`, `AES-CBC-128`, `AES-CBC-256`, `DES-CBC`. Default value is `3DES-CBC`.", + }, + "ipsec_integrity_algorithm": { + Type: schema.TypeString, + Optional: true, + Default: VPN_IPSEC_INTEGRITY_ALGORITHM_MD5, + ValidateFunc: validateAllowedStringValue(VPN_IPSEC_INTEGRITY_ALGORITHM), + Description: "Integrity algorithm of the IPSEC operation specification, valid values are `SHA1`, `MD5`. Default value is `MD5`.", + }, + "ipsec_sa_lifetime_seconds": { + Type: schema.TypeInt, + Optional: true, + Default: 3600, + ValidateFunc: validateIntegerInRange(180, 604800), + Description: "SA lifetime of the IPSEC operation specification, unit is `second`. The value ranges from 180 to 604800. Default value is 3600 seconds.", + }, + "ipsec_pfs_dh_group": { + Type: schema.TypeString, + Optional: true, + Default: "NULL", + ValidateFunc: validateAllowedStringValue(VPN_IPSEC_PFS_DH_GROUP_NAME), + Description: "PFS DH group , valid values are `GROUP1`, `GROUP2`, `GROUP5`, `GROUP14`, `GROUP24`, `NULL`. Default value is `NULL`.", + }, + "ipsec_sa_lifetime_traffic": { + Type: schema.TypeInt, + Optional: true, + Default: 1843200, + ValidateFunc: validateIntegerInRange(2560, 4294967295), + Description: "SA lifetime of the IPSEC operation specification, unit is `KB`. The value ranges from 2560 to 4294967295. Default value is 1843200.", + }, + "tags": { + Type: schema.TypeMap, + Optional: true, + Description: "A list of tags used to associate different resources.", + }, + "create_time": { + Type: schema.TypeString, + Computed: true, + Description: "Create time of the VPN connection.", + }, + "vpn_proto": { + Type: schema.TypeString, + Computed: true, + Description: "Vpn proto of the VPN connection.", + }, + "encrypt_proto": { + Type: schema.TypeString, + Computed: true, + Description: "Encrypt proto of the VPN connection.", + }, + "route_type": { + Type: schema.TypeString, + Computed: true, + Description: "Route type of the VPN connection.", + }, + "state": { + Type: schema.TypeString, + Computed: true, + Description: "State of the connection, values are `PENDING`, `AVAILABLE`, `DELETING`.", + }, + "net_status": { + Type: schema.TypeString, + Computed: true, + Description: "Net status of the VPN connection, values are `AVAILABLE`.", + }, + }, + } +} + +func resourceTencentCloudVpnConnectionCreate(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_vpn_connection.create")() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), "logId", logId) + + request := vpc.NewCreateVpnConnectionRequest() + request.VpnConnectionName = stringToPointer(d.Get("name").(string)) + request.VpcId = stringToPointer(d.Get("vpc_id").(string)) + request.VpnGatewayId = stringToPointer(d.Get("vpn_gateway_id").(string)) + request.CustomerGatewayId = stringToPointer(d.Get("customer_gateway_id").(string)) + request.PreShareKey = stringToPointer(d.Get("pre_share_key").(string)) + + //set up SecurityPolicyDatabases + + sgps := d.Get("security_group_policy").([]interface{}) + if len(sgps) < 1 { + return fmt.Errorf("Para `security_group_policy` should be set at least one.") + } + for _, v := range sgps { + m := v.(map[string]interface{}) + request.SecurityPolicyDatabases = make([]*vpc.SecurityPolicyDatabase, 0, len(sgps)) + var sgp vpc.SecurityPolicyDatabase + local := m["local_cidr_block"].(string) + sgp.LocalCidrBlock = &local + // list + remoteCidrBlocks := m["remote_cidr_block"].(*schema.Set).List() + for _, vv := range remoteCidrBlocks { + remoteCidrBlock := vv.(string) + sgp.RemoteCidrBlock = append(sgp.RemoteCidrBlock, &remoteCidrBlock) + } + request.SecurityPolicyDatabases = append(request.SecurityPolicyDatabases, &sgp) + } + + //set up IKEOptionsSpecification + var ikeOptionsSpecification vpc.IKEOptionsSpecification + ikeOptionsSpecification.PropoEncryAlgorithm = stringToPointer(d.Get("ike_proto_encry_algorithm").(string)) + ikeOptionsSpecification.PropoAuthenAlgorithm = stringToPointer(d.Get("ike_proto_authen_algorithm").(string)) + ikeOptionsSpecification.ExchangeMode = stringToPointer(d.Get("ike_exchange_mode").(string)) + ikeOptionsSpecification.LocalIdentity = stringToPointer(d.Get("ike_local_identity").(string)) + ikeOptionsSpecification.RemoteIdentity = stringToPointer(d.Get("ike_remote_identity").(string)) + if *ikeOptionsSpecification.LocalIdentity == VPN_IKE_IDENTITY_ADDRESS { + if v, ok := d.GetOk("ike_local_address"); ok { + ikeOptionsSpecification.LocalAddress = stringToPointer(v.(string)) + } else { + return fmt.Errorf("ike_local_address need to be set when ike_local_identity is `ADDRESS`.") + } + } else { + if v, ok := d.GetOk("ike_local_fqdn_name"); ok { + ikeOptionsSpecification.LocalFqdnName = stringToPointer(v.(string)) + } else { + return fmt.Errorf("ike_local_fqdn_name need to be set when ike_local_identity is `FQDN`") + } + } + if *ikeOptionsSpecification.LocalIdentity == VPN_IKE_IDENTITY_ADDRESS { + if v, ok := d.GetOk("ike_remote_address"); ok { + ikeOptionsSpecification.RemoteAddress = stringToPointer(v.(string)) + } else { + return fmt.Errorf("ike_remote_address need to be set when ike_remote_identity is `ADDRESS`.") + } + } else { + if v, ok := d.GetOk("ike_remote_fqdn_name"); ok { + ikeOptionsSpecification.RemoteFqdnName = stringToPointer(v.(string)) + } else { + return fmt.Errorf("ike_remote_fqdn_name need to be set when ike_remote_identity is `FQDN`") + } + } + + ikeOptionsSpecification.DhGroupName = stringToPointer(d.Get("ike_dh_group_name").(string)) + saLifetime := d.Get("ike_sa_lifetime_seconds").(int) + saLifetime64 := uint64(saLifetime) + ikeOptionsSpecification.IKESaLifetimeSeconds = &saLifetime64 + ikeOptionsSpecification.IKEVersion = stringToPointer(d.Get("ike_version").(string)) + request.IKEOptionsSpecification = &ikeOptionsSpecification + + //set up IPSECOptionsSpecification + var ipsecOptionsSpecification vpc.IPSECOptionsSpecification + ipsecOptionsSpecification.EncryptAlgorithm = stringToPointer(d.Get("ipsec_encrypt_algorithm").(string)) + ipsecOptionsSpecification.IntegrityAlgorith = stringToPointer(d.Get("ipsec_integrity_algorithm").(string)) + ipsecSaLifetimeSeconds := d.Get("ipsec_sa_lifetime_seconds").(int) + ipsecSaLifetimeSeconds64 := uint64(ipsecSaLifetimeSeconds) + ipsecOptionsSpecification.IPSECSaLifetimeSeconds = &ipsecSaLifetimeSeconds64 + ipsecOptionsSpecification.PfsDhGroup = stringToPointer(d.Get("ipsec_pfs_dh_group").(string)) + ipsecSaLifetimeTraffic := d.Get("ipsec_sa_lifetime_traffic").(int) + ipsecSaLifetimeTraffic64 := uint64(ipsecSaLifetimeTraffic) + ipsecOptionsSpecification.IPSECSaLifetimeTraffic = &ipsecSaLifetimeTraffic64 + request.IPSECOptionsSpecification = &ipsecOptionsSpecification + + var response *vpc.CreateVpnConnectionResponse + err := resource.Retry(readRetryTimeout, func() *resource.RetryError { + result, e := meta.(*TencentCloudClient).apiV3Conn.UseVpcClient().CreateVpnConnection(request) + if e != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return retryError(e) + } + response = result + return nil + }) + if err != nil { + log.Printf("[CRITAL]%s create VPN connection failed, reason:%s\n", logId, err.Error()) + return err + } + + if response.Response.VpnConnection == nil { + return fmt.Errorf("VPN connection id is nil") + } + + vpnConnectionId := "" + //the response will return "" id + if *response.Response.VpnConnection.VpnConnectionId == "" { + idRequest := vpc.NewDescribeVpnConnectionsRequest() + idRequest.Filters = make([]*vpc.Filter, 0, 3) + params := make(map[string]string) + if v, ok := d.GetOk("vpn_gateway_id"); ok { + params["vpn-gateway-id"] = v.(string) + } + if v, ok := d.GetOk("vpc_id"); ok { + params["vpc-id"] = v.(string) + } + if v, ok := d.GetOk("customer_gateway_id"); ok { + params["customer-gateway-id"] = v.(string) + } + for k, v := range params { + filter := &vpc.Filter{ + Name: stringToPointer(k), + Values: []*string{stringToPointer(v)}, + } + idRequest.Filters = append(idRequest.Filters, filter) + } + offset := uint64(0) + idRequest.Offset = &offset + + err = resource.Retry(3*time.Minute, func() *resource.RetryError { + result, e := meta.(*TencentCloudClient).apiV3Conn.UseVpcClient().DescribeVpnConnections(idRequest) + + if e != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, idRequest.GetAction(), idRequest.ToJsonString(), e.Error()) + return retryError(e) + } else { + if len(result.Response.VpnConnectionSet) == 0 || *result.Response.VpnConnectionSet[0].VpnConnectionId == "" { + return resource.RetryableError(fmt.Errorf("Id is creating, wait...")) + } else { + vpnConnectionId = *result.Response.VpnConnectionSet[0].VpnConnectionId + return nil + } + } + }) + + if err != nil { + log.Printf("[CRITAL]%s create VPN connection failed, reason:%s\n", logId, err.Error()) + return err + } + } + + if vpnConnectionId == "" { + return fmt.Errorf("VPN connection id is nil") + } + + d.SetId(vpnConnectionId) + // must wait for finishing creating connection + statRequest := vpc.NewDescribeVpnConnectionsRequest() + statRequest.VpnConnectionIds = []*string{&vpnConnectionId} + err = resource.Retry(3*time.Minute, func() *resource.RetryError { + result, e := meta.(*TencentCloudClient).apiV3Conn.UseVpcClient().DescribeVpnConnections(statRequest) + if e != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, statRequest.GetAction(), statRequest.ToJsonString(), e.Error()) + return retryError(e) + } else { + //if not, quit + if len(result.Response.VpnConnectionSet) != 1 { + return resource.NonRetryableError(fmt.Errorf("creating error")) + } else { + if *result.Response.VpnConnectionSet[0].State == VPN_STATE_AVAILABLE { + return nil + } else { + return resource.RetryableError(fmt.Errorf("State is not available: %s, wait for state to be AVAILABLE.", *result.Response.VpnConnectionSet[0].State)) + } + } + } + }) + if err != nil { + log.Printf("[CRITAL]%s create VPN connection failed, reason:%s\n", logId, err.Error()) + return err + } + + //modify tags + if tags := getTags(d, "tags"); len(tags) > 0 { + tagService := TagService{client: meta.(*TencentCloudClient).apiV3Conn} + + region := meta.(*TencentCloudClient).apiV3Conn.Region + resourceName := BuildTagResourceName("vpc", "vpnx", region, vpnConnectionId) + + if err := tagService.ModifyTags(ctx, resourceName, tags, nil); err != nil { + return err + } + } + + return resourceTencentCloudVpnConnectionRead(d, meta) +} + +func resourceTencentCloudVpnConnectionRead(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_vpn_connection.read")() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), "logId", logId) + + connectionId := d.Id() + request := vpc.NewDescribeVpnConnectionsRequest() + request.VpnConnectionIds = []*string{&connectionId} + var response *vpc.DescribeVpnConnectionsResponse + err := resource.Retry(readRetryTimeout, func() *resource.RetryError { + result, e := meta.(*TencentCloudClient).apiV3Conn.UseVpcClient().DescribeVpnConnections(request) + if e != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return retryError(e) + } + + response = result + return nil + }) + if err != nil { + log.Printf("[CRITAL]%s read VPN connection failed, reason:%s\n", logId, err.Error()) + return err + } + if len(response.Response.VpnConnectionSet) < 1 { + d.SetId("") + return nil + } + + connection := response.Response.VpnConnectionSet[0] + d.Set("name", *connection.VpnConnectionName) + d.Set("vpc_id", *connection.VpcId) + d.Set("create_time", *connection.CreatedTime) + d.Set("vpn_gateway_id", *connection.VpnGatewayId) + d.Set("customer_gateway_id", *connection.CustomerGatewayId) + d.Set("pre_share_key", *connection.PreShareKey) + //set up SPD + d.Set("security_group_policy", flattenVpnSPDList(connection.SecurityPolicyDatabaseSet)) + + //set up IKE + d.Set("ike_proto_encry_algorithm", *connection.IKEOptionsSpecification.PropoEncryAlgorithm) + d.Set("ike_proto_authen_algorithm", *connection.IKEOptionsSpecification.PropoAuthenAlgorithm) + d.Set("ike_exchange_mode", *connection.IKEOptionsSpecification.ExchangeMode) + d.Set("ike_local_identity", *connection.IKEOptionsSpecification.LocalIdentity) + d.Set("ike_remote_idetity", *connection.IKEOptionsSpecification.RemoteIdentity) + //optional + if connection.IKEOptionsSpecification.LocalAddress != nil { + d.Set("ike_local_address", *connection.IKEOptionsSpecification.LocalAddress) + } + if connection.IKEOptionsSpecification.RemoteAddress != nil { + d.Set("ike_remote_address", *connection.IKEOptionsSpecification.RemoteAddress) + } + if connection.IKEOptionsSpecification.LocalFqdnName != nil { + d.Set("ike_local_fqdn_name", *connection.IKEOptionsSpecification.LocalFqdnName) + } + if connection.IKEOptionsSpecification.RemoteFqdnName != nil { + d.Set("ike_remote_fqdn_name", *connection.IKEOptionsSpecification.RemoteFqdnName) + } + d.Set("ike_dh_group_name", *connection.IKEOptionsSpecification.DhGroupName) + d.Set("ike_sa_lifetime_seconds", int(*connection.IKEOptionsSpecification.IKESaLifetimeSeconds)) + d.Set("ike_version", *connection.IKEOptionsSpecification.IKEVersion) + + //set up ipsec + d.Set("ipsec_encrypt_algorithm", *connection.IPSECOptionsSpecification.EncryptAlgorithm) + d.Set("ipsec_integrity_algorithm", *connection.IPSECOptionsSpecification.IntegrityAlgorith) + d.Set("ipsec_sa_lifetime_seconds", int(*connection.IPSECOptionsSpecification.IPSECSaLifetimeSeconds)) + d.Set("ipsec_pfs_dh_group", *connection.IPSECOptionsSpecification.PfsDhGroup) + d.Set("ipsec_sa_lifetime_traffic", int(*connection.IPSECOptionsSpecification.IPSECSaLifetimeTraffic)) + + //to be add + d.Set("state", *connection.State) + d.Set("net_status", *connection.NetStatus) + d.Set("vpn_proto", *connection.VpnProto) + d.Set("encrypt_proto", *connection.EncryptProto) + d.Set("route_type", *connection.RouteType) + + //tags + tagService := TagService{client: meta.(*TencentCloudClient).apiV3Conn} + region := meta.(*TencentCloudClient).apiV3Conn.Region + tags, err := tagService.DescribeResourceTags(ctx, "vpc", "vpnx", region, connectionId) + if err != nil { + return err + } + d.Set("tags", tags) + + return nil +} + +func resourceTencentCloudVpnConnectionUpdate(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_vpn_connection.update")() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), "logId", logId) + + d.Partial(true) + connectionId := d.Id() + request := vpc.NewModifyVpnConnectionAttributeRequest() + request.VpnConnectionId = &connectionId + changeFlag := false + if d.HasChange("name") { + request.VpnConnectionName = stringToPointer(d.Get("name").(string)) + changeFlag = true + } + if d.HasChange("pre_share_key") { + request.PreShareKey = stringToPointer(d.Get("pre_share_key").(string)) + changeFlag = true + } + + //set up SecurityPolicyDatabases + if d.HasChange("security_group_policy") { + sgps := d.Get("security_group_policy").([]interface{}) + if len(sgps) < 1 { + return fmt.Errorf("Para `security_group_policy` should be set at least one.") + } + for _, v := range sgps { + m := v.(map[string]interface{}) + request.SecurityPolicyDatabases = make([]*vpc.SecurityPolicyDatabase, 0, len(sgps)) + var sgp vpc.SecurityPolicyDatabase + local := m["local_cidr_block"].(string) + sgp.LocalCidrBlock = &local + // list + remoteCidrBlocks := m["remote_cidr_block"].(*schema.Set).List() + for _, vv := range remoteCidrBlocks { + remoteCidrBlock := vv.(string) + sgp.RemoteCidrBlock = append(sgp.RemoteCidrBlock, &remoteCidrBlock) + } + request.SecurityPolicyDatabases = append(request.SecurityPolicyDatabases, &sgp) + } + changeFlag = true + } + ikeChangeKeySet := map[string]bool{ + "ike_proto_encry_algorithm": false, + "ike_proto_authen_algorithm": false, + "ike_exchange_mode": false, + "ike_local_identity": false, + "ike_remote_identity": false, + "ike_local_address": false, + "ike_local_fqdn_name": false, + "ike_remote_address": false, + "ike_remote_fqdn_name": false, + "ike_sa_lifetime_seconds": false, + "ike_dh_group_name": false, + } + ikeChangeFlag := false + for key := range ikeChangeKeySet { + if d.HasChange(key) { + ikeChangeFlag = true + ikeChangeKeySet[key] = true + } + } + if ikeChangeFlag { + //set up IKEOptionsSpecification + var ikeOptionsSpecification vpc.IKEOptionsSpecification + ikeOptionsSpecification.PropoEncryAlgorithm = stringToPointer(d.Get("ike_proto_encry_algorithm").(string)) + ikeOptionsSpecification.PropoAuthenAlgorithm = stringToPointer(d.Get("ike_proto_authen_algorithm").(string)) + ikeOptionsSpecification.ExchangeMode = stringToPointer(d.Get("ike_exchange_mode").(string)) + ikeOptionsSpecification.LocalIdentity = stringToPointer(d.Get("ike_local_identity").(string)) + ikeOptionsSpecification.RemoteIdentity = stringToPointer(d.Get("ike_remote_identity").(string)) + if *ikeOptionsSpecification.LocalIdentity == VPN_IKE_IDENTITY_ADDRESS { + if v, ok := d.GetOk("ike_local_address"); ok { + ikeOptionsSpecification.LocalAddress = stringToPointer(v.(string)) + } else { + return fmt.Errorf("ike_local_address need to be set when ike_local_identity is `ADDRESS`.") + } + } else { + if v, ok := d.GetOk("ike_local_fqdn_name"); ok { + ikeOptionsSpecification.LocalFqdnName = stringToPointer(v.(string)) + } else { + return fmt.Errorf("ike_local_fqdn_name need to be set when ike_local_identity is `FQDN`") + } + } + if *ikeOptionsSpecification.LocalIdentity == VPN_IKE_IDENTITY_ADDRESS { + if v, ok := d.GetOk("ike_remote_address"); ok { + ikeOptionsSpecification.RemoteAddress = stringToPointer(v.(string)) + } else { + return fmt.Errorf("ike_remote_address need to be set when ike_remote_identity is `ADDRESS`.") + } + } else { + if v, ok := d.GetOk("ike_remote_fqdn_name"); ok { + ikeOptionsSpecification.RemoteFqdnName = stringToPointer(v.(string)) + } else { + return fmt.Errorf("ike_remote_fqdn_name need to be set when ike_remote_identity is `FQDN`") + } + } + + ikeOptionsSpecification.DhGroupName = stringToPointer(d.Get("ike_dh_group_name").(string)) + saLifetime := d.Get("ike_sa_lifetime_seconds").(int) + saLifetime64 := uint64(saLifetime) + ikeOptionsSpecification.IKESaLifetimeSeconds = &saLifetime64 + ikeOptionsSpecification.IKEVersion = stringToPointer(d.Get("ike_version").(string)) + request.IKEOptionsSpecification = &ikeOptionsSpecification + changeFlag = true + } + //set up IPSECOptionsSpecification + ipsecChangeKeySet := map[string]bool{ + "ipsec_encrypt_algorithm": false, + "ipsec_integrity_algorithm": false, + "ipsec_sa_lifetime_seconds": false, + "ipsec_pfs_dh_group": false, + "ipsec_sa_lifetime_traffic": false} + ipsecChangeFlag := false + for key := range ipsecChangeKeySet { + if d.HasChange(key) { + ipsecChangeFlag = true + ipsecChangeKeySet[key] = true + } + } + if ipsecChangeFlag { + var ipsecOptionsSpecification vpc.IPSECOptionsSpecification + ipsecOptionsSpecification.EncryptAlgorithm = stringToPointer(d.Get("ipsec_encrypt_algorithm").(string)) + ipsecOptionsSpecification.IntegrityAlgorith = stringToPointer(d.Get("ipsec_integrity_algorithm").(string)) + ipsecSaLifetimeSeconds := d.Get("ipsec_sa_lifetime_seconds").(int) + ipsecSaLifetimeSeconds64 := uint64(ipsecSaLifetimeSeconds) + ipsecOptionsSpecification.IPSECSaLifetimeSeconds = &ipsecSaLifetimeSeconds64 + ipsecOptionsSpecification.PfsDhGroup = stringToPointer(d.Get("ipsec_pfs_dh_group").(string)) + ipsecSaLifetimeTraffic := d.Get("ipsec_sa_lifetime_traffic").(int) + ipsecSaLifetimeTraffic64 := uint64(ipsecSaLifetimeTraffic) + ipsecOptionsSpecification.IPSECSaLifetimeTraffic = &ipsecSaLifetimeTraffic64 + request.IPSECOptionsSpecification = &ipsecOptionsSpecification + changeFlag = true + } + if changeFlag { + err := resource.Retry(writeRetryTimeout, func() *resource.RetryError { + _, e := meta.(*TencentCloudClient).apiV3Conn.UseVpcClient().ModifyVpnConnectionAttribute(request) + if e != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return retryError(e) + } + return nil + }) + if err != nil { + log.Printf("[CRITAL]%s modify VPN connection failed, reason:%s\n", logId, err.Error()) + return err + } + } + time.Sleep(3 * time.Minute) + if d.HasChange("name") { + d.SetPartial("name") + } + if d.HasChange("pre_share_key") { + d.SetPartial("pre_share_key") + } + if d.HasChange("security_group_policy") { + d.SetPartial("security_group_policy") + } + + for key := range ikeChangeKeySet { + if ikeChangeKeySet[key] { + d.SetPartial(key) + } + } + + for key := range ipsecChangeKeySet { + if ipsecChangeKeySet[key] { + d.SetPartial(key) + } + } + //tag + if d.HasChange("tags") { + old, new := d.GetChange("tags") + replaceTags, deleteTags := diffTags(old.(map[string]interface{}), new.(map[string]interface{})) + tagService := TagService{ + client: meta.(*TencentCloudClient).apiV3Conn, + } + region := meta.(*TencentCloudClient).apiV3Conn.Region + resourceName := BuildTagResourceName("vpc", "vpnx", region, connectionId) + err := tagService.ModifyTags(ctx, resourceName, replaceTags, deleteTags) + if err != nil { + return err + } + d.SetPartial("tags") + } + d.Partial(false) + + return resourceTencentCloudVpnConnectionRead(d, meta) +} + +func resourceTencentCloudVpnConnectionDelete(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_vpn_connection.delete")() + + logId := getLogId(contextNil) + + connectionId := d.Id() + vpnGatewayId := d.Get("vpn_gateway_id").(string) + //test when tunneling exists, delete may cause a fault, see if sdk returns that error or not + request := vpc.NewDeleteVpnConnectionRequest() + request.VpnConnectionId = &connectionId + request.VpnGatewayId = &vpnGatewayId + err := resource.Retry(writeRetryTimeout, func() *resource.RetryError { + _, e := meta.(*TencentCloudClient).apiV3Conn.UseVpcClient().DeleteVpnConnection(request) + if e != nil { + if ee, ok := e.(*errors.TencentCloudSDKError); ok { + if ee.GetCode() == "UnsupportedOperation.InvalidState" { + return resource.RetryableError(fmt.Errorf("state is not ready, wait to be `AVAILABLE`.")) + } + } + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return retryError(e) + } + return nil + }) + if err != nil { + log.Printf("[CRITAL]%s delete VPN connection failed, reason:%s\n", logId, err.Error()) + return err + } + //to get the status of vpn connection + statRequest := vpc.NewDescribeVpnConnectionsRequest() + statRequest.VpnConnectionIds = []*string{&connectionId} + err = resource.Retry(3*time.Minute, func() *resource.RetryError { + result, e := meta.(*TencentCloudClient).apiV3Conn.UseVpcClient().DescribeVpnConnections(statRequest) + if e != nil { + ee, ok := e.(*errors.TencentCloudSDKError) + if !ok { + return retryError(e) + } + if ee.Code == VPCNotFound { + log.Printf("[CRITAL]%s api[%s] success, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return nil + } else { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return retryError(e) + } + } else { + //if not, quit + if len(result.Response.VpnConnectionSet) == 0 { + return nil + } + //else consider delete fail + return resource.RetryableError(fmt.Errorf("describe retry")) + } + }) + if err != nil { + log.Printf("[CRITAL]%s delete VPN connection failed, reason:%s\n", logId, err.Error()) + return err + } + return nil +} diff --git a/tencentcloud/resource_tc_vpn_connection_test.go b/tencentcloud/resource_tc_vpn_connection_test.go new file mode 100644 index 0000000000..4df69379f7 --- /dev/null +++ b/tencentcloud/resource_tc_vpn_connection_test.go @@ -0,0 +1,269 @@ +package tencentcloud + +import ( + "fmt" + "log" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + errors "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors" + vpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312" +) + +func TestAccTencentCloudVpnConnection_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckVpnConnectionDestroy, + Steps: []resource.TestStep{ + { + Config: testAccVpnConnectionConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckVpnConnectionExists("tencentcloud_vpn_connection.connection"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "name", "vpn_connection_test"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "pre_share_key", "test"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "tags.test", "test"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "ike_proto_encry_algorithm", "3DES-CBC"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "ike_proto_authen_algorithm", "MD5"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "ike_local_identity", "ADDRESS"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "ike_remote_identity", "ADDRESS"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "ike_dh_group_name", "GROUP1"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "ike_exchange_mode", "MAIN"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "ike_sa_lifetime_seconds", "86400"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "ipsec_encrypt_algorithm", "3DES-CBC"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "ipsec_integrity_algorithm", "MD5"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "ipsec_sa_lifetime_seconds", "3600"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "ipsec_pfs_dh_group", "DH-GROUP1"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "ipsec_sa_lifetime_traffic", "2560"), + //resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "security_group_policy.0.remote_cidr_block.0", "3.3.3.0/32"), + resource.TestCheckResourceAttrSet("tencentcloud_vpn_connection.connection", "net_status"), + resource.TestCheckResourceAttrSet("tencentcloud_vpn_connection.connection", "state"), + resource.TestCheckResourceAttrSet("tencentcloud_vpn_connection.connection", "encrypt_proto"), + resource.TestCheckResourceAttrSet("tencentcloud_vpn_connection.connection", "route_type"), + resource.TestCheckResourceAttrSet("tencentcloud_vpn_connection.connection", "vpn_proto"), + ), + }, + { + Config: testAccVpnConnectionConfigUpdate, + Check: resource.ComposeTestCheckFunc( + testAccCheckVpnConnectionExists("tencentcloud_vpn_connection.connection"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "name", "vpn_connection_test2"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "pre_share_key", "testt"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "tags.test", "testt"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "ike_proto_encry_algorithm", "3DES-CBC"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "ike_proto_authen_algorithm", "SHA"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "ike_local_identity", "ADDRESS"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "ike_remote_identity", "ADDRESS"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "ike_dh_group_name", "GROUP2"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "ike_exchange_mode", "AGGRESSIVE"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "ike_sa_lifetime_seconds", "86401"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "ipsec_encrypt_algorithm", "3DES-CBC"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "ipsec_integrity_algorithm", "SHA1"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "ipsec_pfs_dh_group", "NULL"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "ipsec_sa_lifetime_seconds", "7200"), + resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "ipsec_sa_lifetime_traffic", "2570"), + //resource.TestCheckResourceAttr("tencentcloud_vpn_connection.connection", "security_group_policy.0.remote_cidr_block.0", "3.3.3.0/26"), + resource.TestCheckResourceAttrSet("tencentcloud_vpn_connection.connection", "net_status"), + resource.TestCheckResourceAttrSet("tencentcloud_vpn_connection.connection", "state"), + resource.TestCheckResourceAttrSet("tencentcloud_vpn_connection.connection", "encrypt_proto"), + resource.TestCheckResourceAttrSet("tencentcloud_vpn_connection.connection", "route_type"), + resource.TestCheckResourceAttrSet("tencentcloud_vpn_connection.connection", "vpn_proto"), + ), + }, + }, + }) +} + +func testAccCheckVpnConnectionDestroy(s *terraform.State) error { + logId := getLogId(contextNil) + + conn := testAccProvider.Meta().(*TencentCloudClient).apiV3Conn + for _, rs := range s.RootModule().Resources { + if rs.Type != "tencentcloud_vpn_connection" { + continue + } + request := vpc.NewDescribeVpnConnectionsRequest() + request.VpnConnectionIds = []*string{&rs.Primary.ID} + var response *vpc.DescribeVpnConnectionsResponse + err := resource.Retry(readRetryTimeout, func() *resource.RetryError { + result, e := conn.UseVpcClient().DescribeVpnConnections(request) + if e != nil { + ee, ok := e.(*errors.TencentCloudSDKError) + if !ok { + return retryError(e) + } + if ee.Code == VPCNotFound { + log.Printf("[CRITAL]%s api[%s] success, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return resource.NonRetryableError(e) + } else { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return retryError(e) + } + } + response = result + return nil + }) + if err != nil { + log.Printf("[CRITAL]%s read VPN connection failed, reason:%s\n", logId, err.Error()) + ee, ok := err.(*errors.TencentCloudSDKError) + if !ok { + return err + } + if ee.Code == "ResourceNotFound" { + return nil + } else { + return err + } + } else { + if len(response.Response.VpnConnectionSet) != 0 { + return fmt.Errorf("VPN connection id is still exists") + } + } + + } + return nil +} + +func testAccCheckVpnConnectionExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + logId := getLogId(contextNil) + + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("VPN connection instance %s is not found", n) + } + if rs.Primary.ID == "" { + return fmt.Errorf("VPN connection id is not set") + } + conn := testAccProvider.Meta().(*TencentCloudClient).apiV3Conn + request := vpc.NewDescribeVpnConnectionsRequest() + request.VpnConnectionIds = []*string{&rs.Primary.ID} + var response *vpc.DescribeVpnConnectionsResponse + err := resource.Retry(readRetryTimeout, func() *resource.RetryError { + result, e := conn.UseVpcClient().DescribeVpnConnections(request) + if e != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return retryError(e) + } + response = result + return nil + }) + if err != nil { + log.Printf("[CRITAL]%s read VPN connection failed, reason:%s\n", logId, err.Error()) + return err + } + if len(response.Response.VpnConnectionSet) != 1 { + return fmt.Errorf("VPN connection id is not found") + } + return nil + } +} + +const testAccVpnConnectionConfig = ` +resource "tencentcloud_vpn_customer_gateway" "cgw" { + name = "terraform_test" + public_ip_address = "3.3.3.3" + +} + +# Create VPC and Subnet +data "tencentcloud_vpc_instances" "foo" { + name = "Default-VPC" +} + +resource "tencentcloud_vpn_gateway" "vpn" { + name = "terraform_update" + vpc_id = "${data.tencentcloud_vpc_instances.foo.instance_list.0.vpc_id}" + bandwidth = 5 + zone = "ap-guangzhou-3" + + tags = { + test = "test" + } +} +resource "tencentcloud_vpn_connection" "connection" { + name = "vpn_connection_test" + vpc_id = "${data.tencentcloud_vpc_instances.foo.instance_list.0.vpc_id}" + vpn_gateway_id = "${tencentcloud_vpn_gateway.vpn.id}" + customer_gateway_id = "${tencentcloud_vpn_customer_gateway.cgw.id}" + pre_share_key = "test" + ike_proto_encry_algorithm = "3DES-CBC" + ike_proto_authen_algorithm = "MD5" + ike_local_identity = "ADDRESS" + ike_local_address = "${tencentcloud_vpn_gateway.vpn.public_ip_address}" + ike_remote_identity = "ADDRESS" + ike_remote_address = "${tencentcloud_vpn_customer_gateway.cgw.public_ip_address}" + ike_dh_group_name = "GROUP1" + ike_sa_lifetime_seconds = 86400 + ipsec_encrypt_algorithm = "3DES-CBC" + ipsec_integrity_algorithm = "MD5" + ipsec_sa_lifetime_seconds = 3600 + ipsec_pfs_dh_group = "DH-GROUP1" + ipsec_sa_lifetime_traffic = 2560 + + security_group_policy { + local_cidr_block = "172.16.0.0/16" + remote_cidr_block = ["3.3.3.0/32", ] + } + tags = { + test = "test" + } +} +` + +const testAccVpnConnectionConfigUpdate = ` +resource "tencentcloud_vpn_customer_gateway" "cgw" { + name = "terraform_test" + public_ip_address = "3.3.3.3" + +} + +# Create VPC and Subnet +data "tencentcloud_vpc_instances" "foo" { + name = "Default-VPC" +} + +resource "tencentcloud_vpn_gateway" "vpn" { + name = "terraform_update" + vpc_id = "${data.tencentcloud_vpc_instances.foo.instance_list.0.vpc_id}" + bandwidth = 5 + zone = "ap-guangzhou-3" + + tags = { + test = "test" + } +} +resource "tencentcloud_vpn_connection" "connection" { + name = "vpn_connection_test2" + vpc_id = "${data.tencentcloud_vpc_instances.foo.instance_list.0.vpc_id}" + vpn_gateway_id = "${tencentcloud_vpn_gateway.vpn.id}" + customer_gateway_id = "${tencentcloud_vpn_customer_gateway.cgw.id}" + pre_share_key = "testt" + ike_proto_encry_algorithm = "3DES-CBC" + ike_proto_authen_algorithm = "SHA" + ike_local_identity = "ADDRESS" + ike_exchange_mode = "AGGRESSIVE" + ike_local_address = "${tencentcloud_vpn_gateway.vpn.public_ip_address}" + ike_remote_identity = "ADDRESS" + ike_remote_address = "${tencentcloud_vpn_customer_gateway.cgw.public_ip_address}" + ike_dh_group_name = "GROUP2" + ike_sa_lifetime_seconds = 86401 + ipsec_encrypt_algorithm = "3DES-CBC" + ipsec_integrity_algorithm = "SHA1" + ipsec_sa_lifetime_seconds = 7200 + ipsec_pfs_dh_group = "NULL" + ipsec_sa_lifetime_traffic = 2570 + + security_group_policy { + local_cidr_block = "172.16.0.0/16" + remote_cidr_block = ["3.3.3.0/26", ] + } + tags = { + test = "testt" + } +} +` diff --git a/tencentcloud/resource_tc_vpn_customer_gateway.go b/tencentcloud/resource_tc_vpn_customer_gateway.go new file mode 100644 index 0000000000..5e02dd4e62 --- /dev/null +++ b/tencentcloud/resource_tc_vpn_customer_gateway.go @@ -0,0 +1,331 @@ +/* +Provides a resource to create a VPN customer gateway. + +Example Usage + +```hcl +resource "tencentcloud_vpn_customer_gateway" "foo" { + name = "test_vpn_customer_gateway" + public_ip_address = "1.1.1.1" + + tags = { + tag = "test" + } +} +``` + +Import + +VPN customer gateway can be imported using the id, e.g. + +``` +$ terraform import tencentcloud_vpn_customer_gateway.foo cgw-xfqag +``` +*/ +package tencentcloud + +import ( + "context" + "fmt" + "log" + "time" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + errors "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors" + vpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312" +) + +func resourceTencentCloudVpnCustomerGateway() *schema.Resource { + return &schema.Resource{ + Create: resourceTencentCloudVpnCustomerGatewayCreate, + Read: resourceTencentCloudVpnCustomerGatewayRead, + Update: resourceTencentCloudVpnCustomerGatewayUpdate, + Delete: resourceTencentCloudVpnCustomerGatewayDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateStringLengthInRange(1, 60), + Description: "Name of the customer gateway. The length of character is limited to 1-60.", + }, + "public_ip_address": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validateIp, + Description: "Public ip of the customer gateway.", + }, + "tags": { + Type: schema.TypeMap, + Optional: true, + Description: "A list of tags used to associate different resources.", + }, + "create_time": { + Type: schema.TypeString, + Computed: true, + Description: "Create time of the customer gateway.", + }, + }, + } +} + +func resourceTencentCloudVpnCustomerGatewayCreate(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_vpn_customer_gateway.create")() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), "logId", logId) + + request := vpc.NewCreateCustomerGatewayRequest() + request.CustomerGatewayName = stringToPointer(d.Get("name").(string)) + request.IpAddress = stringToPointer(d.Get("public_ip_address").(string)) + var response *vpc.CreateCustomerGatewayResponse + err := resource.Retry(readRetryTimeout, func() *resource.RetryError { + result, e := meta.(*TencentCloudClient).apiV3Conn.UseVpcClient().CreateCustomerGateway(request) + if e != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return retryError(e) + } + response = result + return nil + }) + if err != nil { + log.Printf("[CRITAL]%s create VPN customer gateway failed, reason:%s\n", logId, err.Error()) + return err + } + + if response.Response.CustomerGateway == nil { + return fmt.Errorf("VPN customer gateway id is nil") + } + customerGatewayId := *response.Response.CustomerGateway.CustomerGatewayId + d.SetId(customerGatewayId) + // must wait for finishing creating customer gateway + statRequest := vpc.NewDescribeCustomerGatewaysRequest() + statRequest.CustomerGatewayIds = []*string{response.Response.CustomerGateway.CustomerGatewayId} + err = resource.Retry(3*time.Minute, func() *resource.RetryError { + result, e := meta.(*TencentCloudClient).apiV3Conn.UseVpcClient().DescribeCustomerGateways(statRequest) + if e != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return retryError(e) + } else { + //if not, quit + if len(result.Response.CustomerGatewaySet) != 1 { + return resource.NonRetryableError(fmt.Errorf("creating error")) + } + //else consider created, cos there is no status of gateway + return nil + } + }) + if err != nil { + log.Printf("[CRITAL]%s create VPN customer gateway failed, reason:%s\n", logId, err.Error()) + return err + } + + //modify tags + if tags := getTags(d, "tags"); len(tags) > 0 { + tagService := TagService{client: meta.(*TencentCloudClient).apiV3Conn} + + region := meta.(*TencentCloudClient).apiV3Conn.Region + resourceName := BuildTagResourceName("vpc", "cgw", region, customerGatewayId) + + if err := tagService.ModifyTags(ctx, resourceName, tags, nil); err != nil { + return err + } + } + + return resourceTencentCloudVpnCustomerGatewayRead(d, meta) +} + +func resourceTencentCloudVpnCustomerGatewayRead(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_vpn_customer_gateway.read")() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), "logId", logId) + + customerGatewayId := d.Id() + request := vpc.NewDescribeCustomerGatewaysRequest() + request.CustomerGatewayIds = []*string{&customerGatewayId} + var response *vpc.DescribeCustomerGatewaysResponse + err := resource.Retry(readRetryTimeout, func() *resource.RetryError { + result, e := meta.(*TencentCloudClient).apiV3Conn.UseVpcClient().DescribeCustomerGateways(request) + if e != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return retryError(e) + } + response = result + return nil + }) + if err != nil { + log.Printf("[CRITAL]%s read VPN customer gateway failed, reason:%s\n", logId, err.Error()) + return err + } + if len(response.Response.CustomerGatewaySet) < 1 { + d.SetId("") + return nil + } + + gateway := response.Response.CustomerGatewaySet[0] + + d.Set("name", *gateway.CustomerGatewayName) + d.Set("public_ip_address", *gateway.IpAddress) + d.Set("create_time", *gateway.CreatedTime) + + //tags + tagService := TagService{client: meta.(*TencentCloudClient).apiV3Conn} + region := meta.(*TencentCloudClient).apiV3Conn.Region + tags, err := tagService.DescribeResourceTags(ctx, "vpc", "cgw", region, customerGatewayId) + if err != nil { + return err + } + d.Set("tags", tags) + + return nil +} + +func resourceTencentCloudVpnCustomerGatewayUpdate(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_vpn_customer_gateway.update")() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), "logId", logId) + + d.Partial(true) + customerGatewayId := d.Id() + request := vpc.NewModifyCustomerGatewayAttributeRequest() + request.CustomerGatewayId = &customerGatewayId + if d.HasChange("name") { + request.CustomerGatewayName = stringToPointer(d.Get("name").(string)) + err := resource.Retry(readRetryTimeout, func() *resource.RetryError { + _, e := meta.(*TencentCloudClient).apiV3Conn.UseVpcClient().ModifyCustomerGatewayAttribute(request) + if e != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return retryError(e) + } + return nil + }) + if err != nil { + log.Printf("[CRITAL]%s modify VPN customer gateway failed, reason:%s\n", logId, err.Error()) + return err + } + d.SetPartial("name") + } + + //tag + if d.HasChange("tags") { + old, new := d.GetChange("tags") + replaceTags, deleteTags := diffTags(old.(map[string]interface{}), new.(map[string]interface{})) + tagService := TagService{ + client: meta.(*TencentCloudClient).apiV3Conn, + } + region := meta.(*TencentCloudClient).apiV3Conn.Region + resourceName := BuildTagResourceName("vpc", "cgw", region, customerGatewayId) + err := tagService.ModifyTags(ctx, resourceName, replaceTags, deleteTags) + if err != nil { + return err + } + d.SetPartial("tags") + } + d.Partial(false) + + return resourceTencentCloudVpnCustomerGatewayRead(d, meta) +} + +func resourceTencentCloudVpnCustomerGatewayDelete(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_vpn_customer_gateway.delete")() + + logId := getLogId(contextNil) + + customerGatewayId := d.Id() + + //check the customer gateway is not related with any tunnel + tRequest := vpc.NewDescribeVpnConnectionsRequest() + tRequest.Filters = make([]*vpc.Filter, 0, 1) + params := make(map[string]string) + params["customer-gateway-id"] = customerGatewayId + + for k, v := range params { + filter := &vpc.Filter{ + Name: stringToPointer(k), + Values: []*string{stringToPointer(v)}, + } + tRequest.Filters = append(tRequest.Filters, filter) + } + offset := uint64(0) + tRequest.Offset = &offset + + tErr := resource.Retry(3*time.Minute, func() *resource.RetryError { + result, e := meta.(*TencentCloudClient).apiV3Conn.UseVpcClient().DescribeVpnConnections(tRequest) + + if e != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, tRequest.GetAction(), tRequest.ToJsonString(), e.Error()) + return retryError(e) + } else { + if len(result.Response.VpnConnectionSet) == 0 { + return nil + } else { + return resource.NonRetryableError(fmt.Errorf("There is associated tunnel exists, please delete associated tunnels first.")) + } + } + }) + if tErr != nil { + log.Printf("[CRITAL]%s describe VPN connection failed, reason:%s\n", logId, tErr.Error()) + return tErr + } + + request := vpc.NewDeleteCustomerGatewayRequest() + request.CustomerGatewayId = &customerGatewayId + err := resource.Retry(writeRetryTimeout, func() *resource.RetryError { + _, e := meta.(*TencentCloudClient).apiV3Conn.UseVpcClient().DeleteCustomerGateway(request) + if e != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return retryError(e) + } + return nil + }) + if err != nil { + log.Printf("[CRITAL]%s delete VPN customer gateway failed, reason:%s\n", logId, err.Error()) + return err + } + //to get the status of customer gateway + statRequest := vpc.NewDescribeCustomerGatewaysRequest() + statRequest.CustomerGatewayIds = []*string{&customerGatewayId} + err = resource.Retry(3*time.Minute, func() *resource.RetryError { + result, e := meta.(*TencentCloudClient).apiV3Conn.UseVpcClient().DescribeCustomerGateways(statRequest) + if e != nil { + ee, ok := e.(*errors.TencentCloudSDKError) + if !ok { + return retryError(e) + } + if ee.Code == VPCNotFound { + log.Printf("[CRITAL]%s api[%s] success, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return nil + } else { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return retryError(e) + } + } else { + //if not, quit + if len(result.Response.CustomerGatewaySet) == 0 { + return nil + } + //else consider delete fail + return resource.RetryableError(fmt.Errorf("deleting retry")) + } + }) + if err != nil { + log.Printf("[CRITAL]%s delete VPN customer gateway failed, reason:%s\n", logId, err.Error()) + return err + } + return nil +} diff --git a/tencentcloud/resource_tc_vpn_customer_gateway_test.go b/tencentcloud/resource_tc_vpn_customer_gateway_test.go new file mode 100644 index 0000000000..d964356578 --- /dev/null +++ b/tencentcloud/resource_tc_vpn_customer_gateway_test.go @@ -0,0 +1,148 @@ +package tencentcloud + +import ( + "fmt" + "log" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + errors "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors" + vpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312" +) + +func TestAccTencentCloudVpnCustomerGateway_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckVpnCustomerGatewayDestroy, + Steps: []resource.TestStep{ + { + Config: testAccVpnCustomerGatewayConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckVpnCustomerGatewayExists("tencentcloud_vpn_customer_gateway.my_cgw"), + resource.TestCheckResourceAttr("tencentcloud_vpn_customer_gateway.my_cgw", "name", "terraform_test"), + resource.TestCheckResourceAttr("tencentcloud_vpn_customer_gateway.my_cgw", "public_ip_address", "1.1.1.2"), + resource.TestCheckResourceAttr("tencentcloud_vpn_customer_gateway.my_cgw", "tags.test", "tf"), + ), + }, + { + Config: testAccVpnCustomerGatewayConfigUpdate, + Check: resource.ComposeTestCheckFunc( + testAccCheckVpnCustomerGatewayExists("tencentcloud_vpn_customer_gateway.my_cgw"), + resource.TestCheckResourceAttr("tencentcloud_vpn_customer_gateway.my_cgw", "name", "terraform_update"), + resource.TestCheckResourceAttr("tencentcloud_vpn_customer_gateway.my_cgw", "public_ip_address", "1.1.1.2"), + ), + }, + }, + }) +} + +func testAccCheckVpnCustomerGatewayDestroy(s *terraform.State) error { + logId := getLogId(contextNil) + + conn := testAccProvider.Meta().(*TencentCloudClient).apiV3Conn + for _, rs := range s.RootModule().Resources { + if rs.Type != "tencentcloud_vpn_customer_gateway" { + continue + } + request := vpc.NewDescribeCustomerGatewaysRequest() + request.CustomerGatewayIds = []*string{&rs.Primary.ID} + var response *vpc.DescribeCustomerGatewaysResponse + err := resource.Retry(readRetryTimeout, func() *resource.RetryError { + result, e := conn.UseVpcClient().DescribeCustomerGateways(request) + if e != nil { + ee, ok := e.(*errors.TencentCloudSDKError) + if !ok { + return retryError(e) + } + if ee.Code == VPCNotFound { + log.Printf("[CRITAL]%s api[%s] success, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return resource.NonRetryableError(e) + } else { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return retryError(e) + } + } + response = result + return nil + }) + if err != nil { + log.Printf("[CRITAL]%s read VPN customer gateway failed, reason:%s\n", logId, err.Error()) + ee, ok := err.(*errors.TencentCloudSDKError) + if !ok { + return err + } + if ee.Code == "ResourceNotFound" { + return nil + } else { + return err + } + } else { + if len(response.Response.CustomerGatewaySet) != 0 { + return fmt.Errorf("VPN customer gateway id is still exists") + } + } + + } + return nil +} + +func testAccCheckVpnCustomerGatewayExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + logId := getLogId(contextNil) + + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("VPN customer gateway instance %s is not found", n) + } + if rs.Primary.ID == "" { + return fmt.Errorf("VPN customer gateway id is not set") + } + conn := testAccProvider.Meta().(*TencentCloudClient).apiV3Conn + request := vpc.NewDescribeCustomerGatewaysRequest() + request.CustomerGatewayIds = []*string{&rs.Primary.ID} + var response *vpc.DescribeCustomerGatewaysResponse + err := resource.Retry(readRetryTimeout, func() *resource.RetryError { + result, e := conn.UseVpcClient().DescribeCustomerGateways(request) + if e != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return retryError(e) + } + response = result + return nil + }) + if err != nil { + log.Printf("[CRITAL]%s read VPN customer gateway failed, reason:%s\n", logId, err.Error()) + return err + } + if len(response.Response.CustomerGatewaySet) != 1 { + return fmt.Errorf("VPN customer gateway id is not found") + } + return nil + } +} + +const testAccVpnCustomerGatewayConfig = ` +resource "tencentcloud_vpn_customer_gateway" "my_cgw" { + name = "terraform_test" + public_ip_address = "1.1.1.2" + + tags = { + test = "tf" + } +} +` +const testAccVpnCustomerGatewayConfigUpdate = ` +resource "tencentcloud_vpn_customer_gateway" "my_cgw" { + name = "terraform_update" + public_ip_address = "1.1.1.2" + + tags = { + test = "test" + } +} +` diff --git a/tencentcloud/resource_tc_vpn_gateway.go b/tencentcloud/resource_tc_vpn_gateway.go new file mode 100644 index 0000000000..2f2082e0fb --- /dev/null +++ b/tencentcloud/resource_tc_vpn_gateway.go @@ -0,0 +1,443 @@ +/* +Provides a resource to create a VPN gateway. + +Example Usage + +```hcl +resource "tencentcloud_vpn_gateway" "my_cgw" { + name = "test" + vpc_id = "vpc-dk8zmwuf" + bandwidth = 5 + zone = "ap-guangzhou-3" + + tags = { + test = "test" + } +} +``` + +Import + +VPN gateway can be imported using the id, e.g. + +``` +$ terraform import tencentcloud_vpn_gateway.foo vpngw-8ccsnclt +``` +*/ +package tencentcloud + +import ( + "context" + "fmt" + "log" + "time" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/helper/schema" + errors "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors" + vpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312" +) + +func resourceTencentCloudVpnGateway() *schema.Resource { + return &schema.Resource{ + Create: resourceTencentCloudVpnGatewayCreate, + Read: resourceTencentCloudVpnGatewayRead, + Update: resourceTencentCloudVpnGatewayUpdate, + Delete: resourceTencentCloudVpnGatewayDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateStringLengthInRange(1, 60), + Description: "Name of the VPN gateway. The length of character is limited to 1-60.", + }, + "vpc_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "ID of the VPC.", + }, + "bandwidth": { + Type: schema.TypeInt, + Optional: true, + Default: 5, + ValidateFunc: validateAllowedIntValue([]int{5, 10, 20, 50, 100}), + Description: "The maximum public network output bandwidth of VPN gateway (unit: Mbps), the available values include: 5,10,20,50,100. Default is 5.", + }, + "public_ip_address": { + Type: schema.TypeString, + Computed: true, + Description: "Public ip of the VPN gateway.", + }, + "type": { + Type: schema.TypeString, + Computed: true, + Description: "Type of gateway instance, valid values are `IPSEC`, `SSL`.", + }, + "state": { + Type: schema.TypeString, + Computed: true, + Description: "State of the VPN gateway, valid values are `PENDING`, `DELETING`, `AVAILABLE`.", + }, + "prepaid_renew_flag": { + Type: schema.TypeString, + Computed: true, + Description: "Flag indicates whether to renew or not, valid values are `NOTIFY_AND_RENEW`, `NOTIFY_AND_AUTO_RENEW`, `NOT_NOTIFY_AND_NOT_RENEW`.", + }, + "prepaid_period": { + Type: schema.TypeInt, + Computed: true, + Description: "Period of instance to be prepaid. Valid values are 1, 2, 3, 4, 6, 7, 8, 9, 12, 24, 36 and unit is month.", + }, + "charge_type": { + Type: schema.TypeString, + Computed: true, + Description: "Charge Type of the VPN gateway, valid values are `PREPAID`, `POSTPAID_BY_HOUR` and default is `POSTPAID_BY_HOUR`.", + }, + "expired_time": { + Type: schema.TypeString, + Computed: true, + Description: "Expired time of the VPN gateway when charge type is `PREPAID`.", + }, + "is_address_blocked": { + Type: schema.TypeBool, + Computed: true, + Description: "Indicates whether ip address is blocked.", + }, + "new_purchase_plan": { + Type: schema.TypeString, + Computed: true, + Description: "The plan of new purchase, valid value is `PREPAID_TO_POSTPAID`.", + }, + "restrict_state": { + Type: schema.TypeString, + Computed: true, + Description: "Restrict state of gateway, valid values are `PRETECIVELY_ISOLATED`, `NORMAL`.", + }, + "zone": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Zone of the VPN gateway.", + }, + "tags": { + Type: schema.TypeMap, + Optional: true, + Description: "A list of tags used to associate different resources.", + }, + "create_time": { + Type: schema.TypeString, + Computed: true, + Description: "Create time of the VPN gateway.", + }, + }, + } +} + +func resourceTencentCloudVpnGatewayCreate(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_vpn_gateway.create")() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), "logId", logId) + + request := vpc.NewCreateVpnGatewayRequest() + request.VpnGatewayName = stringToPointer(d.Get("name").(string)) + bandwidth := d.Get("bandwidth").(int) + bandwidth64 := uint64(bandwidth) + request.InternetMaxBandwidthOut = &bandwidth64 + request.Zone = stringToPointer(d.Get("zone").(string)) + request.VpcId = stringToPointer(d.Get("vpc_id").(string)) + + var response *vpc.CreateVpnGatewayResponse + err := resource.Retry(readRetryTimeout, func() *resource.RetryError { + result, e := meta.(*TencentCloudClient).apiV3Conn.UseVpcClient().CreateVpnGateway(request) + if e != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return retryError(e) + } + response = result + return nil + }) + if err != nil { + log.Printf("[CRITAL]%s create VPN gateway failed, reason:%s\n ", logId, err.Error()) + return err + } + + if response.Response.VpnGateway == nil { + return fmt.Errorf("VPN gateway id is nil") + } + gatewayId := *response.Response.VpnGateway.VpnGatewayId + d.SetId(gatewayId) + + // must wait for creating gateway finished + statRequest := vpc.NewDescribeVpnGatewaysRequest() + statRequest.VpnGatewayIds = []*string{stringToPointer(gatewayId)} + err = resource.Retry(3*time.Minute, func() *resource.RetryError { + result, e := meta.(*TencentCloudClient).apiV3Conn.UseVpcClient().DescribeVpnGateways(statRequest) + if e != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return retryError(e) + } else { + //if not, quit + if len(result.Response.VpnGatewaySet) != 1 { + return resource.NonRetryableError(fmt.Errorf("creating error")) + } else { + if *result.Response.VpnGatewaySet[0].State == VPN_STATE_AVAILABLE { + return nil + } else { + return resource.RetryableError(fmt.Errorf("State is not available: %s, wait for state to be AVAILABLE.", *result.Response.VpnGatewaySet[0].State)) + } + } + } + }) + if err != nil { + log.Printf("[CRITAL]%s create VPN gateway failed, reason:%s\n ", logId, err.Error()) + return err + } + + //modify tags + if tags := getTags(d, "tags"); len(tags) > 0 { + tagService := TagService{client: meta.(*TencentCloudClient).apiV3Conn} + + region := meta.(*TencentCloudClient).apiV3Conn.Region + resourceName := BuildTagResourceName("vpc", "vpngw", region, gatewayId) + + if err := tagService.ModifyTags(ctx, resourceName, tags, nil); err != nil { + return err + } + } + + return resourceTencentCloudVpnGatewayRead(d, meta) +} + +func resourceTencentCloudVpnGatewayRead(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_vpn_gateway.read")() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), "logId", logId) + + gatewayId := d.Id() + request := vpc.NewDescribeVpnGatewaysRequest() + request.VpnGatewayIds = []*string{&gatewayId} + var response *vpc.DescribeVpnGatewaysResponse + err := resource.Retry(readRetryTimeout, func() *resource.RetryError { + result, e := meta.(*TencentCloudClient).apiV3Conn.UseVpcClient().DescribeVpnGateways(request) + if e != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return retryError(e) + } + response = result + return nil + }) + if err != nil { + log.Printf("[CRITAL]%s read VPN gateway failed, reason:%s\n ", logId, err.Error()) + return err + } + if len(response.Response.VpnGatewaySet) < 1 { + d.SetId("") + return nil + } + + gateway := response.Response.VpnGatewaySet[0] + + d.Set("name", *gateway.VpnGatewayName) + d.Set("public_ip_address", *gateway.PublicIpAddress) + d.Set("bandwidth", int(*gateway.InternetMaxBandwidthOut)) + d.Set("type", *gateway.Type) + d.Set("create_time", *gateway.CreatedTime) + d.Set("state", *gateway.Type) + d.Set("prepaid_renew_flag", *gateway.RenewFlag) + d.Set("charge_type", *gateway.InstanceChargeType) + d.Set("expired_time", *gateway.ExpiredTime) + d.Set("is_address_blocked", *gateway.IsAddressBlocked) + d.Set("new_purchase_plan", *gateway.NewPurchasePlan) + d.Set("restrict_state", *gateway.RestrictState) + d.Set("zone", *gateway.Zone) + //tags + tagService := TagService{client: meta.(*TencentCloudClient).apiV3Conn} + region := meta.(*TencentCloudClient).apiV3Conn.Region + tags, err := tagService.DescribeResourceTags(ctx, "vpc", "vpngw", region, gatewayId) + if err != nil { + return err + } + d.Set("tags", tags) + + return nil +} + +func resourceTencentCloudVpnGatewayUpdate(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_vpn_gateway.update")() + + logId := getLogId(contextNil) + ctx := context.WithValue(context.TODO(), "logId", logId) + + d.Partial(true) + gatewayId := d.Id() + + if d.HasChange("name") { + request := vpc.NewModifyVpnGatewayAttributeRequest() + request.VpnGatewayId = &gatewayId + request.VpnGatewayName = stringToPointer(d.Get("name").(string)) + err := resource.Retry(readRetryTimeout, func() *resource.RetryError { + _, e := meta.(*TencentCloudClient).apiV3Conn.UseVpcClient().ModifyVpnGatewayAttribute(request) + if e != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return retryError(e) + } + return nil + }) + if err != nil { + log.Printf("[CRITAL]%s modify VPN gateway name failed, reason:%s\n ", logId, err.Error()) + return err + } + d.SetPartial("name") + } + + //bandwidth + if d.HasChange("bandwidth") { + request := vpc.NewResetVpnGatewayInternetMaxBandwidthRequest() + request.VpnGatewayId = &gatewayId + bandwidth := d.Get("bandwidth").(int) + bandwidth64 := uint64(bandwidth) + request.InternetMaxBandwidthOut = &bandwidth64 + err := resource.Retry(readRetryTimeout, func() *resource.RetryError { + _, e := meta.(*TencentCloudClient).apiV3Conn.UseVpcClient().ResetVpnGatewayInternetMaxBandwidth(request) + if e != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return retryError(e) + } + return nil + }) + if err != nil { + log.Printf("[CRITAL]%s modify VPN gateway bandwidth failed, reason:%s\n ", logId, err.Error()) + return err + } + d.SetPartial("bandwidth") + } + + //tag + if d.HasChange("tags") { + old, new := d.GetChange("tags") + replaceTags, deleteTags := diffTags(old.(map[string]interface{}), new.(map[string]interface{})) + tagService := TagService{ + client: meta.(*TencentCloudClient).apiV3Conn, + } + region := meta.(*TencentCloudClient).apiV3Conn.Region + resourceName := BuildTagResourceName("vpc", "vpngw", region, gatewayId) + err := tagService.ModifyTags(ctx, resourceName, replaceTags, deleteTags) + if err != nil { + return err + } + d.SetPartial("tags") + } + d.Partial(false) + + return resourceTencentCloudVpnGatewayRead(d, meta) +} + +func resourceTencentCloudVpnGatewayDelete(d *schema.ResourceData, meta interface{}) error { + defer logElapsed("resource.tencentcloud_vpn_gateway.delete")() + + logId := getLogId(contextNil) + + gatewayId := d.Id() + + //check the vpn gateway is not related with any tunnel + tRequest := vpc.NewDescribeVpnConnectionsRequest() + tRequest.Filters = make([]*vpc.Filter, 0, 2) + params := make(map[string]string) + params["vpn-gateway-id"] = gatewayId + + if v, ok := d.GetOk("vpc_id"); ok { + params["vpc-id"] = v.(string) + } + + for k, v := range params { + filter := &vpc.Filter{ + Name: stringToPointer(k), + Values: []*string{stringToPointer(v)}, + } + tRequest.Filters = append(tRequest.Filters, filter) + } + offset := uint64(0) + tRequest.Offset = &offset + + tErr := resource.Retry(3*time.Minute, func() *resource.RetryError { + result, e := meta.(*TencentCloudClient).apiV3Conn.UseVpcClient().DescribeVpnConnections(tRequest) + + if e != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, tRequest.GetAction(), tRequest.ToJsonString(), e.Error()) + return retryError(e) + } else { + if len(result.Response.VpnConnectionSet) == 0 { + return nil + } else { + return resource.NonRetryableError(fmt.Errorf("There is associated tunnel exists, please delete associated tunnels first.")) + } + } + }) + if tErr != nil { + log.Printf("[CRITAL]%s describe VPN connection failed, reason:%s\n", logId, tErr.Error()) + return tErr + } + + request := vpc.NewDeleteVpnGatewayRequest() + request.VpnGatewayId = &gatewayId + + err := resource.Retry(writeRetryTimeout, func() *resource.RetryError { + _, e := meta.(*TencentCloudClient).apiV3Conn.UseVpcClient().DeleteVpnGateway(request) + if e != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return retryError(e) + } + return nil + }) + if err != nil { + log.Printf("[CRITAL]%s delete VPN gateway failed, reason:%s\n", logId, err.Error()) + return err + } + //to get the status of gateway + statRequest := vpc.NewDescribeVpnGatewaysRequest() + statRequest.VpnGatewayIds = []*string{&gatewayId} + err = resource.Retry(3*time.Minute, func() *resource.RetryError { + result, e := meta.(*TencentCloudClient).apiV3Conn.UseVpcClient().DescribeVpnGateways(statRequest) + if e != nil { + ee, ok := e.(*errors.TencentCloudSDKError) + if !ok { + return retryError(e) + } + if ee.Code == VPCNotFound { + log.Printf("[CRITAL]%s api[%s] success, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return nil + } else { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return retryError(e) + } + } else { + //if not, quit + if len(result.Response.VpnGatewaySet) == 0 { + return nil + } + //else consider delete fail + return resource.RetryableError(fmt.Errorf("deleting retry")) + } + }) + if err != nil { + log.Printf("[CRITAL]%s delete VPN gateway failed, reason:%s\n", logId, err.Error()) + return err + } + return nil +} diff --git a/tencentcloud/resource_tc_vpn_gateway_test.go b/tencentcloud/resource_tc_vpn_gateway_test.go new file mode 100644 index 0000000000..e006a085ff --- /dev/null +++ b/tencentcloud/resource_tc_vpn_gateway_test.go @@ -0,0 +1,167 @@ +package tencentcloud + +import ( + "fmt" + "log" + "testing" + + "github.com/hashicorp/terraform/helper/resource" + "github.com/hashicorp/terraform/terraform" + errors "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors" + vpc "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/vpc/v20170312" +) + +func TestAccTencentCloudVpnGateway_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckVpnGatewayDestroy, + Steps: []resource.TestStep{ + { + Config: testAccVpnGatewayConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckVpnGatewayExists("tencentcloud_vpn_gateway.my_cgw"), + resource.TestCheckResourceAttr("tencentcloud_vpn_gateway.my_cgw", "name", "terraform_test"), + resource.TestCheckResourceAttr("tencentcloud_vpn_gateway.my_cgw", "bandwidth", "10"), + resource.TestCheckResourceAttr("tencentcloud_vpn_gateway.my_cgw", "charge_type", "POSTPAID_BY_HOUR"), + resource.TestCheckResourceAttr("tencentcloud_vpn_gateway.my_cgw", "tags.test", "tf"), + resource.TestCheckResourceAttrSet("tencentcloud_vpn_gateway.my_cgw", "state"), + ), + }, + { + Config: testAccVpnGatewayConfigUpdate, + Check: resource.ComposeTestCheckFunc( + testAccCheckVpnGatewayExists("tencentcloud_vpn_gateway.my_cgw"), + resource.TestCheckResourceAttr("tencentcloud_vpn_gateway.my_cgw", "name", "terraform_update"), + resource.TestCheckResourceAttr("tencentcloud_vpn_gateway.my_cgw", "bandwidth", "5"), + resource.TestCheckResourceAttr("tencentcloud_vpn_gateway.my_cgw", "charge_type", "POSTPAID_BY_HOUR"), + resource.TestCheckResourceAttr("tencentcloud_vpn_gateway.my_cgw", "tags.test", "test"), + resource.TestCheckResourceAttrSet("tencentcloud_vpn_gateway.my_cgw", "state"), + ), + }, + }, + }) +} + +func testAccCheckVpnGatewayDestroy(s *terraform.State) error { + logId := getLogId(contextNil) + + conn := testAccProvider.Meta().(*TencentCloudClient).apiV3Conn + for _, rs := range s.RootModule().Resources { + if rs.Type != "tencentcloud_vpn_gateway" { + continue + } + request := vpc.NewDescribeVpnGatewaysRequest() + request.VpnGatewayIds = []*string{&rs.Primary.ID} + var response *vpc.DescribeVpnGatewaysResponse + err := resource.Retry(readRetryTimeout, func() *resource.RetryError { + result, e := conn.UseVpcClient().DescribeVpnGateways(request) + if e != nil { + ee, ok := e.(*errors.TencentCloudSDKError) + if !ok { + return retryError(e) + } + if ee.Code == "ResourceNotFound" { + log.Printf("[CRITAL]%s api[%s] success, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return resource.NonRetryableError(e) + } else { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return retryError(e) + } + } + response = result + return nil + }) + if err != nil { + log.Printf("[CRITAL]%s read VPN gateway failed, reason:%s\n", logId, err.Error()) + ee, ok := err.(*errors.TencentCloudSDKError) + if !ok { + return err + } + if ee.Code == VPCNotFound { + return nil + } else { + return err + } + } else { + if len(response.Response.VpnGatewaySet) != 0 { + return fmt.Errorf("VPN gateway id is still exists") + } + } + + } + return nil +} + +func testAccCheckVpnGatewayExists(n string) resource.TestCheckFunc { + return func(s *terraform.State) error { + logId := getLogId(contextNil) + + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("VPN gateway instance %s is not found", n) + } + if rs.Primary.ID == "" { + return fmt.Errorf("VPN gateway id is not set") + } + conn := testAccProvider.Meta().(*TencentCloudClient).apiV3Conn + request := vpc.NewDescribeVpnGatewaysRequest() + request.VpnGatewayIds = []*string{&rs.Primary.ID} + var response *vpc.DescribeVpnGatewaysResponse + err := resource.Retry(readRetryTimeout, func() *resource.RetryError { + result, e := conn.UseVpcClient().DescribeVpnGateways(request) + if e != nil { + log.Printf("[CRITAL]%s api[%s] fail, request body [%s], reason[%s]\n", + logId, request.GetAction(), request.ToJsonString(), e.Error()) + return retryError(e) + } + response = result + return nil + }) + if err != nil { + log.Printf("[CRITAL]%s read VPN gateway failed, reason:%s\n", logId, err.Error()) + return err + } + if len(response.Response.VpnGatewaySet) != 1 { + return fmt.Errorf("VPN gateway id is not found") + } + return nil + } +} + +const testAccVpnGatewayConfig = ` +# Create VPC +data "tencentcloud_vpc_instances" "foo" { + name = "Default-VPC" +} + +resource "tencentcloud_vpn_gateway" "my_cgw" { + name = "terraform_test" + vpc_id = "${data.tencentcloud_vpc_instances.foo.instance_list.0.vpc_id}" + bandwidth = 10 + zone = "ap-guangzhou-3" + + tags = { + test = "tf" + } +} +` +const testAccVpnGatewayConfigUpdate = ` +# Create VPC and Subnet +data "tencentcloud_vpc_instances" "foo" { + name = "Default-VPC" +} +resource "tencentcloud_vpn_gateway" "my_cgw" { + name = "terraform_update" + vpc_id = "${data.tencentcloud_vpc_instances.foo.instance_list.0.vpc_id}" + bandwidth = 5 + zone = "ap-guangzhou-3" + + tags = { + test = "test" + } +} + +` diff --git a/tencentcloud/service_tencentcloud_vpc.go b/tencentcloud/service_tencentcloud_vpc.go index 9443cbc594..831c3503c0 100644 --- a/tencentcloud/service_tencentcloud_vpc.go +++ b/tencentcloud/service_tencentcloud_vpc.go @@ -2470,3 +2470,14 @@ func waitEniReady(ctx context.Context, id string, client *vpc.Client, wantIpv4s return nil } + +func flattenVpnSPDList(spd []*vpc.SecurityPolicyDatabase) (mapping []*map[string]interface{}) { + mapping = make([]*map[string]interface{}, 0, len(spd)) + for _, spg := range spd { + item := make(map[string]interface{}) + item["local_cidr_block"] = spg.LocalCidrBlock + item["remote_cidr_block"] = spg.RemoteCidrBlock + mapping = append(mapping, &item) + } + return +} diff --git a/website/docs/d/vpn_connections.html.markdown b/website/docs/d/vpn_connections.html.markdown new file mode 100644 index 0000000000..27f26e10cc --- /dev/null +++ b/website/docs/d/vpn_connections.html.markdown @@ -0,0 +1,79 @@ +--- +layout: "tencentcloud" +page_title: "TencentCloud: tencentcloud_vpn_connections" +sidebar_current: "docs-tencentcloud-datasource-vpn_connections" +description: |- + Use this data source to query detailed information of VPN connections. +--- + +# tencentcloud_vpn_connections + +Use this data source to query detailed information of VPN connections. + +## Example Usage + +```hcl +data "tencentcloud_vpn_connections" "foo" { + name = "main" + id = "vpnx-xfqag" + vpn_gateway_id = "vpngw-8ccsnclt" + vpc_id = "cgw-xfqag" + customer_gateway_id = "" + tags = { + test = "tf" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `customer_gateway_id` - (Optional) Customer gateway ID of the VPN connection. +* `id` - (Optional) ID of the VPN connection. +* `name` - (Optional) Name of the VPN connection. The length of character is limited to 1-60. +* `result_output_file` - (Optional) Used to save results. +* `tags` - (Optional) Tags of the VPN connection to be queried. +* `vpc_id` - (Optional) ID of the VPC. +* `vpn_gateway_id` - (Optional) VPN gateway ID of the VPN connection. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `connection_list` - Information list of the dedicated connections. + * `create_time` - Create time of the VPN connection. + * `customer_gateway_id` - ID of the customer gateway. + * `encrypt_proto` - Encrypt proto of the VPN connection. + * `id` - ID of the VPN connection. + * `ike_dh_group_name` - DH group name of the IKE operation specification. + * `ike_exchange_mode` - Exchange mode of the IKE operation specification. + * `ike_local_address` - Local address of the IKE operation specification. + * `ike_local_fqdn_name` - Local FQDN name of the IKE operation specification. + * `ike_local_identity` - Local identity of the IKE operation specification. + * `ike_proto_authen_algorithm` - Proto authenticate algorithm of the IKE operation specification. + * `ike_proto_encry_algorithm` - Proto encrypt algorithm of the IKE operation specification. + * `ike_remote_address` - Remote address of the IKE operation specification. + * `ike_remote_fqdn_name` - Remote FQDN name of the IKE operation specification. + * `ike_remote_identity` - Remote identity of the IKE operation specification. + * `ike_sa_lifetime_seconds` - SA lifetime of the IKE operation specification, unit is `second`. + * `ike_version` - Version of the IKE operation specification. + * `ipsec_encrypt_algorithm` - Encrypt algorithm of the IPSEC operation specification. + * `ipsec_integrity_algorithm` - Integrity algorithm of the IPSEC operation specification. + * `ipsec_pfs_dh_group` - PFS DH group name of the IPSEC operation specification. + * `ipsec_sa_lifetime_seconds` - SA lifetime of the IPSEC operation specification, unit is `second`. + * `ipsec_sa_lifetime_traffic` - SA lifetime traffic of the IPSEC operation specification, unit is `KB`. + * `name` - Name of the VPN connection. + * `net_status` - Net status of the VPN connection. + * `pre_share_key` - Pre-shared key of the VPN connection. + * `route_type` - Route type of the VPN connection. + * `security_group_policy` - Security group policy of the VPN connection. + * `local_cidr_block` - Local cidr block. + * `remote_cidr_block` - Remote cidr block list. + * `state` - State of the VPN connection. + * `tags` - A list of tags used to associate different resources. + * `vpc_id` - ID of the VPC. + * `vpn_gateway_id` - ID of the VPN gateway. + * `vpn_proto` - Vpn proto of the VPN connection. + + diff --git a/website/docs/d/vpn_customer_gateways.html.markdown b/website/docs/d/vpn_customer_gateways.html.markdown new file mode 100644 index 0000000000..0528db1ee2 --- /dev/null +++ b/website/docs/d/vpn_customer_gateways.html.markdown @@ -0,0 +1,47 @@ +--- +layout: "tencentcloud" +page_title: "TencentCloud: tencentcloud_vpn_customer_gateways" +sidebar_current: "docs-tencentcloud-datasource-vpn_customer_gateways" +description: |- + Use this data source to query detailed information of VPN customer gateways. +--- + +# tencentcloud_vpn_customer_gateways + +Use this data source to query detailed information of VPN customer gateways. + +## Example Usage + +```hcl +data "tencentcloud_nat_gateways" "foo" { + name = "main" + id = "cgw-xfqag" + public_ip_address = "1.1.1.1" + tags = { + test = "tf" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `id` - (Optional) ID of the VPN customer gateway. +* `name` - (Optional) Name of the customer gateway. The length of character is limited to 1-60. +* `public_ip_address` - (Optional) Public ip address of the VPN customer gateway. +* `result_output_file` - (Optional) Used to save results. +* `tags` - (Optional) Tags of the VPN customer gateway to be queried. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `gateway_list` - Information list of the dedicated gateways. + * `create_time` - Create time of the VPN customer gateway. + * `id` - ID of the VPN customer gateway. + * `name` - Name of the VPN customer gateway. + * `public_ip_address` - Public ip address of the VPN customer gateway. + * `tags` - Tags of the VPN customer gateway. + + diff --git a/website/docs/d/vpn_gateways.html.markdown b/website/docs/d/vpn_gateways.html.markdown new file mode 100644 index 0000000000..24224c0350 --- /dev/null +++ b/website/docs/d/vpn_gateways.html.markdown @@ -0,0 +1,62 @@ +--- +layout: "tencentcloud" +page_title: "TencentCloud: tencentcloud_vpn_gateways" +sidebar_current: "docs-tencentcloud-datasource-vpn_gateways" +description: |- + Use this data source to query detailed information of VPN gateways. +--- + +# tencentcloud_vpn_gateways + +Use this data source to query detailed information of VPN gateways. + +## Example Usage + +```hcl +data "tencentcloud_vpn_gateways" "foo" { + name = "main" + id = "vpngw-8ccsnclt" + public_ip_address = "1.1.1.1" + zone = "ap-guangzhou-3" + vpc_id = "vpc-dk8zmwuf" + tags = { + test = "tf" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `id` - (Optional) ID of the VPN gateway. +* `name` - (Optional) Name of the VPN gateway. The length of character is limited to 1-60. +* `public_ip_address` - (Optional) Public ip address of the VPN gateway. +* `result_output_file` - (Optional) Used to save results. +* `tags` - (Optional) Tags of the VPN gateway to be queried. +* `vpc_id` - (Optional) ID of the VPC. +* `zone` - (Optional) Zone of the VPN gateway. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `gateway_list` - Information list of the dedicated gateways. + * `bandwidth` - The maximum public network output bandwidth of nat gateway (unit: Mbps), the available values include: 5,10,20,50,100. Default is 5. + * `charge_type` - Charge Type of the VPN gateway, valid values are `PREPAID`, `POSTPAID_BY_HOUR` and default is `POSTPAID_BY_HOUR`. + * `create_time` - Create time of the VPN gateway. + * `expired_time` - Expired time of the VPN gateway when charge type is `PREPAID`. + * `id` - ID of the VPN gateway. + * `is_address_blocked` - Indicates whether ip address is blocked. + * `name` - Name of the VPN gateway. + * `new_purchase_plan` - The plan of new purchase, valid value is `PREPAID_TO_POSTPAID`. + * `prepaid_renew_flag` - Flag indicates whether to renew or not, valid values are `NOTIFY_AND_RENEW`, `NOTIFY_AND_AUTO_RENEW`, `NOT_NOTIFY_AND_NOT_RENEW`. + * `public_ip_address` - Public ip of the VPN gateway. + * `restrict_state` - Restrict state of VPN gateway, valid values are `PRETECIVELY_ISOLATED`, `NORMAL`. + * `state` - State of the VPN gateway, valid values are `PENDING`, `DELETING`, `AVAILABLE`. + * `tags` - A list of tags used to associate different resources. + * `type` - Type of gateway instance, valid values are `IPSEC`, `SSL`. + * `vpc_id` - ID of the VPC. + * `zone` - Zone of the VPN gateway. + + diff --git a/website/docs/r/nat_gateway.html.markdown b/website/docs/r/nat_gateway.html.markdown index 23f3218bab..0dea3a64df 100644 --- a/website/docs/r/nat_gateway.html.markdown +++ b/website/docs/r/nat_gateway.html.markdown @@ -26,8 +26,8 @@ resource "tencentcloud_nat_gateway" "foo" { The following arguments are supported: -* `name` - (Required) Name of the nat gateway. -* `vpc_id` - (Required, ForceNew) ID of the vpc. +* `name` - (Required) Name of the NAT gateway. +* `vpc_id` - (Required, ForceNew) Id of the VPC. * `assigned_eip_set` - (Optional) EIP arrays bound to the gateway. The value of at least 1. * `bandwidth` - (Optional) The maximum public network output bandwidth of nat gateway (unit: Mbps), the available values include: 20,50,100,200,500,1000,2000,5000. Default is 100. * `max_concurrent` - (Optional) The upper limit of concurrent connection of nat gateway, the available values include: 1000000,3000000,10000000. Default is 1000000. diff --git a/website/docs/r/vpn_connection.html.markdown b/website/docs/r/vpn_connection.html.markdown new file mode 100644 index 0000000000..f2c172ef41 --- /dev/null +++ b/website/docs/r/vpn_connection.html.markdown @@ -0,0 +1,100 @@ +--- +layout: "tencentcloud" +page_title: "TencentCloud: tencentcloud_vpn_connection" +sidebar_current: "docs-tencentcloud-resource-vpn_connection" +description: |- + Provides a resource to create a VPN connection. +--- + +# tencentcloud_vpn_connection + +Provides a resource to create a VPN connection. + +## Example Usage + +```hcl +resource "tencentcloud_vpn_connection" "foo" { + name = "vpn_connection_test" + vpc_id = "vpc-dk8zmwuf" + vpn_gateway_id = "vpngw-8ccsnclt" + customer_gateway_id = "cgw-xfqag" + pre_share_key = "testt" + ike_proto_encry_algorithm = "3DES-CBC" + ike_proto_authen_algorithm = "SHA" + ike_local_identity = "ADDRESS" + ike_exchange_mode = "AGGRESSIVE" + ike_local_address = "1.1.1.1" + ike_remote_identity = "ADDRESS" + ike_remote_address = "2.2.2.2" + ike_dh_group_name = "GROUP2" + ike_sa_lifetime_seconds = 86401 + ipsec_encrypt_algorithm = "3DES-CBC" + ipsec_integrity_algorithm = "SHA1" + ipsec_sa_lifetime_seconds = 7200 + ipsec_pfs_dh_group = "NULL" + ipsec_sa_lifetime_traffic = 2570 + + security_group_policy { + local_cidr_block = "172.16.0.0/16" + remote_cidr_block = ["2.2.2.0/26", ] + } + tags = { + test = "testt" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `customer_gateway_id` - (Required, ForceNew) ID of the customer gateway. +* `name` - (Required) Name of the VPN connection. The length of character is limited to 1-60. +* `pre_share_key` - (Required) Pre-shared key of the VPN connection. +* `security_group_policy` - (Required) Security group policy of the VPN connection. +* `vpc_id` - (Required, ForceNew) ID of the VPC. +* `vpn_gateway_id` - (Required, ForceNew) ID of the VPN gateway. +* `ike_dh_group_name` - (Optional) DH group name of the IKE operation specification, valid values are `GROUP1`, `GROUP2`, `GROUP5`, `GROUP14`, `GROUP24`. Default value is `GROUP1`. +* `ike_exchange_mode` - (Optional) Exchange mode of the IKE operation specification, valid values are `AGGRESSIVE`, `MAIN`. Default value is `MAIN`. +* `ike_local_address` - (Optional) Local address of IKE operation specification, valid when ike_local_identity is `ADDRESS`, generally the value is public_ip_address of the related VPN gateway. +* `ike_local_fqdn_name` - (Optional) Local FQDN name of the IKE operation specification. +* `ike_local_identity` - (Optional) Local identity way of IKE operation specification, valid values are `ADDRESS`, `FQDN`. Default value is `ADDRESS`. +* `ike_proto_authen_algorithm` - (Optional) Proto authenticate algorithm of the IKE operation specification, valid values are `MD5`, `SHA`. Default Value is `MD5`. +* `ike_proto_encry_algorithm` - (Optional) Proto encrypt algorithm of the IKE operation specification, valid values are `3DES-CBC`, `AES-CBC-128`, `AES-CBC-128`, `AES-CBC-256`, `DES-CBC`. Default value is `3DES-CBC`. +* `ike_remote_address` - (Optional) Remote address of IKE operation specification, valid when ike_remote_identity is `ADDRESS`, generally the value is public_ip_address of the related customer gateway. +* `ike_remote_fqdn_name` - (Optional) Remote FQDN name of the IKE operation specification. +* `ike_remote_identity` - (Optional) Remote identity way of IKE operation specification, valid values are `ADDRESS`, `FQDN`. Default value is `ADDRESS`. +* `ike_sa_lifetime_seconds` - (Optional) SA lifetime of the IKE operation specification, unit is `second`. The value ranges from 60 to 604800. Default value is 86400 seconds. +* `ike_version` - (Optional) Version of the IKE operation specification. Default value is `IKEV1`. +* `ipsec_encrypt_algorithm` - (Optional) Encrypt algorithm of the IPSEC operation specification, valid values are `3DES-CBC`, `AES-CBC-128`, `AES-CBC-128`, `AES-CBC-256`, `DES-CBC`. Default value is `3DES-CBC`. +* `ipsec_integrity_algorithm` - (Optional) Integrity algorithm of the IPSEC operation specification, valid values are `SHA1`, `MD5`. Default value is `MD5`. +* `ipsec_pfs_dh_group` - (Optional) PFS DH group , valid values are `GROUP1`, `GROUP2`, `GROUP5`, `GROUP14`, `GROUP24`, `NULL`. Default value is `NULL`. +* `ipsec_sa_lifetime_seconds` - (Optional) SA lifetime of the IPSEC operation specification, unit is `second`. The value ranges from 180 to 604800. Default value is 3600 seconds. +* `ipsec_sa_lifetime_traffic` - (Optional) SA lifetime of the IPSEC operation specification, unit is `KB`. The value ranges from 2560 to 4294967295. Default value is 1843200. +* `tags` - (Optional) A list of tags used to associate different resources. + +The `security_group_policy` object supports the following: + +* `local_cidr_block` - (Required) Local cidr block. +* `remote_cidr_block` - (Required) Remote cidr block list. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `create_time` - Create time of the VPN connection. +* `encrypt_proto` - Encrypt proto of the VPN connection. +* `net_status` - Net status of the VPN connection, values are `AVAILABLE`. +* `route_type` - Route type of the VPN connection. +* `state` - State of the connection, values are `PENDING`, `AVAILABLE`, `DELETING`. +* `vpn_proto` - Vpn proto of the VPN connection. + + +## Import + +VPN connection can be imported using the id, e.g. + +``` +$ terraform import tencentcloud_vpn_connection.foo vpnx-nadifg3s +``` + diff --git a/website/docs/r/vpn_customer_gateway.html.markdown b/website/docs/r/vpn_customer_gateway.html.markdown new file mode 100644 index 0000000000..5e7ba1e461 --- /dev/null +++ b/website/docs/r/vpn_customer_gateway.html.markdown @@ -0,0 +1,48 @@ +--- +layout: "tencentcloud" +page_title: "TencentCloud: tencentcloud_vpn_customer_gateway" +sidebar_current: "docs-tencentcloud-resource-vpn_customer_gateway" +description: |- + Provides a resource to create a VPN customer gateway. +--- + +# tencentcloud_vpn_customer_gateway + +Provides a resource to create a VPN customer gateway. + +## Example Usage + +```hcl +resource "tencentcloud_vpn_customer_gateway" "foo" { + name = "test_vpn_customer_gateway" + public_ip_address = "1.1.1.1" + + tags = { + tag = "test" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) Name of the customer gateway. The length of character is limited to 1-60. +* `public_ip_address` - (Required, ForceNew) Public ip of the customer gateway. +* `tags` - (Optional) A list of tags used to associate different resources. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `create_time` - Create time of the customer gateway. + + +## Import + +VPN customer gateway can be imported using the id, e.g. + +``` +$ terraform import tencentcloud_vpn_customer_gateway.foo cgw-xfqag +``` + diff --git a/website/docs/r/vpn_gateway.html.markdown b/website/docs/r/vpn_gateway.html.markdown new file mode 100644 index 0000000000..9efb9f9033 --- /dev/null +++ b/website/docs/r/vpn_gateway.html.markdown @@ -0,0 +1,62 @@ +--- +layout: "tencentcloud" +page_title: "TencentCloud: tencentcloud_vpn_gateway" +sidebar_current: "docs-tencentcloud-resource-vpn_gateway" +description: |- + Provides a resource to create a VPN gateway. +--- + +# tencentcloud_vpn_gateway + +Provides a resource to create a VPN gateway. + +## Example Usage + +```hcl +resource "tencentcloud_vpn_gateway" "my_cgw" { + name = "test" + vpc_id = "vpc-dk8zmwuf" + bandwidth = 5 + zone = "ap-guangzhou-3" + + tags = { + test = "test" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) Name of the VPN gateway. The length of character is limited to 1-60. +* `vpc_id` - (Required, ForceNew) ID of the VPC. +* `zone` - (Required, ForceNew) Zone of the VPN gateway. +* `bandwidth` - (Optional) The maximum public network output bandwidth of VPN gateway (unit: Mbps), the available values include: 5,10,20,50,100. Default is 5. +* `tags` - (Optional) A list of tags used to associate different resources. + +## Attributes Reference + +In addition to all arguments above, the following attributes are exported: + +* `charge_type` - Charge Type of the VPN gateway, valid values are `PREPAID`, `POSTPAID_BY_HOUR` and default is `POSTPAID_BY_HOUR`. +* `create_time` - Create time of the VPN gateway. +* `expired_time` - Expired time of the VPN gateway when charge type is `PREPAID`. +* `is_address_blocked` - Indicates whether ip address is blocked. +* `new_purchase_plan` - The plan of new purchase, valid value is `PREPAID_TO_POSTPAID`. +* `prepaid_period` - Period of instance to be prepaid. Valid values are 1, 2, 3, 4, 6, 7, 8, 9, 12, 24, 36 and unit is month. +* `prepaid_renew_flag` - Flag indicates whether to renew or not, valid values are `NOTIFY_AND_RENEW`, `NOTIFY_AND_AUTO_RENEW`, `NOT_NOTIFY_AND_NOT_RENEW`. +* `public_ip_address` - Public ip of the VPN gateway. +* `restrict_state` - Restrict state of gateway, valid values are `PRETECIVELY_ISOLATED`, `NORMAL`. +* `state` - State of the VPN gateway, valid values are `PENDING`, `DELETING`, `AVAILABLE`. +* `type` - Type of gateway instance, valid values are `IPSEC`, `SSL`. + + +## Import + +VPN gateway can be imported using the id, e.g. + +``` +$ terraform import tencentcloud_vpn_gateway.foo vpngw-8ccsnclt +``` + diff --git a/website/tencentcloud.erb b/website/tencentcloud.erb index 285ec97122..b1c2f95119 100644 --- a/website/tencentcloud.erb +++ b/website/tencentcloud.erb @@ -245,6 +245,15 @@