From 020acefd125282cbf7e8d7b4ad9fade8daa3aeb3 Mon Sep 17 00:00:00 2001 From: gailwang Date: Mon, 11 Nov 2019 22:40:43 +0800 Subject: [PATCH 1/5] add resrouce `vpn gateway`, `customer gateway` and `vpn connection` --- examples/tencentcloud-vpn/main.tf | 63 ++ examples/tencentcloud-vpn/variables.tf | 3 + go.mod | 6 + go.sum | 22 + .../data_source_tc_vpn_connections.go | 387 +++++++++ .../data_source_tc_vpn_connections_test.go | 83 ++ .../data_source_tc_vpn_customer_gateways.go | 201 +++++ ...ta_source_tc_vpn_customer_gateways_test.go | 40 + tencentcloud/data_source_tc_vpn_gateways.go | 286 ++++++ .../data_source_tc_vpn_gateways_test.go | 48 ++ tencentcloud/extension_vpc.go | 165 ++++ tencentcloud/provider.go | 11 +- tencentcloud/resource_tc_vpn_connection.go | 811 ++++++++++++++++++ .../resource_tc_vpn_connection_test.go | 269 ++++++ .../resource_tc_vpn_customer_gateway.go | 334 ++++++++ .../resource_tc_vpn_customer_gateway_test.go | 148 ++++ tencentcloud/resource_tc_vpn_gateway.go | 443 ++++++++++ tencentcloud/resource_tc_vpn_gateway_test.go | 167 ++++ tencentcloud/service_tencentcloud_vpc.go | 11 + website/docs/d/vpn_connections.html.markdown | 79 ++ .../d/vpn_customer_gateways.html.markdown | 47 + website/docs/d/vpn_gateways.html.markdown | 60 ++ website/docs/r/vpn_connection.html.markdown | 100 +++ .../docs/r/vpn_customer_gateway.html.markdown | 48 ++ website/docs/r/vpn_gateway.html.markdown | 62 ++ 25 files changed, 3893 insertions(+), 1 deletion(-) create mode 100644 examples/tencentcloud-vpn/main.tf create mode 100644 examples/tencentcloud-vpn/variables.tf create mode 100644 tencentcloud/data_source_tc_vpn_connections.go create mode 100644 tencentcloud/data_source_tc_vpn_connections_test.go create mode 100644 tencentcloud/data_source_tc_vpn_customer_gateways.go create mode 100644 tencentcloud/data_source_tc_vpn_customer_gateways_test.go create mode 100644 tencentcloud/data_source_tc_vpn_gateways.go create mode 100644 tencentcloud/data_source_tc_vpn_gateways_test.go create mode 100644 tencentcloud/resource_tc_vpn_connection.go create mode 100644 tencentcloud/resource_tc_vpn_connection_test.go create mode 100644 tencentcloud/resource_tc_vpn_customer_gateway.go create mode 100644 tencentcloud/resource_tc_vpn_customer_gateway_test.go create mode 100644 tencentcloud/resource_tc_vpn_gateway.go create mode 100644 tencentcloud/resource_tc_vpn_gateway_test.go create mode 100644 website/docs/d/vpn_connections.html.markdown create mode 100644 website/docs/d/vpn_customer_gateways.html.markdown create mode 100644 website/docs/d/vpn_gateways.html.markdown create mode 100644 website/docs/r/vpn_connection.html.markdown create mode 100644 website/docs/r/vpn_customer_gateway.html.markdown create mode 100644 website/docs/r/vpn_gateway.html.markdown diff --git a/examples/tencentcloud-vpn/main.tf b/examples/tencentcloud-vpn/main.tf new file mode 100644 index 0000000000..3ff79028fd --- /dev/null +++ b/examples/tencentcloud-vpn/main.tf @@ -0,0 +1,63 @@ + +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.mod b/go.mod index 85ef17229e..b29912411a 100644 --- a/go.mod +++ b/go.mod @@ -12,10 +12,16 @@ require ( github.com/hashicorp/terraform v0.12.3 github.com/likexian/gokit v0.0.0-20190604165112-68b8a4ba758c github.com/mitchellh/go-homedir v1.1.0 + github.com/stamblerre/gocode v1.0.0 // indirect github.com/tencentcloud/tencentcloud-sdk-go v3.0.87-0.20190912121957-c0615982c83b+incompatible github.com/terraform-providers/terraform-provider-template v1.0.0 github.com/yangwenmai/ratelimit v0.0.0-20180104140304-44221c2292e1 github.com/zqfan/tencentcloud-sdk-go v0.0.0-0.20181105105106-4c76f78ff2e6 + golang.org/x/net v0.0.0-20191105084925-a882066a44e0 // indirect + golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect + golang.org/x/tools v0.0.0-20191107010934-f79515f33823 // indirect + golang.org/x/tools/gopls v0.1.7 // indirect + golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 // indirect labix.org/v2/mgo v0.0.0-20140701140051-000000000287 // indirect launchpad.net/gocheck v0.0.0-20140225173054-000000000087 // indirect ) diff --git a/go.sum b/go.sum index 65e793210f..8299403eb6 100644 --- a/go.sum +++ b/go.sum @@ -268,6 +268,8 @@ github.com/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpR github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/kardianos/osext v0.0.0-20170510131534-ae77be60afb1/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= 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/gotool v0.0.0-20161130080628-0de1eaf82fa3/go.mod h1:jxZFDH7ILpTPQTk+E2s+z4CUas9lVNjIuKR4c5/zKgM= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= @@ -438,6 +440,8 @@ github.com/spf13/pflag v1.0.2 h1:Fy0orTDgHdbnzHcsOgfCN4LtHf0ec3wwtiwJqwvf3Gc= github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/viper v1.0.2 h1:Ncr3ZIuJn322w2k1qmzXDnkLAdQMlJqBa9kfAH+irso= github.com/spf13/viper v1.0.2/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= +github.com/stamblerre/gocode v1.0.0 h1:5aTRgkRTOS8mELHoKatkwhfX44OdEV3iwu3FCXyvLzk= +github.com/stamblerre/gocode v1.0.0/go.mod h1:ONyGamdxpnxaG2+XLyGkNuuoYISmz0QFVHScxvsXsqM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= @@ -496,6 +500,7 @@ golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734 h1:p/H982KKEjUnLJkM3tt/Le golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/net v0.0.0-20170915142106-8351a756f30f/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -514,6 +519,10 @@ golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190502183928-7f726cade0ab h1:9RfW3ktsOZxgo9YNbBAjq1FWzc/igwEcUzZz8IXgSbk= golang.org/x/net v0.0.0-20190502183928-7f726cade0ab/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191105084925-a882066a44e0 h1:QPlSTtPE2k6PZPasQUbzuK3p9JbS+vMXYVto8g/yrsg= +golang.org/x/net v0.0.0-20191105084925-a882066a44e0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -525,6 +534,8 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20171026204733-164713f0dfce/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -564,6 +575,17 @@ golang.org/x/tools v0.0.0-20190510151030-63859f3815cb h1:fuQCchFfDC5laXoMg4zQC0+ golang.org/x/tools v0.0.0-20190510151030-63859f3815cb/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd h1:7E3PabyysDSEjnaANKBgums/hyvMI/HoHQ50qZEzTrg= golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190918214516-5a1a30219888/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191030062658-86caa796c7ab h1:tpc/nJ4vD66vAk/2KN0sw/DvQIz2sKmCpWvyKtPmfMQ= +golang.org/x/tools v0.0.0-20191030062658-86caa796c7ab/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191107010934-f79515f33823 h1:akkRBeitX2EZP59KdtKw310CI4WGPCNPyrLbE7WZA8Y= +golang.org/x/tools v0.0.0-20191107010934-f79515f33823/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools/gopls v0.1.7 h1:YwKf8t9h69++qCtVmc2q6fVuetFXmmu9LKoPMYLZid4= +golang.org/x/tools/gopls v0.1.7/go.mod h1:PE3vTwT0ejw3a2L2fFgSJkxlEbA8Slbk+Lsy9hTmbG8= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.1.0 h1:K6z2u68e86TPdSdefXdzvXgR1zEMa+459vBSfWYAZkI= diff --git a/tencentcloud/data_source_tc_vpn_connections.go b/tencentcloud/data_source_tc_vpn_connections.go new file mode 100644 index 0000000000..0614421413 --- /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(DEFAULT_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) < 100 { + 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 tags != nil { + 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..e0c71c4dbc --- /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(DEFAULT_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) < 100 { + 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 tags != nil { + 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..f7782fd632 --- /dev/null +++ b/tencentcloud/data_source_tc_vpn_gateways.go @@ -0,0 +1,286 @@ +/* +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, + Required: true, + ForceNew: true, + Description: "Zone of the VPN gateway.", + }, + "tags": { + Type: schema.TypeMap, + Optional: true, + Description: "A list of tags used to associate diffrent 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(DEFAULT_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) < 100 { + 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 tags != nil { + 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 520335e7b2..8362f09b81 100644 --- a/tencentcloud/extension_vpc.go +++ b/tencentcloud/extension_vpc.go @@ -12,6 +12,7 @@ const GATE_WAY_TYPE_NAT = "NAT" const GATE_WAY_TYPE_NORMAL_CVM = "NORMAL_CVM" const GATE_WAY_TYPE_EIP = "EIP" const GATE_WAY_TYPE_CCN = "CCN" +const DEFAULT_LIMIT = 100 var ALL_GATE_WAY_TYPES = []string{GATE_WAY_TYPE_CVM, GATE_WAY_TYPE_VPN, @@ -35,3 +36,167 @@ const ( EIP_STATUS_OFFLINING = "OFFLINING" EIP_STATUS_BIND_ENI = "BIND_ENI" ) + +/* +VPN +*/ + +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, +} diff --git a/tencentcloud/provider.go b/tencentcloud/provider.go index 57c165cc35..75e015e745 100644 --- a/tencentcloud/provider.go +++ b/tencentcloud/provider.go @@ -78,6 +78,8 @@ Data Sources tencentcloud_vpc_instances tencentcloud_vpc_route_tables tencentcloud_vpc_subnets + tencentcloud_vpn_customer_gateways + tencentcloud_vpn_gateways AS Resources tencentcloud_as_scaling_config @@ -174,7 +176,8 @@ VPC Resources tencentcloud_route_table_entry tencentcloud_dnat tencentcloud_nat_gateway - + tencentcloud_vpn_customer_gateway + tencentcloud_vpn_gateway */ package tencentcloud @@ -274,6 +277,9 @@ func Provider() *schema.Provider { "tencentcloud_placement_groups": dataSourceTencentCloudPlacementGroups(), "tencentcloud_eips": dataSourceTencentCloudEips(), "tencentcloud_key_pairs": dataSourceTencentCloudKeyPairs(), + "tencentcloud_vpn_customer_gateways": dataSourceTencentCloudVpnCustomerGateways(), + "tencentcloud_vpn_gateways": dataSourceTencentCloudVpnGateways(), + "tencentcloud_vpn_connections": dataSourceTencentCloudVpnConnections(), }, ResourcesMap: map[string]*schema.Resource{ @@ -341,6 +347,9 @@ func Provider() *schema.Provider { "tencentcloud_ssl_certificate": resourceTencentCloudSslCertificate(), "tencentcloud_security_group_lite_rule": resourceTencentCloudSecurityGroupLiteRule(), "tencentcloud_placement_group": resourceTencentCloudPlacementGroup(), + "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..7a62fc50ed --- /dev/null +++ b/tencentcloud/resource_tc_vpn_connection.go @@ -0,0 +1,811 @@ +/* +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 { + d.SetId("") + 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 == "" { + d.SetId("") + 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 availabe: %s, wait for state to be AVAILABLE.", *result.Response.VpnConnectionSet[0].State)) + } + } + return nil + } + }) + 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 := fmt.Sprintf("qcs::vpc:%s:uin/:vpnx/%s", 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) + } + if len(result.Response.VpnConnectionSet) > 0 && *result.Response.VpnConnectionSet[0].State != VPN_STATE_AVAILABLE { + return resource.RetryableError(fmt.Errorf("state is not AVAILABLE, wait ...")) + } else { + 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 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_algorithem": 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 := fmt.Sprintf("qcs::vpc:%s:uin/:vpnx/%s", 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 { + return nil + } else { + //if not, quit + if len(result.Response.VpnConnectionSet) == 0 { + log.Printf("deleting done") + return nil + } + //else consider delete fail + return resource.RetryableError(fmt.Errorf("deleting 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..4093013f81 --- /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..70d9948b89 --- /dev/null +++ b/tencentcloud/resource_tc_vpn_customer_gateway.go @@ -0,0 +1,334 @@ +/* +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 nat-1asg3t63 +``` +*/ +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 { + d.SetId("") + 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 := fmt.Sprintf("qcs::vpc:%s:uin/:cgw/%s", 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 { + return fmt.Errorf("VPN customer gateway id is 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 + } + } + if d.HasChange("name") { + 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 := fmt.Sprintf("qcs::vpc:%s:uin/:cgw/%s", 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 tunnels + 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 create 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 { + log.Printf("deleting done") + 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..1a0d071c9b --- /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..3abfa14e07 --- /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, + 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 { + d.SetId("") + 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 availabe: %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 := fmt.Sprintf("qcs::vpc:%s:uin/:vpngw/%s", 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 { + return fmt.Errorf("VPN gateway id is 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 := fmt.Sprintf("qcs::vpc:%s:uin/:vpngw/%s", 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 tunnels + 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 create 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 { + log.Printf("deleting done") + 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..ee63e692aa --- /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 539e7f83ac..b61437e723 100644 --- a/tencentcloud/service_tencentcloud_vpc.go +++ b/tencentcloud/service_tencentcloud_vpc.go @@ -1739,3 +1739,14 @@ func (me *VpcService) UnattachEip(ctx context.Context, eipId string) error { 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..eea8350d3f --- /dev/null +++ b/website/docs/d/vpn_gateways.html.markdown @@ -0,0 +1,60 @@ +--- +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`. + * `type` - Type of gateway instance, valid values are `IPSEC`, `SSL`. + * `vpc_id` - ID of the VPC. + + 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..7a3c826a94 --- /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 nat-1asg3t63 +``` + 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 +``` + From 8be719735cce0d602e660e54ee0d545a55c86b99 Mon Sep 17 00:00:00 2001 From: gailwang Date: Mon, 11 Nov 2019 23:01:08 +0800 Subject: [PATCH 2/5] add erb file and changelog, fix spelling bugs --- CHANGELOG.md | 6 +++++ tencentcloud/data_source_tc_vpn_gateways.go | 2 +- tencentcloud/provider.go | 4 ++-- tencentcloud/resource_tc_vpn_connection.go | 25 ++++++++++----------- tencentcloud/resource_tc_vpn_gateway.go | 2 +- website/docs/r/nat_gateway.html.markdown | 4 ++-- website/tencentcloud.erb | 18 +++++++++++++++ 7 files changed, 42 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 17db5b0f2c..552994e866 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,12 +11,18 @@ FEATURES: * **New Data Source**: `tencentcloud_cfs_access_rules` * **New Data Source**: `tencentcloud_scf_functions` * **New Data Source**: `tencentcloud_scf_namespaces` +* **New Data Source**: `tencentcloud_vpn_gateways` +* **New Data Source**: `tencentcloud_customer_gateways` +* **New Data Source**: `tencentcloud_vpn_connections` * **New Data Source**: `tencentcloud_scf_logs` * **New Resource**: `tencentcloud_cfs_file_system` * **New Resource**: `tencentcloud_cfs_access_group` * **New Resource**: `tencentcloud_cfs_access_rule` * **New Resource**: `tencentcloud_scf_function` * **New Resource**: `tencentcloud_scf_namespace` +* **New Resource**: `tencentcloud_vpn_gateway` +* **New Resource**: `tencentcloud_customer_gateway` +* **New Resource**: `tencentcloud_vpn_connection` ## 1.21.2 (October 29, 2019) diff --git a/tencentcloud/data_source_tc_vpn_gateways.go b/tencentcloud/data_source_tc_vpn_gateways.go index f7782fd632..bf06ef4a6b 100644 --- a/tencentcloud/data_source_tc_vpn_gateways.go +++ b/tencentcloud/data_source_tc_vpn_gateways.go @@ -152,7 +152,7 @@ func dataSourceTencentCloudVpnGateways() *schema.Resource { "tags": { Type: schema.TypeMap, Optional: true, - Description: "A list of tags used to associate diffrent resources.", + Description: "A list of tags used to associate different resources.", }, "create_time": { Type: schema.TypeString, diff --git a/tencentcloud/provider.go b/tencentcloud/provider.go index c0db52c208..d5ece63520 100644 --- a/tencentcloud/provider.go +++ b/tencentcloud/provider.go @@ -338,7 +338,7 @@ func Provider() *schema.Provider { "tencentcloud_scf_functions": dataSourceTencentCloudScfFunctions(), "tencentcloud_scf_namespaces": dataSourceTencentCloudScfNamespaces(), "tencentcloud_scf_logs": dataSourceTencentCloudScfLogs(), - "tencentcloud_vpn_customer_gateways": dataSourceTencentCloudVpnCustomerGateways(), + "tencentcloud_vpn_customer_gateways": dataSourceTencentCloudVpnCustomerGateways(), "tencentcloud_vpn_gateways": dataSourceTencentCloudVpnGateways(), "tencentcloud_vpn_connections": dataSourceTencentCloudVpnConnections(), }, @@ -425,7 +425,7 @@ func Provider() *schema.Provider { "tencentcloud_cfs_access_rule": resourceTencentCloudCfsAccessRule(), "tencentcloud_scf_function": resourceTencentCloudScfFunction(), "tencentcloud_scf_namespace": resourceTencentCloudScfNamespace(), - "tencentcloud_vpn_customer_gateway": resourceTencentCloudVpnCustomerGateway(), + "tencentcloud_vpn_customer_gateway": resourceTencentCloudVpnCustomerGateway(), "tencentcloud_vpn_gateway": resourceTencentCloudVpnGateway(), "tencentcloud_vpn_connection": resourceTencentCloudVpnConnection(), }, diff --git a/tencentcloud/resource_tc_vpn_connection.go b/tencentcloud/resource_tc_vpn_connection.go index 7a62fc50ed..98853544f9 100644 --- a/tencentcloud/resource_tc_vpn_connection.go +++ b/tencentcloud/resource_tc_vpn_connection.go @@ -450,10 +450,9 @@ func resourceTencentCloudVpnConnectionCreate(d *schema.ResourceData, meta interf if *result.Response.VpnConnectionSet[0].State == VPN_STATE_AVAILABLE { return nil } else { - return resource.RetryableError(fmt.Errorf("State is not availabe: %s, wait for state to be AVAILABLE.", *result.Response.VpnConnectionSet[0].State)) + return resource.RetryableError(fmt.Errorf("State is not available: %s, wait for state to be AVAILABLE.", *result.Response.VpnConnectionSet[0].State)) } } - return nil } }) if err != nil { @@ -610,17 +609,17 @@ func resourceTencentCloudVpnConnectionUpdate(d *schema.ResourceData, meta interf changeFlag = true } ikeChangeKeySet := map[string]bool{ - "ike_proto_encry_algorithm": false, - "ike_proto_authen_algorithem": 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, + "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 { diff --git a/tencentcloud/resource_tc_vpn_gateway.go b/tencentcloud/resource_tc_vpn_gateway.go index 3abfa14e07..3fad926854 100644 --- a/tencentcloud/resource_tc_vpn_gateway.go +++ b/tencentcloud/resource_tc_vpn_gateway.go @@ -191,7 +191,7 @@ func resourceTencentCloudVpnGatewayCreate(d *schema.ResourceData, meta interface if *result.Response.VpnGatewaySet[0].State == VPN_STATE_AVAILABLE { return nil } else { - return resource.RetryableError(fmt.Errorf("State is not availabe: %s, wait for state to be AVAILABLE.", *result.Response.VpnGatewaySet[0].State)) + return resource.RetryableError(fmt.Errorf("State is not available: %s, wait for state to be AVAILABLE.", *result.Response.VpnGatewaySet[0].State)) } } } 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/tencentcloud.erb b/website/tencentcloud.erb index abfbdb423f..c1fcd69088 100644 --- a/website/tencentcloud.erb +++ b/website/tencentcloud.erb @@ -242,6 +242,15 @@ > tencentcloud_vpc_subnets + > + tencentcloud_vpn_customer_gateways + + > + tencentcloud_vpn_gateways + + > + tencentcloud_vpn_connections + @@ -618,6 +627,15 @@ > tencentcloud_nat_gateway + > + tencentcloud_vpn_customer_gateway + + > + tencentcloud_vpn_gateway + + > + tencentcloud_vpn_connection + From ea2d951cde89fc9b06ff36fcb4b82e2732cc50e9 Mon Sep 17 00:00:00 2001 From: gailwang Date: Mon, 11 Nov 2019 23:16:16 +0800 Subject: [PATCH 3/5] remove extra space character --- tencentcloud/data_source_tc_nat_gateways.go | 66 +++++++++++-------- .../data_source_tc_vpn_connections.go | 4 +- .../data_source_tc_vpn_customer_gateways.go | 4 +- tencentcloud/resource_tc_vpn_connection.go | 19 +++--- .../resource_tc_vpn_connection_test.go | 4 +- .../resource_tc_vpn_customer_gateway.go | 17 +++-- .../resource_tc_vpn_customer_gateway_test.go | 4 +- tencentcloud/resource_tc_vpn_gateway.go | 7 +- tencentcloud/resource_tc_vpn_gateway_test.go | 4 +- .../docs/r/vpn_customer_gateway.html.markdown | 2 +- 10 files changed, 71 insertions(+), 60 deletions(-) diff --git a/tencentcloud/data_source_tc_nat_gateways.go b/tencentcloud/data_source_tc_nat_gateways.go index 654fbd7c2a..5a398ef051 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(DEFAULT_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) < DEFAULT_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 index 0614421413..be1870e467 100644 --- a/tencentcloud/data_source_tc_vpn_connections.go +++ b/tencentcloud/data_source_tc_vpn_connections.go @@ -309,7 +309,7 @@ func dataSourceTencentCloudVpnConnectionsRead(d *schema.ResourceData, meta inter return nil }) if err != nil { - log.Printf("[CRITAL]%s read VPN connection failed, reason:%s\n ", logId, err.Error()) + log.Printf("[CRITAL]%s read VPN connection failed, reason:%s\n", logId, err.Error()) return err } else { result = append(result, response.Response.VpnConnectionSet...) @@ -371,7 +371,7 @@ func dataSourceTencentCloudVpnConnectionsRead(d *schema.ResourceData, meta inter } 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()) + log.Printf("[CRITAL]%s provider set connection list fail, reason:%s\n", logId, e.Error()) return e } diff --git a/tencentcloud/data_source_tc_vpn_customer_gateways.go b/tencentcloud/data_source_tc_vpn_customer_gateways.go index e0c71c4dbc..e4bc6612ff 100644 --- a/tencentcloud/data_source_tc_vpn_customer_gateways.go +++ b/tencentcloud/data_source_tc_vpn_customer_gateways.go @@ -147,7 +147,7 @@ func dataSourceTencentCloudVpnCustomerGatewaysRead(d *schema.ResourceData, meta return nil }) if err != nil { - log.Printf("[CRITAL]%s read VPN customer gateway failed, reason:%s\n ", logId, err.Error()) + log.Printf("[CRITAL]%s read VPN customer gateway failed, reason:%s\n", logId, err.Error()) return err } else { result = append(result, response.Response.CustomerGatewaySet...) @@ -185,7 +185,7 @@ func dataSourceTencentCloudVpnCustomerGatewaysRead(d *schema.ResourceData, meta } 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()) + log.Printf("[CRITAL]%s provider set gateway list fail, reason:%s\n", logId, e.Error()) return e } diff --git a/tencentcloud/resource_tc_vpn_connection.go b/tencentcloud/resource_tc_vpn_connection.go index 98853544f9..5127b58319 100644 --- a/tencentcloud/resource_tc_vpn_connection.go +++ b/tencentcloud/resource_tc_vpn_connection.go @@ -370,7 +370,7 @@ func resourceTencentCloudVpnConnectionCreate(d *schema.ResourceData, meta interf return nil }) if err != nil { - log.Printf("[CRITAL]%s create VPN connection failed, reason:%s\n ", logId, err.Error()) + log.Printf("[CRITAL]%s create VPN connection failed, reason:%s\n", logId, err.Error()) return err } @@ -422,7 +422,7 @@ func resourceTencentCloudVpnConnectionCreate(d *schema.ResourceData, meta interf }) if err != nil { - log.Printf("[CRITAL]%s create VPN connection failed, reason:%s\n ", logId, err.Error()) + log.Printf("[CRITAL]%s create VPN connection failed, reason:%s\n", logId, err.Error()) return err } } @@ -456,7 +456,7 @@ func resourceTencentCloudVpnConnectionCreate(d *schema.ResourceData, meta interf } }) if err != nil { - log.Printf("[CRITAL]%s create VPN connection failed, reason:%s\n ", logId, err.Error()) + log.Printf("[CRITAL]%s create VPN connection failed, reason:%s\n", logId, err.Error()) return err } @@ -493,14 +493,14 @@ func resourceTencentCloudVpnConnectionRead(d *schema.ResourceData, meta interfac return retryError(e) } if len(result.Response.VpnConnectionSet) > 0 && *result.Response.VpnConnectionSet[0].State != VPN_STATE_AVAILABLE { - return resource.RetryableError(fmt.Errorf("state is not AVAILABLE, wait ...")) + return resource.RetryableError(fmt.Errorf("state is not `AVAILABLE`, wait ...")) } else { response = result return nil } }) if err != nil { - log.Printf("[CRITAL]%s read VPN connection failed, reason:%s\n ", logId, err.Error()) + log.Printf("[CRITAL]%s read VPN connection failed, reason:%s\n", logId, err.Error()) return err } if len(response.Response.VpnConnectionSet) < 1 { @@ -710,7 +710,7 @@ func resourceTencentCloudVpnConnectionUpdate(d *schema.ResourceData, meta interf return nil }) if err != nil { - log.Printf("[CRITAL]%s modify VPN connection failed, reason:%s\n ", logId, err.Error()) + log.Printf("[CRITAL]%s modify VPN connection failed, reason:%s\n", logId, err.Error()) return err } } @@ -772,7 +772,7 @@ func resourceTencentCloudVpnConnectionDelete(d *schema.ResourceData, meta interf 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.")) + 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", @@ -782,7 +782,7 @@ func resourceTencentCloudVpnConnectionDelete(d *schema.ResourceData, meta interf return nil }) if err != nil { - log.Printf("[CRITAL]%s delete VPN connection failed, reason:%s\n ", logId, err.Error()) + log.Printf("[CRITAL]%s delete VPN connection failed, reason:%s\n", logId, err.Error()) return err } //to get the status of vpn connection @@ -795,7 +795,6 @@ func resourceTencentCloudVpnConnectionDelete(d *schema.ResourceData, meta interf } else { //if not, quit if len(result.Response.VpnConnectionSet) == 0 { - log.Printf("deleting done") return nil } //else consider delete fail @@ -803,7 +802,7 @@ func resourceTencentCloudVpnConnectionDelete(d *schema.ResourceData, meta interf } }) if err != nil { - log.Printf("[CRITAL]%s delete VPN connection failed, reason:%s\n ", logId, err.Error()) + 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 index 4093013f81..4df69379f7 100644 --- a/tencentcloud/resource_tc_vpn_connection_test.go +++ b/tencentcloud/resource_tc_vpn_connection_test.go @@ -107,7 +107,7 @@ func testAccCheckVpnConnectionDestroy(s *terraform.State) error { return nil }) if err != nil { - log.Printf("[CRITAL]%s read VPN connection failed, reason:%s\n ", logId, err.Error()) + log.Printf("[CRITAL]%s read VPN connection failed, reason:%s\n", logId, err.Error()) ee, ok := err.(*errors.TencentCloudSDKError) if !ok { return err @@ -153,7 +153,7 @@ func testAccCheckVpnConnectionExists(n string) resource.TestCheckFunc { return nil }) if err != nil { - log.Printf("[CRITAL]%s read VPN connection failed, reason:%s\n ", logId, err.Error()) + log.Printf("[CRITAL]%s read VPN connection failed, reason:%s\n", logId, err.Error()) return err } if len(response.Response.VpnConnectionSet) != 1 { diff --git a/tencentcloud/resource_tc_vpn_customer_gateway.go b/tencentcloud/resource_tc_vpn_customer_gateway.go index 70d9948b89..f6e50477ac 100644 --- a/tencentcloud/resource_tc_vpn_customer_gateway.go +++ b/tencentcloud/resource_tc_vpn_customer_gateway.go @@ -19,7 +19,7 @@ Import VPN customer gateway can be imported using the id, e.g. ``` -$ terraform import tencentcloud_vpn_customer_gateway.foo nat-1asg3t63 +$ terraform import tencentcloud_vpn_customer_gateway.foo cgw-xfqag ``` */ package tencentcloud @@ -95,7 +95,7 @@ func resourceTencentCloudVpnCustomerGatewayCreate(d *schema.ResourceData, meta i return nil }) if err != nil { - log.Printf("[CRITAL]%s create VPN customer gateway failed, reason:%s\n ", logId, err.Error()) + log.Printf("[CRITAL]%s create VPN customer gateway failed, reason:%s\n", logId, err.Error()) return err } @@ -124,7 +124,7 @@ func resourceTencentCloudVpnCustomerGatewayCreate(d *schema.ResourceData, meta i } }) if err != nil { - log.Printf("[CRITAL]%s create VPN customer gateway failed, reason:%s\n ", logId, err.Error()) + log.Printf("[CRITAL]%s create VPN customer gateway failed, reason:%s\n", logId, err.Error()) return err } @@ -164,7 +164,7 @@ func resourceTencentCloudVpnCustomerGatewayRead(d *schema.ResourceData, meta int return nil }) if err != nil { - log.Printf("[CRITAL]%s read VPN customer gateway failed, reason:%s\n ", logId, err.Error()) + log.Printf("[CRITAL]%s read VPN customer gateway failed, reason:%s\n", logId, err.Error()) return err } if len(response.Response.CustomerGatewaySet) < 1 { @@ -211,7 +211,7 @@ func resourceTencentCloudVpnCustomerGatewayUpdate(d *schema.ResourceData, meta i return nil }) if err != nil { - log.Printf("[CRITAL]%s modify VPN customer gateway failed, reason:%s\n ", logId, err.Error()) + log.Printf("[CRITAL]%s modify VPN customer gateway failed, reason:%s\n", logId, err.Error()) return err } } @@ -278,7 +278,7 @@ func resourceTencentCloudVpnCustomerGatewayDelete(d *schema.ResourceData, meta i } }) if tErr != nil { - log.Printf("[CRITAL]%s create VPN connection failed, reason:%s\n ", logId, tErr.Error()) + log.Printf("[CRITAL]%s create VPN connection failed, reason:%s\n", logId, tErr.Error()) return tErr } @@ -294,7 +294,7 @@ func resourceTencentCloudVpnCustomerGatewayDelete(d *schema.ResourceData, meta i return nil }) if err != nil { - log.Printf("[CRITAL]%s delete VPN customer gateway failed, reason:%s\n ", logId, err.Error()) + log.Printf("[CRITAL]%s delete VPN customer gateway failed, reason:%s\n", logId, err.Error()) return err } //to get the status of customer gateway @@ -319,7 +319,6 @@ func resourceTencentCloudVpnCustomerGatewayDelete(d *schema.ResourceData, meta i } else { //if not, quit if len(result.Response.CustomerGatewaySet) == 0 { - log.Printf("deleting done") return nil } //else consider delete fail @@ -327,7 +326,7 @@ func resourceTencentCloudVpnCustomerGatewayDelete(d *schema.ResourceData, meta i } }) if err != nil { - log.Printf("[CRITAL]%s delete VPN customer gateway failed, reason:%s\n ", logId, err.Error()) + 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 index 1a0d071c9b..d964356578 100644 --- a/tencentcloud/resource_tc_vpn_customer_gateway_test.go +++ b/tencentcloud/resource_tc_vpn_customer_gateway_test.go @@ -70,7 +70,7 @@ func testAccCheckVpnCustomerGatewayDestroy(s *terraform.State) error { return nil }) if err != nil { - log.Printf("[CRITAL]%s read VPN customer gateway failed, reason:%s\n ", logId, err.Error()) + log.Printf("[CRITAL]%s read VPN customer gateway failed, reason:%s\n", logId, err.Error()) ee, ok := err.(*errors.TencentCloudSDKError) if !ok { return err @@ -116,7 +116,7 @@ func testAccCheckVpnCustomerGatewayExists(n string) resource.TestCheckFunc { return nil }) if err != nil { - log.Printf("[CRITAL]%s read VPN customer gateway failed, reason:%s\n ", logId, err.Error()) + log.Printf("[CRITAL]%s read VPN customer gateway failed, reason:%s\n", logId, err.Error()) return err } if len(response.Response.CustomerGatewaySet) != 1 { diff --git a/tencentcloud/resource_tc_vpn_gateway.go b/tencentcloud/resource_tc_vpn_gateway.go index 3fad926854..ffbe6efa40 100644 --- a/tencentcloud/resource_tc_vpn_gateway.go +++ b/tencentcloud/resource_tc_vpn_gateway.go @@ -386,7 +386,7 @@ func resourceTencentCloudVpnGatewayDelete(d *schema.ResourceData, meta interface } }) if tErr != nil { - log.Printf("[CRITAL]%s create VPN connection failed, reason:%s\n ", logId, tErr.Error()) + log.Printf("[CRITAL]%s create VPN connection failed, reason:%s\n", logId, tErr.Error()) return tErr } @@ -403,7 +403,7 @@ func resourceTencentCloudVpnGatewayDelete(d *schema.ResourceData, meta interface return nil }) if err != nil { - log.Printf("[CRITAL]%s delete VPN gateway failed, reason:%s\n ", logId, err.Error()) + log.Printf("[CRITAL]%s delete VPN gateway failed, reason:%s\n", logId, err.Error()) return err } //to get the status of gateway @@ -428,7 +428,6 @@ func resourceTencentCloudVpnGatewayDelete(d *schema.ResourceData, meta interface } else { //if not, quit if len(result.Response.VpnGatewaySet) == 0 { - log.Printf("deleting done") return nil } //else consider delete fail @@ -436,7 +435,7 @@ func resourceTencentCloudVpnGatewayDelete(d *schema.ResourceData, meta interface } }) if err != nil { - log.Printf("[CRITAL]%s delete VPN gateway failed, reason:%s\n ", logId, err.Error()) + 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 index ee63e692aa..e006a085ff 100644 --- a/tencentcloud/resource_tc_vpn_gateway_test.go +++ b/tencentcloud/resource_tc_vpn_gateway_test.go @@ -75,7 +75,7 @@ func testAccCheckVpnGatewayDestroy(s *terraform.State) error { return nil }) if err != nil { - log.Printf("[CRITAL]%s read VPN gateway failed, reason:%s\n ", logId, err.Error()) + log.Printf("[CRITAL]%s read VPN gateway failed, reason:%s\n", logId, err.Error()) ee, ok := err.(*errors.TencentCloudSDKError) if !ok { return err @@ -121,7 +121,7 @@ func testAccCheckVpnGatewayExists(n string) resource.TestCheckFunc { return nil }) if err != nil { - log.Printf("[CRITAL]%s read VPN gateway failed, reason:%s\n ", logId, err.Error()) + log.Printf("[CRITAL]%s read VPN gateway failed, reason:%s\n", logId, err.Error()) return err } if len(response.Response.VpnGatewaySet) != 1 { diff --git a/website/docs/r/vpn_customer_gateway.html.markdown b/website/docs/r/vpn_customer_gateway.html.markdown index 7a3c826a94..5e7ba1e461 100644 --- a/website/docs/r/vpn_customer_gateway.html.markdown +++ b/website/docs/r/vpn_customer_gateway.html.markdown @@ -43,6 +43,6 @@ In addition to all arguments above, the following attributes are exported: VPN customer gateway can be imported using the id, e.g. ``` -$ terraform import tencentcloud_vpn_customer_gateway.foo nat-1asg3t63 +$ terraform import tencentcloud_vpn_customer_gateway.foo cgw-xfqag ``` From db420ffbc5cb62694afe6f966d91ac3cf7fb205a Mon Sep 17 00:00:00 2001 From: gailwang Date: Tue, 12 Nov 2019 14:23:05 +0800 Subject: [PATCH 4/5] fix bugs --- CHANGELOG.md | 14 ++++++-- examples/tencentcloud-vpn/main.tf | 2 ++ tencentcloud/data_source_tc_nat_gateways.go | 4 +-- .../data_source_tc_vpn_connections.go | 6 ++-- .../data_source_tc_vpn_customer_gateways.go | 6 ++-- tencentcloud/data_source_tc_vpn_gateways.go | 11 +++--- tencentcloud/extension_vpc.go | 13 ++++++- tencentcloud/resource_tc_vpn_connection.go | 34 ++++++++++++------- .../resource_tc_vpn_customer_gateway.go | 14 ++++---- tencentcloud/resource_tc_vpn_gateway.go | 12 +++---- 10 files changed, 71 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 552994e866..4bc4d3ee7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,14 @@ ## 1.22.1 (Unreleased) + +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` + BUG FIXES: * Fixed docs of CAM @@ -20,9 +30,7 @@ FEATURES: * **New Resource**: `tencentcloud_cfs_access_rule` * **New Resource**: `tencentcloud_scf_function` * **New Resource**: `tencentcloud_scf_namespace` -* **New Resource**: `tencentcloud_vpn_gateway` -* **New Resource**: `tencentcloud_customer_gateway` -* **New Resource**: `tencentcloud_vpn_connection` + ## 1.21.2 (October 29, 2019) diff --git a/examples/tencentcloud-vpn/main.tf b/examples/tencentcloud-vpn/main.tf index 3ff79028fd..b82bfc506c 100644 --- a/examples/tencentcloud-vpn/main.tf +++ b/examples/tencentcloud-vpn/main.tf @@ -2,6 +2,7 @@ resource "tencentcloud_vpn_customer_gateway" "example" { name = "example" public_ip_address = "3.3.3.3" + tags = { test = "example" } @@ -21,6 +22,7 @@ resource "tencentcloud_vpn_gateway" "example" { test = "test" } } + resource "tencentcloud_vpn_connection" "example" { name = "example" vpc_id = "${data.tencentcloud_vpc_instances.example.instance_list.0.vpc_id}" diff --git a/tencentcloud/data_source_tc_nat_gateways.go b/tencentcloud/data_source_tc_nat_gateways.go index 5a398ef051..5b47405ade 100644 --- a/tencentcloud/data_source_tc_nat_gateways.go +++ b/tencentcloud/data_source_tc_nat_gateways.go @@ -129,7 +129,7 @@ func dataSourceTencentCloudNatGatewaysRead(d *schema.ResourceData, meta interfac offset := uint64(0) request.Offset = &offset result := make([]*vpc.NatGateway, 0) - limit := uint64(DEFAULT_LIMIT) + limit := uint64(NAT_DESCRIBE_LIMIT) for { var response *vpc.DescribeNatGatewaysResponse err := resource.Retry(readRetryTimeout, func() *resource.RetryError { @@ -147,7 +147,7 @@ func dataSourceTencentCloudNatGatewaysRead(d *schema.ResourceData, meta interfac return err } else { result = append(result, response.Response.NatGatewaySet...) - if len(response.Response.NatGatewaySet) < DEFAULT_LIMIT { + if len(response.Response.NatGatewaySet) < NAT_DESCRIBE_LIMIT { break } else { offset = offset + limit diff --git a/tencentcloud/data_source_tc_vpn_connections.go b/tencentcloud/data_source_tc_vpn_connections.go index be1870e467..6f0ac9c8cf 100644 --- a/tencentcloud/data_source_tc_vpn_connections.go +++ b/tencentcloud/data_source_tc_vpn_connections.go @@ -295,7 +295,7 @@ func dataSourceTencentCloudVpnConnectionsRead(d *schema.ResourceData, meta inter offset := uint64(0) request.Offset = &offset result := make([]*vpc.VpnConnection, 0) - limit := uint64(DEFAULT_LIMIT) + limit := uint64(VPN_DESCRIBE_LIMIT) for { var response *vpc.DescribeVpnConnectionsResponse err := resource.Retry(readRetryTimeout, func() *resource.RetryError { @@ -313,7 +313,7 @@ func dataSourceTencentCloudVpnConnectionsRead(d *schema.ResourceData, meta inter return err } else { result = append(result, response.Response.VpnConnectionSet...) - if len(response.Response.VpnConnectionSet) < 100 { + if len(response.Response.VpnConnectionSet) < VPN_DESCRIBE_LIMIT { break } else { offset = offset + limit @@ -329,7 +329,7 @@ func dataSourceTencentCloudVpnConnectionsRead(d *schema.ResourceData, meta inter if err != nil { return err } - if tags != nil { + if len(tags) > 0 { if !reflect.DeepEqual(respTags, tags) { continue } diff --git a/tencentcloud/data_source_tc_vpn_customer_gateways.go b/tencentcloud/data_source_tc_vpn_customer_gateways.go index e4bc6612ff..64cf7c0fad 100644 --- a/tencentcloud/data_source_tc_vpn_customer_gateways.go +++ b/tencentcloud/data_source_tc_vpn_customer_gateways.go @@ -132,7 +132,7 @@ func dataSourceTencentCloudVpnCustomerGatewaysRead(d *schema.ResourceData, meta offset := uint64(0) request.Offset = &offset result := make([]*vpc.CustomerGateway, 0) - limit := uint64(DEFAULT_LIMIT) + limit := uint64(VPN_DESCRIBE_LIMIT) for { var response *vpc.DescribeCustomerGatewaysResponse //add for cycle and add this to nat too @@ -151,7 +151,7 @@ func dataSourceTencentCloudVpnCustomerGatewaysRead(d *schema.ResourceData, meta return err } else { result = append(result, response.Response.CustomerGatewaySet...) - if len(response.Response.CustomerGatewaySet) < 100 { + if len(response.Response.CustomerGatewaySet) < VPN_DESCRIBE_LIMIT { break } else { offset = offset + limit @@ -167,7 +167,7 @@ func dataSourceTencentCloudVpnCustomerGatewaysRead(d *schema.ResourceData, meta if err != nil { return err } - if tags != nil { + if len(tags) > 0 { if !reflect.DeepEqual(respTags, tags) { continue } diff --git a/tencentcloud/data_source_tc_vpn_gateways.go b/tencentcloud/data_source_tc_vpn_gateways.go index bf06ef4a6b..4cb714b0d2 100644 --- a/tencentcloud/data_source_tc_vpn_gateways.go +++ b/tencentcloud/data_source_tc_vpn_gateways.go @@ -145,13 +145,12 @@ func dataSourceTencentCloudVpnGateways() *schema.Resource { }, "zone": { Type: schema.TypeString, - Required: true, - ForceNew: true, + Computed: true, Description: "Zone of the VPN gateway.", }, "tags": { Type: schema.TypeMap, - Optional: true, + Computed: true, Description: "A list of tags used to associate different resources.", }, "create_time": { @@ -207,7 +206,7 @@ func dataSourceTencentCloudVpnGatewaysRead(d *schema.ResourceData, meta interfac offset := uint64(0) request.Offset = &offset result := make([]*vpc.VpnGateway, 0) - limit := uint64(DEFAULT_LIMIT) + limit := uint64(VPN_DESCRIBE_LIMIT) for { var response *vpc.DescribeVpnGatewaysResponse //add for cycle and add this to nat too @@ -226,7 +225,7 @@ func dataSourceTencentCloudVpnGatewaysRead(d *schema.ResourceData, meta interfac return err } else { result = append(result, response.Response.VpnGatewaySet...) - if len(response.Response.VpnGatewaySet) < 100 { + if len(response.Response.VpnGatewaySet) < VPN_DESCRIBE_LIMIT { break } else { offset = offset + limit @@ -242,7 +241,7 @@ func dataSourceTencentCloudVpnGatewaysRead(d *schema.ResourceData, meta interfac if err != nil { return err } - if tags != nil { + if len(tags) > 0 { if !reflect.DeepEqual(respTags, tags) { continue } diff --git a/tencentcloud/extension_vpc.go b/tencentcloud/extension_vpc.go index 34df91fa62..eddbca45a4 100644 --- a/tencentcloud/extension_vpc.go +++ b/tencentcloud/extension_vpc.go @@ -12,7 +12,6 @@ const GATE_WAY_TYPE_NAT = "NAT" const GATE_WAY_TYPE_NORMAL_CVM = "NORMAL_CVM" const GATE_WAY_TYPE_EIP = "EIP" const GATE_WAY_TYPE_CCN = "CCN" -const DEFAULT_LIMIT = 100 var ALL_GATE_WAY_TYPES = []string{GATE_WAY_TYPE_CVM, GATE_WAY_TYPE_VPN, @@ -86,10 +85,22 @@ 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" diff --git a/tencentcloud/resource_tc_vpn_connection.go b/tencentcloud/resource_tc_vpn_connection.go index 5127b58319..adc87bd01b 100644 --- a/tencentcloud/resource_tc_vpn_connection.go +++ b/tencentcloud/resource_tc_vpn_connection.go @@ -375,7 +375,6 @@ func resourceTencentCloudVpnConnectionCreate(d *schema.ResourceData, meta interf } if response.Response.VpnConnection == nil { - d.SetId("") return fmt.Errorf("VPN connection id is nil") } @@ -428,7 +427,6 @@ func resourceTencentCloudVpnConnectionCreate(d *schema.ResourceData, meta interf } if vpnConnectionId == "" { - d.SetId("") return fmt.Errorf("VPN connection id is nil") } @@ -465,7 +463,7 @@ func resourceTencentCloudVpnConnectionCreate(d *schema.ResourceData, meta interf tagService := TagService{client: meta.(*TencentCloudClient).apiV3Conn} region := meta.(*TencentCloudClient).apiV3Conn.Region - resourceName := fmt.Sprintf("qcs::vpc:%s:uin/:vpnx/%s", region, vpnConnectionId) + resourceName := BuildTagResourceName("vpc", "vpnx", region, vpnConnectionId) if err := tagService.ModifyTags(ctx, resourceName, tags, nil); err != nil { return err @@ -492,19 +490,17 @@ func resourceTencentCloudVpnConnectionRead(d *schema.ResourceData, meta interfac logId, request.GetAction(), request.ToJsonString(), e.Error()) return retryError(e) } - if len(result.Response.VpnConnectionSet) > 0 && *result.Response.VpnConnectionSet[0].State != VPN_STATE_AVAILABLE { - return resource.RetryableError(fmt.Errorf("state is not `AVAILABLE`, wait ...")) - } else { - response = result - return nil - } + + 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 nil") + d.SetId("") + return nil } connection := response.Response.VpnConnectionSet[0] @@ -744,7 +740,7 @@ func resourceTencentCloudVpnConnectionUpdate(d *schema.ResourceData, meta interf client: meta.(*TencentCloudClient).apiV3Conn, } region := meta.(*TencentCloudClient).apiV3Conn.Region - resourceName := fmt.Sprintf("qcs::vpc:%s:uin/:vpnx/%s", region, connectionId) + resourceName := BuildTagResourceName("vpc", "vpnx", region, connectionId) err := tagService.ModifyTags(ctx, resourceName, replaceTags, deleteTags) if err != nil { return err @@ -791,14 +787,26 @@ func resourceTencentCloudVpnConnectionDelete(d *schema.ResourceData, meta interf err = resource.Retry(3*time.Minute, func() *resource.RetryError { result, e := meta.(*TencentCloudClient).apiV3Conn.UseVpcClient().DescribeVpnConnections(statRequest) if e != nil { - return 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("deleting retry")) + return resource.RetryableError(fmt.Errorf("describe retry")) } }) if err != nil { diff --git a/tencentcloud/resource_tc_vpn_customer_gateway.go b/tencentcloud/resource_tc_vpn_customer_gateway.go index f6e50477ac..5e02dd4e62 100644 --- a/tencentcloud/resource_tc_vpn_customer_gateway.go +++ b/tencentcloud/resource_tc_vpn_customer_gateway.go @@ -100,7 +100,6 @@ func resourceTencentCloudVpnCustomerGatewayCreate(d *schema.ResourceData, meta i } if response.Response.CustomerGateway == nil { - d.SetId("") return fmt.Errorf("VPN customer gateway id is nil") } customerGatewayId := *response.Response.CustomerGateway.CustomerGatewayId @@ -133,7 +132,7 @@ func resourceTencentCloudVpnCustomerGatewayCreate(d *schema.ResourceData, meta i tagService := TagService{client: meta.(*TencentCloudClient).apiV3Conn} region := meta.(*TencentCloudClient).apiV3Conn.Region - resourceName := fmt.Sprintf("qcs::vpc:%s:uin/:cgw/%s", region, customerGatewayId) + resourceName := BuildTagResourceName("vpc", "cgw", region, customerGatewayId) if err := tagService.ModifyTags(ctx, resourceName, tags, nil); err != nil { return err @@ -168,7 +167,8 @@ func resourceTencentCloudVpnCustomerGatewayRead(d *schema.ResourceData, meta int return err } if len(response.Response.CustomerGatewaySet) < 1 { - return fmt.Errorf("VPN customer gateway id is nil") + d.SetId("") + return nil } gateway := response.Response.CustomerGatewaySet[0] @@ -214,8 +214,6 @@ func resourceTencentCloudVpnCustomerGatewayUpdate(d *schema.ResourceData, meta i log.Printf("[CRITAL]%s modify VPN customer gateway failed, reason:%s\n", logId, err.Error()) return err } - } - if d.HasChange("name") { d.SetPartial("name") } @@ -227,7 +225,7 @@ func resourceTencentCloudVpnCustomerGatewayUpdate(d *schema.ResourceData, meta i client: meta.(*TencentCloudClient).apiV3Conn, } region := meta.(*TencentCloudClient).apiV3Conn.Region - resourceName := fmt.Sprintf("qcs::vpc:%s:uin/:cgw/%s", region, customerGatewayId) + resourceName := BuildTagResourceName("vpc", "cgw", region, customerGatewayId) err := tagService.ModifyTags(ctx, resourceName, replaceTags, deleteTags) if err != nil { return err @@ -246,7 +244,7 @@ func resourceTencentCloudVpnCustomerGatewayDelete(d *schema.ResourceData, meta i customerGatewayId := d.Id() - //check the customer gateway is not related with any tunnels + //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) @@ -278,7 +276,7 @@ func resourceTencentCloudVpnCustomerGatewayDelete(d *schema.ResourceData, meta i } }) if tErr != nil { - log.Printf("[CRITAL]%s create VPN connection failed, reason:%s\n", logId, tErr.Error()) + log.Printf("[CRITAL]%s describe VPN connection failed, reason:%s\n", logId, tErr.Error()) return tErr } diff --git a/tencentcloud/resource_tc_vpn_gateway.go b/tencentcloud/resource_tc_vpn_gateway.go index ffbe6efa40..1e3c2b9fdd 100644 --- a/tencentcloud/resource_tc_vpn_gateway.go +++ b/tencentcloud/resource_tc_vpn_gateway.go @@ -168,7 +168,6 @@ func resourceTencentCloudVpnGatewayCreate(d *schema.ResourceData, meta interface } if response.Response.VpnGateway == nil { - d.SetId("") return fmt.Errorf("VPN gateway id is nil") } gatewayId := *response.Response.VpnGateway.VpnGatewayId @@ -206,7 +205,7 @@ func resourceTencentCloudVpnGatewayCreate(d *schema.ResourceData, meta interface tagService := TagService{client: meta.(*TencentCloudClient).apiV3Conn} region := meta.(*TencentCloudClient).apiV3Conn.Region - resourceName := fmt.Sprintf("qcs::vpc:%s:uin/:vpngw/%s", region, gatewayId) + resourceName := BuildTagResourceName("vpc", "vpngw", region, gatewayId) if err := tagService.ModifyTags(ctx, resourceName, tags, nil); err != nil { return err @@ -241,7 +240,8 @@ func resourceTencentCloudVpnGatewayRead(d *schema.ResourceData, meta interface{} return err } if len(response.Response.VpnGatewaySet) < 1 { - return fmt.Errorf("VPN gateway id is nil") + d.SetId("") + return nil } gateway := response.Response.VpnGatewaySet[0] @@ -331,7 +331,7 @@ func resourceTencentCloudVpnGatewayUpdate(d *schema.ResourceData, meta interface client: meta.(*TencentCloudClient).apiV3Conn, } region := meta.(*TencentCloudClient).apiV3Conn.Region - resourceName := fmt.Sprintf("qcs::vpc:%s:uin/:vpngw/%s", region, gatewayId) + resourceName := BuildTagResourceName("vpc", "vpngw", region, gatewayId) err := tagService.ModifyTags(ctx, resourceName, replaceTags, deleteTags) if err != nil { return err @@ -350,7 +350,7 @@ func resourceTencentCloudVpnGatewayDelete(d *schema.ResourceData, meta interface gatewayId := d.Id() - //check the vpn gateway is not related with any tunnels + //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) @@ -386,7 +386,7 @@ func resourceTencentCloudVpnGatewayDelete(d *schema.ResourceData, meta interface } }) if tErr != nil { - log.Printf("[CRITAL]%s create VPN connection failed, reason:%s\n", logId, tErr.Error()) + log.Printf("[CRITAL]%s describe VPN connection failed, reason:%s\n", logId, tErr.Error()) return tErr } From f32f4da4c711d53a39c5c5f1c3462e606ecd0eb0 Mon Sep 17 00:00:00 2001 From: gailwang Date: Tue, 12 Nov 2019 19:46:56 +0800 Subject: [PATCH 5/5] fix version issue --- CHANGELOG.md | 5 +---- tencentcloud/provider.go | 2 ++ tencentcloud/resource_tc_vpn_gateway.go | 1 + website/docs/d/vpn_gateways.html.markdown | 2 ++ website/tencentcloud.erb | 7 +++++++ 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4bc4d3ee7c..793e991f66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## 1.22.1 (Unreleased) +## 1.23.0 (Unreleased) FEATURES: @@ -21,9 +21,6 @@ FEATURES: * **New Data Source**: `tencentcloud_cfs_access_rules` * **New Data Source**: `tencentcloud_scf_functions` * **New Data Source**: `tencentcloud_scf_namespaces` -* **New Data Source**: `tencentcloud_vpn_gateways` -* **New Data Source**: `tencentcloud_customer_gateways` -* **New Data Source**: `tencentcloud_vpn_connections` * **New Data Source**: `tencentcloud_scf_logs` * **New Resource**: `tencentcloud_cfs_file_system` * **New Resource**: `tencentcloud_cfs_access_group` diff --git a/tencentcloud/provider.go b/tencentcloud/provider.go index d5ece63520..1a692ccac2 100644 --- a/tencentcloud/provider.go +++ b/tencentcloud/provider.go @@ -218,6 +218,8 @@ VPC Resources tencentcloud_route_table_entry tencentcloud_dnat tencentcloud_nat_gateway + +VPN Resources tencentcloud_vpn_customer_gateway tencentcloud_vpn_gateway tencentcloud_vpn_connection diff --git a/tencentcloud/resource_tc_vpn_gateway.go b/tencentcloud/resource_tc_vpn_gateway.go index 1e3c2b9fdd..2f2082e0fb 100644 --- a/tencentcloud/resource_tc_vpn_gateway.go +++ b/tencentcloud/resource_tc_vpn_gateway.go @@ -64,6 +64,7 @@ func resourceTencentCloudVpnGateway() *schema.Resource { "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.", }, diff --git a/website/docs/d/vpn_gateways.html.markdown b/website/docs/d/vpn_gateways.html.markdown index eea8350d3f..24224c0350 100644 --- a/website/docs/d/vpn_gateways.html.markdown +++ b/website/docs/d/vpn_gateways.html.markdown @@ -54,7 +54,9 @@ In addition to all arguments above, the following attributes are exported: * `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/tencentcloud.erb b/website/tencentcloud.erb index c1fcd69088..e2163b42c2 100644 --- a/website/tencentcloud.erb +++ b/website/tencentcloud.erb @@ -627,6 +627,13 @@ > tencentcloud_nat_gateway + + + + > + VPN Resources +