diff --git a/docs/resources/vpn_gateway_v5.md b/docs/resources/vpn_gateway_v5.md
new file mode 100644
index 000000000..8de7b7680
--- /dev/null
+++ b/docs/resources/vpn_gateway_v5.md
@@ -0,0 +1,252 @@
+---
+subcategory: "Virtual Private Network (VPN)"
+layout: "opentelekomcloud"
+page_title: "OpenTelekomCloud: opentelekomcloud_enterprise_vpn_gateway_v5"
+sidebar_current: "docs-opentelekomcloud-resource-enterprise-vpn-gateway-v5"
+description: |-
+Manages a Enterprise VPN Gateway Service resource within OpenTelekomCloud.
+---
+
+# opentelekomcloud_enterprise_vpn_gateway_v5
+
+Manages a VPN gateway resource within OpenTelekomCloud.
+
+## Example Usage
+
+### Basic Usage
+
+```hcl
+variable "name" {}
+
+resource "opentelekomcloud_enterprise_vpn_gateway_v5" "gw_1" {
+ name = var.name
+ vpc_id = opentelekomcloud_vpc_v1.vpc.id
+ local_subnets = [opentelekomcloud_vpc_subnet_v1.subnet.cidr]
+ connect_subnet = opentelekomcloud_vpc_subnet_v1.subnet.id
+
+ availability_zones = [
+ "eu-de-01",
+ "eu-de-02"
+ ]
+
+ eip1 {
+ id = opentelekomcloud_vpc_eip_v1.eip_1.id
+ }
+
+ eip2 {
+ id = opentelekomcloud_vpc_eip_v1.eip_2.id
+ }
+
+ tags = {
+ key = "val"
+ foo = "bar"
+ }
+}
+```
+
+### Creating a VPN gateway with creating new EIPs
+
+```hcl
+variable "name" {}
+
+resource "opentelekomcloud_enterprise_vpn_gateway_v5" "gw_1" {
+ name = var.name
+ ha_mode = "active-standby"
+ vpc_id = opentelekomcloud_vpc_v1.vpc.id
+ local_subnets = [opentelekomcloud_vpc_subnet_v1.subnet.cidr]
+ connect_subnet = opentelekomcloud_vpc_subnet_v1.subnet.id
+
+ availability_zones = [
+ "eu-de-01",
+ "eu-de-02"
+ ]
+
+ eip1 {
+ bandwidth_name = "evpn-gw-bw-1"
+ type = "5_bgp"
+ bandwidth_size = 5
+ charge_mode = "traffic"
+ }
+
+ eip2 {
+ bandwidth_name = "evpn-gw-bw-2"
+ type = "5_bgp"
+ bandwidth_size = 5
+ charge_mode = "traffic"
+ }
+}
+
+```
+
+### Creating a private VPN gateway with Enterprise Router
+
+```hcl
+variable "name" {}
+variable "er_id" {}
+
+resource "opentelekomcloud_enterprise_vpn_gateway_v5" "gw_1" {
+ name = var.name
+ network_type = "private"
+ attachment_type = "er"
+ er_id = var.er_id
+
+ availability_zones = [
+ "eu-de-01",
+ "eu-de-02"
+ ]
+
+ access_vpc_id = opentelekomcloud_vpc_v1.vpc_er.id
+ access_subnet_id = opentelekomcloud_vpc_subnet_v1.subnet_er.id
+
+ access_private_ip_1 = "172.16.0.99"
+ access_private_ip_2 = "172.16.0.100"
+}
+```
+
+## Argument Reference
+
+The following arguments are supported:
+* `name` - (Required, String) The name of the VPN gateway.
+ The valid length is limited from `1` to `64`, only letters, digits, hyphens (-) and underscores (_) are allowed.
+
+* `availability_zones` - (Required, List, ForceNew) The list of availability zone IDs.
+ Changing this parameter will create a new resource.
+
+* `flavor` - (Optional, String, ForceNew) The flavor of the VPN gateway.
+ The value can be `Basic`, `Professional1`, `Professional2`. Defaults to `Professional1`.
+ Changing this parameter will create a new resource.
+
+* `attachment_type` - (Optional, String, ForceNew) The attachment type. The value can be `vpc` and `er`.
+ Defaults to `vpc`.
+ Changing this parameter will create a new resource.
+
+* `network_type` - (Optional, String, ForceNew) The network type. The value can be `public` and `private`.
+ Defaults to `public`.
+ Changing this parameter will create a new resource.
+
+* `vpc_id` - (Optional, String, ForceNew) The ID of the VPC to which the VPN gateway is connected.
+ This parameter is mandatory when `attachment_type` is `vpc`.
+ Changing this parameter will create a new resource.
+
+* `local_subnets` - (Optional, List) The list of local subnets.
+ This parameter is mandatory when `attachment_type` is `vpc`.
+
+* `connect_subnet` - (Optional, String, ForceNew) The Network ID of the VPC subnet used by the VPN gateway.
+ This parameter is mandatory when `attachment_type` is `vpc`.
+ Changing this parameter will create a new resource.
+
+* `er_id` - (Optional, String, ForceNew) The enterprise router ID to attach with to VPN gateway.
+ This parameter is mandatory when `attachment_type` is `er`.
+ Changing this parameter will create a new resource.
+
+* `ha_mode` - (Optional, String, ForceNew) The HA mode of VPN gateway. Valid values are `active-active` and
+ `active-standby`. The default value is `active-active`.
+ Changing this parameter will create a new resource.
+
+* `eip1` - (Optional, List) The master 1 IP in active-active VPN gateway or the master IP
+ in active-standby VPN gateway. This parameter is mandatory when `network_type` is `public` or left empty.
+ The [object](#GwCreateRequestEip) structure is documented below.
+
+* `eip2` - (Optional, List, ForceNew) The master 2 IP in active-active VPN gateway or the slave IP
+ in active-standby VPN gateway. This parameter is mandatory when `network_type` is **public** or left empty.
+ The [object](#GwCreateRequestEip) structure is documented below.
+
+* `access_vpc_id` - (Optional, String, ForceNew) The access VPC ID.
+ The default value is the value of `vpc_id`.
+ Changing this parameter will create a new resource.
+
+* `access_subnet_id` - (Optional, String, ForceNew) The access subnet ID.
+ The default value is the value of `connect_subnet`.
+ Changing this parameter will create a new resource.
+
+* `access_private_ip_1` - (Optional, String, ForceNew) The private IP 1 in private network type VPN gateway.
+ It is the master IP 1 in `active-active` HA mode, and the master IP in `active-standby` HA mode.
+ Must declare the `access_private_ip_2` at the same time, and can not use the same IP value.
+ Changing this parameter will create a new resource.
+
+* `access_private_ip_2` - (Optional, String, ForceNew) The private IP 2 in private network type VPN gateway.
+ It is the master IP 2 in `active-active` HA mode, and the slave IP in `active-standby` HA mode.
+ Must declare the `access_private_ip_1` at the same time, and can not use the same IP value.
+ Changing this parameter will create a new resource.
+
+* `asn` - (Optional, Int, ForceNew) The ASN number of BGP. The value ranges from `1` to `4,294,967,295`.
+ Defaults to `64,512`.
+ Changing this parameter will create a new resource.
+
+
+The `eip1` or `eip2` block supports:
+
+* `id` - (Optional, String, ForceNew) The public IP ID.
+ Changing this parameter will create a new resource.
+
+* `type` - (Optional, String, ForceNew) The EIP type.
+ Changing this parameter will create a new resource.
+
+* `bandwidth_name` - (Optional, String, ForceNew) The bandwidth name.
+ The valid length is limited from `1` to `64`, only letters, digits, hyphens (-) and underscores (_) are allowed.
+ Changing this parameter will create a new resource.
+
+* `bandwidth_size` - (Optional, Int, ForceNew) Bandwidth size in Mbit/s. When the `flavor` is `Basic`, the value
+ cannot be greater than `100`. When the `flavor` is `Professional1`, the value cannot be greater than `300`.
+ When the `flavor` is `Professional2`, the value cannot be greater than `1,000`.
+ Changing this parameter will create a new resource.
+
+* `charge_mode` - (Optional, String, ForceNew) The charge mode of the bandwidth. The value can be `bandwidth` and `traffic`.
+ Changing this parameter will create a new resource.
+
+ ~> You can use `id` to specify an existing EIP or use `type`, `bandwidth_name`, `bandwidth_size` and `charge_mode` to
+ create a new EIP.
+
+* `tags` - (Optional, Map) Specifies the tags of the VPN gateway.
+
+
+## Attribute Reference
+
+In addition to all arguments above, the following attributes are exported:
+
+* `id` - The ID of the VPN gateway
+
+* `status` - The status of VPN gateway.
+
+* `created_at` - The create time.
+
+* `updated_at` - The update time.
+
+* `used_connection_group` - The number of used connection groups.
+
+* `used_connection_number` - The number of used connections.
+
+* `er_attachment_id` - The ER attachment ID.
+
+* `region` - Specifies the region in which to create the resource.
+
+* `eip1` - The master 1 IP in active-active VPN gateway or the master IP in active-standby VPN gateway.
+ The [object](#GatewayGetResponseEip) structure is documented below.
+
+* `eip2` - The master 2 IP in active-active VPN gateway or the slave IP in active-standby VPN gateway.
+ The [object](#GatewayGetResponseEip) structure is documented below.
+
+
+The `eip1` or `eip2` block supports:
+
+* `bandwidth_id` - The bandwidth ID.
+
+* `ip_address` - The public IP address.
+
+* `ip_version` - Specifies the EIP version.
+
+## Timeouts
+
+This resource provides the following timeouts configuration options:
+
+* `create` - Default is 10 minutes.
+* `update` - Default is 10 minutes.
+* `delete` - Default is 10 minutes.
+
+## Import
+
+The gateway can be imported using the `id`, e.g.
+
+```bash
+$ terraform import opentelekomcloud_enterprise_vpn_gateway_v5.test
+```
diff --git a/go.mod b/go.mod
index aba0714da..e6f9c59d2 100644
--- a/go.mod
+++ b/go.mod
@@ -17,7 +17,7 @@ require (
github.com/jmespath/go-jmespath v0.4.0
github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4
github.com/mitchellh/go-homedir v1.1.0
- github.com/opentelekomcloud/gophertelekomcloud v0.9.4-0.20241001094048-fbd948f2ab7e
+ github.com/opentelekomcloud/gophertelekomcloud v0.9.4-0.20241004091110-c63bcb5025ba
github.com/unknwon/com v1.0.1
golang.org/x/crypto v0.21.0
golang.org/x/sync v0.1.0
diff --git a/go.sum b/go.sum
index cec509e5b..ed28558d2 100644
--- a/go.sum
+++ b/go.sum
@@ -156,8 +156,8 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA
github.com/nsf/jsondiff v0.0.0-20200515183724-f29ed568f4ce h1:RPclfga2SEJmgMmz2k+Mg7cowZ8yv4Trqw9UsJby758=
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
-github.com/opentelekomcloud/gophertelekomcloud v0.9.4-0.20241001094048-fbd948f2ab7e h1:JT2bfxV3X4Pb+1H3rVts1olmWCqtiXb3PI+9z334WBU=
-github.com/opentelekomcloud/gophertelekomcloud v0.9.4-0.20241001094048-fbd948f2ab7e/go.mod h1:M1F6OfSRZRzAmAFKQqSLClX952at5hx5rHe4UTEykgg=
+github.com/opentelekomcloud/gophertelekomcloud v0.9.4-0.20241004091110-c63bcb5025ba h1:zq7iB7GwrjXqRdh/2WDXcaG82e34iq1X5j0giiFqGNw=
+github.com/opentelekomcloud/gophertelekomcloud v0.9.4-0.20241004091110-c63bcb5025ba/go.mod h1:M1F6OfSRZRzAmAFKQqSLClX952at5hx5rHe4UTEykgg=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
diff --git a/opentelekomcloud/acceptance/vpn/resource_opentelekomcloud_enterprise_vpn_gateway_v5_test.go b/opentelekomcloud/acceptance/vpn/resource_opentelekomcloud_enterprise_vpn_gateway_v5_test.go
new file mode 100644
index 000000000..c9c372785
--- /dev/null
+++ b/opentelekomcloud/acceptance/vpn/resource_opentelekomcloud_enterprise_vpn_gateway_v5_test.go
@@ -0,0 +1,337 @@
+package acceptance
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
+ "github.com/opentelekomcloud/gophertelekomcloud/openstack/evpn/v5/gateway"
+ "github.com/opentelekomcloud/terraform-provider-opentelekomcloud/opentelekomcloud/acceptance/common"
+ "github.com/opentelekomcloud/terraform-provider-opentelekomcloud/opentelekomcloud/acceptance/env"
+ "github.com/opentelekomcloud/terraform-provider-opentelekomcloud/opentelekomcloud/common/cfg"
+)
+
+const (
+ resourceEvpnGatewayName = "opentelekomcloud_enterprise_vpn_gateway_v5.gw_1"
+ resourceEvpnGatewayEip1Name = "opentelekomcloud_vpc_eip_v1.eip_1"
+ resourceEvpnGatewayEip2Name = "opentelekomcloud_vpc_eip_v1.eip_2"
+)
+
+func getGatewayResourceFunc(conf *cfg.Config, state *terraform.ResourceState) (interface{}, error) {
+ client, err := conf.EvpnV5Client(env.OS_REGION_NAME)
+ if err != nil {
+ return nil, fmt.Errorf("error creating OpenTelekomCloud EVPN v5 client: %s", err)
+ }
+ return gateway.Get(client, state.Primary.ID)
+}
+
+func TestAccGateway_basic(t *testing.T) {
+ var gw gateway.Gateway
+ name := fmt.Sprintf("evpn_acc_gw_%s", acctest.RandString(5))
+ updateName := fmt.Sprintf("evpn_acc_gw_up_%s", acctest.RandString(5))
+
+ rc := common.InitResourceCheck(
+ resourceEvpnGatewayName,
+ &gw,
+ getGatewayResourceFunc,
+ )
+
+ resource.ParallelTest(t, resource.TestCase{
+ PreCheck: func() { common.TestAccPreCheck(t) },
+ ProviderFactories: common.TestAccProviderFactories,
+ CheckDestroy: rc.CheckResourceDestroy(),
+ Steps: []resource.TestStep{
+ {
+ Config: testEvpnGateway_basic(name),
+ Check: resource.ComposeTestCheckFunc(
+ rc.CheckResourceExists(),
+ resource.TestCheckResourceAttr(resourceEvpnGatewayName, "name", name),
+ resource.TestCheckResourceAttr(resourceEvpnGatewayName, "ha_mode", "active-active"),
+ resource.TestCheckResourceAttr(resourceEvpnGatewayName, "status", "ACTIVE"),
+ resource.TestCheckResourceAttrPair(resourceEvpnGatewayName, "eip1.0.id", resourceEvpnGatewayEip1Name, "id"),
+ resource.TestCheckResourceAttrPair(resourceEvpnGatewayName, "eip2.0.id", resourceEvpnGatewayEip2Name, "id"),
+ resource.TestCheckResourceAttr(resourceEvpnGatewayName, "tags.key", "val"),
+ resource.TestCheckResourceAttr(resourceEvpnGatewayName, "tags.foo", "bar"),
+ ),
+ },
+ {
+ Config: testEvpnGateway_update(updateName),
+ Check: resource.ComposeTestCheckFunc(
+ rc.CheckResourceExists(),
+ resource.TestCheckResourceAttr(resourceEvpnGatewayName, "name", updateName),
+ resource.TestCheckResourceAttr(resourceEvpnGatewayName, "local_subnets.1", "192.168.2.0/24"),
+ resource.TestCheckResourceAttr(resourceEvpnGatewayName, "tags.key", "val"),
+ resource.TestCheckResourceAttr(resourceEvpnGatewayName, "tags.foo", "bar-update"),
+ ),
+ },
+ {
+ ResourceName: resourceEvpnGatewayName,
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ },
+ })
+}
+
+func TestAccGateway_activeStandbyHAMode(t *testing.T) {
+ var gw gateway.Gateway
+ name := fmt.Sprintf("evpn_acc_gw_%s", acctest.RandString(5))
+
+ rc := common.InitResourceCheck(
+ resourceEvpnGatewayName,
+ &gw,
+ getGatewayResourceFunc,
+ )
+
+ resource.ParallelTest(t, resource.TestCase{
+ PreCheck: func() { common.TestAccPreCheck(t) },
+ ProviderFactories: common.TestAccProviderFactories,
+ CheckDestroy: rc.CheckResourceDestroy(),
+ Steps: []resource.TestStep{
+ {
+ Config: testEvpnGateway_activeStandbyHAMode(name),
+ Check: resource.ComposeTestCheckFunc(
+ rc.CheckResourceExists(),
+ resource.TestCheckResourceAttr(resourceEvpnGatewayName, "name", name),
+ resource.TestCheckResourceAttr(resourceEvpnGatewayName, "ha_mode", "active-standby"),
+ resource.TestCheckResourceAttr(resourceEvpnGatewayName, "status", "ACTIVE"),
+ resource.TestCheckResourceAttr(resourceEvpnGatewayName, "eip1.0.bandwidth_name", "evpn-gw-bw-1"),
+ resource.TestCheckResourceAttr(resourceEvpnGatewayName, "eip2.0.bandwidth_name", "evpn-gw-bw-2"),
+ ),
+ },
+ {
+ ResourceName: resourceEvpnGatewayName,
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ },
+ })
+}
+
+func TestAccGateway_withER(t *testing.T) {
+ var gw gateway.Gateway
+ name := fmt.Sprintf("evpn_acc_gw_%s", acctest.RandString(5))
+
+ rc := common.InitResourceCheck(
+ resourceEvpnGatewayName,
+ &gw,
+ getGatewayResourceFunc,
+ )
+
+ resource.ParallelTest(t, resource.TestCase{
+ PreCheck: func() { common.TestAccPreCheck(t) },
+ ProviderFactories: common.TestAccProviderFactories,
+ CheckDestroy: rc.CheckResourceDestroy(),
+ Steps: []resource.TestStep{
+ {
+ Config: testEvpnGateway_withER(name),
+ Check: resource.ComposeTestCheckFunc(
+ rc.CheckResourceExists(),
+ resource.TestCheckResourceAttr(resourceEvpnGatewayName, "name", name),
+ resource.TestCheckResourceAttr(resourceEvpnGatewayName, "network_type", "private"),
+ resource.TestCheckResourceAttr(resourceEvpnGatewayName, "attachment_type", "er"),
+ resource.TestCheckResourceAttr(resourceEvpnGatewayName, "status", "ACTIVE"),
+ resource.TestCheckResourceAttr(resourceEvpnGatewayName, "access_private_ip_1", "172.16.0.99"),
+ resource.TestCheckResourceAttr(resourceEvpnGatewayName, "access_private_ip_2", "172.16.0.100"),
+ ),
+ },
+ {
+ ResourceName: resourceEvpnGatewayName,
+ ImportState: true,
+ ImportStateVerify: true,
+ },
+ },
+ })
+}
+
+func testEvpnGateway_base(name string) string {
+ return fmt.Sprintf(`
+resource "opentelekomcloud_vpc_v1" "vpc" {
+ name = "%[1]s"
+ cidr = "192.168.0.0/16"
+}
+
+resource "opentelekomcloud_vpc_subnet_v1" "subnet" {
+ name = "%[1]s"
+ vpc_id = opentelekomcloud_vpc_v1.vpc.id
+ cidr = "192.168.0.0/24"
+ gateway_ip = "192.168.0.1"
+}
+
+resource "opentelekomcloud_vpc_eip_v1" "eip_1" {
+ publicip {
+ type = "5_bgp"
+ }
+ bandwidth {
+ name = "%[1]s-1"
+ size = 8
+ share_type = "PER"
+ charge_mode = "traffic"
+ }
+}
+
+resource "opentelekomcloud_vpc_eip_v1" "eip_2" {
+ publicip {
+ type = "5_bgp"
+ }
+ bandwidth {
+ name = "%[1]s-2"
+ size = 9
+ share_type = "PER"
+ charge_mode = "traffic"
+ }
+}
+`, name)
+}
+
+func testEvpnGateway_basic(name string) string {
+ return fmt.Sprintf(`
+%s
+
+resource "opentelekomcloud_enterprise_vpn_gateway_v5" "gw_1" {
+ name = "%s"
+ vpc_id = opentelekomcloud_vpc_v1.vpc.id
+ local_subnets = [opentelekomcloud_vpc_subnet_v1.subnet.cidr]
+ connect_subnet = opentelekomcloud_vpc_subnet_v1.subnet.id
+
+ availability_zones = [
+ "eu-de-01",
+ "eu-de-02"
+ ]
+
+ eip1 {
+ id = opentelekomcloud_vpc_eip_v1.eip_1.id
+ }
+
+ eip2 {
+ id = opentelekomcloud_vpc_eip_v1.eip_2.id
+ }
+
+ tags = {
+ key = "val"
+ foo = "bar"
+ }
+}
+`, testEvpnGateway_base(name), name)
+}
+
+func testEvpnGateway_update(name string) string {
+ return fmt.Sprintf(`
+%s
+
+resource "opentelekomcloud_enterprise_vpn_gateway_v5" "gw_1" {
+ name = "%s"
+ vpc_id = opentelekomcloud_vpc_v1.vpc.id
+ local_subnets = [
+ opentelekomcloud_vpc_subnet_v1.subnet.cidr,
+ "192.168.2.0/24"
+ ]
+ connect_subnet = opentelekomcloud_vpc_subnet_v1.subnet.id
+
+ availability_zones = [
+ "eu-de-01",
+ "eu-de-02"
+ ]
+
+ eip1 {
+ id = opentelekomcloud_vpc_eip_v1.eip_1.id
+ }
+
+ eip2 {
+ id = opentelekomcloud_vpc_eip_v1.eip_2.id
+ }
+
+ tags = {
+ key = "val"
+ foo = "bar-update"
+ }
+}
+`, testEvpnGateway_base(name), name)
+}
+
+func testEvpnGateway_activeStandbyHAMode(name string) string {
+ return fmt.Sprintf(`
+resource "opentelekomcloud_vpc_v1" "vpc" {
+ name = "%[1]s"
+ cidr = "192.168.0.0/16"
+}
+
+resource "opentelekomcloud_vpc_subnet_v1" "subnet" {
+ name = "%[1]s"
+ vpc_id = opentelekomcloud_vpc_v1.vpc.id
+ cidr = "192.168.0.0/24"
+ gateway_ip = "192.168.0.1"
+}
+
+resource "opentelekomcloud_enterprise_vpn_gateway_v5" "gw_1" {
+ name = "%[1]s"
+ ha_mode = "active-standby"
+ vpc_id = opentelekomcloud_vpc_v1.vpc.id
+ local_subnets = [opentelekomcloud_vpc_subnet_v1.subnet.cidr]
+ connect_subnet = opentelekomcloud_vpc_subnet_v1.subnet.id
+
+ availability_zones = [
+ "eu-de-01",
+ "eu-de-02"
+ ]
+
+ eip1 {
+ bandwidth_name = "evpn-gw-bw-1"
+ type = "5_bgp"
+ bandwidth_size = 5
+ charge_mode = "traffic"
+ }
+
+ eip2 {
+ bandwidth_name = "evpn-gw-bw-2"
+ type = "5_bgp"
+ bandwidth_size = 5
+ charge_mode = "traffic"
+ }
+}
+`, name)
+}
+
+func testEvpnGateway_withER(name string) string {
+ return fmt.Sprintf(`
+resource "opentelekomcloud_vpc_v1" "vpc_er" {
+ name = "%[1]s"
+ cidr = "172.16.0.0/16"
+}
+
+resource "opentelekomcloud_vpc_subnet_v1" "subnet_er" {
+ name = "%[1]s"
+ vpc_id = opentelekomcloud_vpc_v1.vpc_er.id
+ cidr = "172.16.0.0/24"
+ gateway_ip = "172.16.0.1"
+}
+
+resource "opentelekomcloud_er_instance_v3" "er_1" {
+ availability_zones = ["eu-de-01", "eu-de-02"]
+
+ name = "%[1]s"
+ asn = "65000"
+ description = "evpn test"
+}
+
+resource "opentelekomcloud_enterprise_vpn_gateway_v5" "gw_1" {
+ name = "%[1]s"
+ network_type = "private"
+ attachment_type = "er"
+ er_id = opentelekomcloud_er_instance_v3.er_1.id
+
+ availability_zones = [
+ "eu-de-01",
+ "eu-de-02"
+ ]
+
+ access_vpc_id = opentelekomcloud_vpc_v1.vpc_er.id
+ access_subnet_id = opentelekomcloud_vpc_subnet_v1.subnet_er.id
+
+ access_private_ip_1 = "172.16.0.99"
+ access_private_ip_2 = "172.16.0.100"
+}
+`, name)
+}
diff --git a/opentelekomcloud/common/cfg/config.go b/opentelekomcloud/common/cfg/config.go
index f87bb8094..eefe69512 100644
--- a/opentelekomcloud/common/cfg/config.go
+++ b/opentelekomcloud/common/cfg/config.go
@@ -1175,6 +1175,13 @@ func (c *Config) TmsV1Client() (*golangsdk.ServiceClient, error) {
return service, nil
}
+func (c *Config) EvpnV5Client(region string) (*golangsdk.ServiceClient, error) {
+ return openstack.NewEVPNServiceV3(c.HwClient, golangsdk.EndpointOpts{
+ Region: region,
+ Availability: c.getEndpointType(),
+ })
+}
+
func reconfigProjectName(src Config, projectName ProjectName) (*Config, error) {
config := &Config{}
if err := copier.Copy(config, &src); err != nil {
diff --git a/opentelekomcloud/provider.go b/opentelekomcloud/provider.go
index d478da3c9..6c0a30342 100644
--- a/opentelekomcloud/provider.go
+++ b/opentelekomcloud/provider.go
@@ -560,6 +560,7 @@ func Provider() *schema.Provider {
"opentelekomcloud_vpnaas_ike_policy_v2": vpn.ResourceVpnIKEPolicyV2(),
"opentelekomcloud_vpnaas_endpoint_group_v2": vpn.ResourceVpnEndpointGroupV2(),
"opentelekomcloud_vpnaas_site_connection_v2": vpn.ResourceVpnSiteConnectionV2(),
+ "opentelekomcloud_enterprise_vpn_gateway_v5": vpn.ResourceEnterpriseVpnGateway(),
"opentelekomcloud_waf_alarm_notification_v1": waf.ResourceWafAlarmNotificationV1(),
"opentelekomcloud_waf_certificate_v1": waf.ResourceWafCertificateV1(),
"opentelekomcloud_waf_domain_v1": waf.ResourceWafDomainV1(),
diff --git a/opentelekomcloud/services/vpn/common.go b/opentelekomcloud/services/vpn/common.go
index 4ddec2b24..16aa94ee7 100644
--- a/opentelekomcloud/services/vpn/common.go
+++ b/opentelekomcloud/services/vpn/common.go
@@ -2,5 +2,7 @@ package vpn
const (
errCreationV2Client = "error creating OpenTelekomCloud NetworkingV2 client: %w"
+ errCreationV5Client = "error creating OpenTelekomCloud EvpnV5 client: %w"
keyClientV2 = "vpc-v2-client"
+ keyClientV5 = "vpc-v5-client"
)
diff --git a/opentelekomcloud/services/vpn/resource_opentelekomcloud_enterprise_vpn_gateway_v5.go b/opentelekomcloud/services/vpn/resource_opentelekomcloud_enterprise_vpn_gateway_v5.go
new file mode 100644
index 000000000..718c563ff
--- /dev/null
+++ b/opentelekomcloud/services/vpn/resource_opentelekomcloud_enterprise_vpn_gateway_v5.go
@@ -0,0 +1,526 @@
+package vpn
+
+import (
+ "context"
+ "fmt"
+ "log"
+ "time"
+
+ "github.com/hashicorp/go-multierror"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/diag"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
+ golangsdk "github.com/opentelekomcloud/gophertelekomcloud"
+ "github.com/opentelekomcloud/gophertelekomcloud/openstack/common/pointerto"
+ "github.com/opentelekomcloud/gophertelekomcloud/openstack/common/tags"
+ "github.com/opentelekomcloud/gophertelekomcloud/openstack/evpn/v5/gateway"
+ vpntags "github.com/opentelekomcloud/gophertelekomcloud/openstack/evpn/v5/tags"
+ "github.com/opentelekomcloud/terraform-provider-opentelekomcloud/opentelekomcloud/common"
+ "github.com/opentelekomcloud/terraform-provider-opentelekomcloud/opentelekomcloud/common/cfg"
+ "github.com/opentelekomcloud/terraform-provider-opentelekomcloud/opentelekomcloud/common/fmterr"
+)
+
+func ResourceEnterpriseVpnGateway() *schema.Resource {
+ return &schema.Resource{
+ CreateContext: resourceEvpnGatewayCreate,
+ UpdateContext: resourceEvpnGatewayUpdate,
+ ReadContext: resourceEvpnGatewayRead,
+ DeleteContext: resourceEvpnGatewayDelete,
+ Importer: &schema.ResourceImporter{
+ StateContext: schema.ImportStatePassthroughContext,
+ },
+ Timeouts: &schema.ResourceTimeout{
+ Create: schema.DefaultTimeout(10 * time.Minute),
+ Update: schema.DefaultTimeout(10 * time.Minute),
+ Delete: schema.DefaultTimeout(10 * time.Minute),
+ },
+
+ Schema: map[string]*schema.Schema{
+ "name": {
+ Type: schema.TypeString,
+ Required: true,
+ },
+ "availability_zones": {
+ Type: schema.TypeList,
+ Elem: &schema.Schema{Type: schema.TypeString},
+ Required: true,
+ ForceNew: true,
+ },
+ "flavor": {
+ Type: schema.TypeString,
+ Optional: true,
+ ForceNew: true,
+ Computed: true,
+ },
+ "attachment_type": {
+ Type: schema.TypeString,
+ Optional: true,
+ Default: "vpc",
+ ForceNew: true,
+ ValidateFunc: validation.StringInSlice([]string{
+ "vpc", "er",
+ }, false),
+ },
+ "network_type": {
+ Type: schema.TypeString,
+ Optional: true,
+ ForceNew: true,
+ Computed: true,
+ },
+ "vpc_id": {
+ Type: schema.TypeString,
+ Optional: true,
+ Computed: true,
+ ForceNew: true,
+ },
+ "local_subnets": {
+ Type: schema.TypeList,
+ Elem: &schema.Schema{Type: schema.TypeString},
+ Optional: true,
+ Computed: true,
+ },
+ "connect_subnet": {
+ Type: schema.TypeString,
+ Optional: true,
+ Computed: true,
+ ForceNew: true,
+ },
+ "er_id": {
+ Type: schema.TypeString,
+ Optional: true,
+ ForceNew: true,
+ Computed: true,
+ },
+ "ha_mode": {
+ Type: schema.TypeString,
+ Optional: true,
+ ForceNew: true,
+ Computed: true,
+ ValidateFunc: validation.StringInSlice([]string{"active-active", "active-standby"}, false),
+ },
+ "eip1": {
+ Type: schema.TypeList,
+ MaxItems: 1,
+ Elem: GatewayEipSchema(),
+ Optional: true,
+ Computed: true,
+ RequiredWith: []string{"eip2"},
+ },
+ "eip2": {
+ Type: schema.TypeList,
+ MaxItems: 1,
+ Elem: GatewayEipSchema(),
+ Optional: true,
+ Computed: true,
+ RequiredWith: []string{"eip1"},
+ },
+ "access_vpc_id": {
+ Type: schema.TypeString,
+ Optional: true,
+ ForceNew: true,
+ Computed: true,
+ },
+ "access_subnet_id": {
+ Type: schema.TypeString,
+ Optional: true,
+ ForceNew: true,
+ Computed: true,
+ },
+ "asn": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Default: 64512,
+ ForceNew: true,
+ },
+ "access_private_ip_1": {
+ Type: schema.TypeString,
+ Optional: true,
+ Computed: true,
+ ForceNew: true,
+ RequiredWith: []string{"access_private_ip_2"},
+ },
+ "access_private_ip_2": {
+ Type: schema.TypeString,
+ Optional: true,
+ Computed: true,
+ ForceNew: true,
+ RequiredWith: []string{"access_private_ip_1"},
+ },
+ "tags": common.TagsSchema(),
+ "status": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "created_at": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "updated_at": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "er_attachment_id": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "used_connection_group": {
+ Type: schema.TypeInt,
+ Computed: true,
+ },
+ "used_connection_number": {
+ Type: schema.TypeInt,
+ Computed: true,
+ },
+ "region": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ },
+ }
+}
+
+func GatewayEipSchema() *schema.Resource {
+ sc := schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "id": {
+ Type: schema.TypeString,
+ Optional: true,
+ Computed: true,
+ ForceNew: true,
+ },
+ "type": {
+ Type: schema.TypeString,
+ Optional: true,
+ Computed: true,
+ ForceNew: true,
+ },
+ "bandwidth_name": {
+ Type: schema.TypeString,
+ Optional: true,
+ Computed: true,
+ ForceNew: true,
+ },
+ "bandwidth_size": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Computed: true,
+ ForceNew: true,
+ },
+ "charge_mode": {
+ Type: schema.TypeString,
+ Optional: true,
+ Computed: true,
+ ForceNew: true,
+ ValidateFunc: validation.StringInSlice([]string{
+ "bandwidth", "traffic",
+ }, false),
+ },
+
+ "bandwidth_id": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "ip_address": {
+ Type: schema.TypeString,
+ Computed: true,
+ },
+ "ip_version": {
+ Type: schema.TypeInt,
+ Computed: true,
+ },
+ },
+ }
+ return &sc
+}
+
+func resourceEvpnGatewayCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ config := meta.(*cfg.Config)
+ client, err := common.ClientFromCtx(ctx, keyClientV5, func() (*golangsdk.ServiceClient, error) {
+ return config.EvpnV5Client(config.GetRegion(d))
+ })
+ if err != nil {
+ return fmterr.Errorf(errCreationV5Client, err)
+ }
+
+ var zones []string
+ azRaw := d.Get("availability_zones").([]interface{})
+ for _, az := range azRaw {
+ zones = append(zones, az.(string))
+ }
+ gatewayTags := d.Get("tags").(map[string]interface{})
+ var tagSlice []tags.ResourceTag
+ for k, v := range gatewayTags {
+ tagSlice = append(tagSlice, tags.ResourceTag{Key: k, Value: v.(string)})
+ }
+ createOpts := gateway.CreateOpts{
+ Name: d.Get("name").(string),
+ NetworkType: d.Get("network_type").(string),
+ AttachmentType: d.Get("attachment_type").(string),
+ ErId: d.Get("er_id").(string),
+ VpcId: d.Get("vpc_id").(string),
+ LocalSubnets: buildLocalSubnets(d),
+ ConnectSubnet: d.Get("connect_subnet").(string),
+ BgpAsn: pointerto.Int(d.Get("asn").(int)),
+ Flavor: d.Get("flavor").(string),
+ AvailabilityZoneIds: zones,
+ Eip1: buildCreateEvpnGatewayEIP(d, "eip1"),
+ Eip2: buildCreateEvpnGatewayEIP(d, "eip2"),
+ AccessVpcId: d.Get("access_vpc_id").(string),
+ AccessSubnetId: d.Get("access_subnet_id").(string),
+ HaMode: d.Get("ha_mode").(string),
+ AccessPrivateIp1: d.Get("access_private_ip_1").(string),
+ AccessPrivateIp2: d.Get("access_private_ip_2").(string),
+ Tags: tagSlice,
+ }
+
+ n, err := gateway.Create(client, createOpts)
+ if err != nil {
+ return fmterr.Errorf("error creating OpenTelekomCloud EVPN gateway: %w", err)
+ }
+
+ d.SetId(n.ID)
+ stateConf := &resource.StateChangeConf{
+ Pending: []string{"CREATING"},
+ Target: []string{"ACTIVE"},
+ Refresh: waitForGatewayActive(client, n.ID),
+ Timeout: d.Timeout(schema.TimeoutCreate),
+ Delay: 10 * time.Second,
+ PollInterval: 5 * time.Second,
+ MinTimeout: 3 * time.Second,
+ }
+
+ _, err = stateConf.WaitForStateContext(ctx)
+ if err != nil {
+ return fmterr.Errorf("error waiting for OpenTelekomCloud EVPN gateway (%s) to become ACTIVE: %w", n.ID, err)
+ }
+ clientCtx := common.CtxWithClient(ctx, client, keyClientV5)
+ return resourceEvpnGatewayRead(clientCtx, d, meta)
+}
+
+func buildLocalSubnets(d *schema.ResourceData) []string {
+ var localSubnets []string
+ subRaw := d.Get("local_subnets").([]interface{})
+ for _, s := range subRaw {
+ localSubnets = append(localSubnets, s.(string))
+ }
+ return localSubnets
+}
+
+func buildCreateEvpnGatewayEIP(d *schema.ResourceData, param string) *gateway.Eip {
+ if rawArray, ok := d.Get(param).([]interface{}); ok {
+ if len(rawArray) == 0 {
+ return nil
+ }
+
+ raw, ok := rawArray[0].(map[string]interface{})
+ if !ok {
+ return nil
+ }
+
+ eip := &gateway.Eip{
+ ID: raw["id"].(string),
+ Type: raw["type"].(string),
+ ChargeMode: raw["charge_mode"].(string),
+ BandwidthSize: raw["bandwidth_size"].(int),
+ BandwidthName: raw["bandwidth_name"].(string),
+ }
+ return eip
+ }
+ return nil
+}
+
+func resourceEvpnGatewayRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ config := meta.(*cfg.Config)
+ client, err := common.ClientFromCtx(ctx, keyClientV5, func() (*golangsdk.ServiceClient, error) {
+ return config.EvpnV5Client(config.GetRegion(d))
+ })
+ if err != nil {
+ return fmterr.Errorf(errCreationV5Client, err)
+ }
+ gw, err := gateway.Get(client, d.Id())
+ if err != nil {
+ return diag.Errorf("error retrieving OpenTelekomCloud EVPN gateway (%s): %s", d.Id(), err)
+ }
+
+ tagsMap := make(map[string]string)
+ for _, tag := range gw.Tags {
+ tagsMap[tag.Key] = tag.Value
+ }
+
+ mErr := multierror.Append(
+ nil,
+ d.Set("region", config.GetRegion(d)),
+ d.Set("attachment_type", gw.AttachmentType),
+ d.Set("availability_zones", gw.AvailabilityZoneIds),
+ d.Set("asn", gw.BgpAsn),
+ d.Set("connect_subnet", gw.ConnectSubnet),
+ d.Set("created_at", gw.CreatedAt),
+ d.Set("flavor", gw.Flavor),
+ d.Set("local_subnets", gw.LocalSubnets),
+ d.Set("ha_mode", gw.HaMode),
+ d.Set("eip1", flattenEvpGatewayResponseEip(gw.Eip1)),
+ d.Set("name", gw.Name),
+ d.Set("eip2", flattenEvpGatewayResponseEip(gw.Eip2)),
+ d.Set("status", gw.Status),
+ d.Set("updated_at", gw.UpdatedAt),
+ d.Set("used_connection_group", gw.UsedConnectionGroup),
+ d.Set("used_connection_number", gw.UsedConnectionNumber),
+ d.Set("vpc_id", gw.VpcId),
+ d.Set("access_vpc_id", gw.AccessVpcId),
+ d.Set("access_subnet_id", gw.AccessSubnetId),
+ d.Set("er_id", gw.ErId),
+ d.Set("network_type", gw.NetworkType),
+ d.Set("access_private_ip_1", gw.AccessPrivateIp1),
+ d.Set("access_private_ip_2", gw.AccessPrivateIp2),
+ d.Set("tags", tagsMap),
+ )
+
+ return diag.FromErr(mErr.ErrorOrNil())
+}
+
+func flattenEvpGatewayResponseEip(resp gateway.EipResp) []interface{} {
+ rst := []interface{}{
+ map[string]interface{}{
+ "bandwidth_id": resp.BandwidthId,
+ "bandwidth_name": resp.BandwidthName,
+ "bandwidth_size": resp.BandwidthSize,
+ "charge_mode": resp.ChargeMode,
+ "id": resp.ID,
+ "ip_address": resp.IpAddress,
+ "ip_version": resp.IpVersion,
+ "type": resp.Type,
+ },
+ }
+ return rst
+}
+
+func resourceEvpnGatewayUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ config := meta.(*cfg.Config)
+ client, err := common.ClientFromCtx(ctx, keyClientV5, func() (*golangsdk.ServiceClient, error) {
+ return config.EvpnV5Client(config.GetRegion(d))
+ })
+ if err != nil {
+ return fmterr.Errorf(errCreationV5Client, err)
+ }
+
+ updateChanges := []string{
+ "name",
+ "local_subnets",
+ "eip1",
+ "eip2",
+ }
+
+ if d.HasChanges(updateChanges...) {
+ opts := gateway.UpdateOpts{
+ GatewayID: d.Id(),
+ Name: d.Get("name").(string),
+ LocalSubnets: buildLocalSubnets(d),
+ Eip1: buildCreateEvpnGatewayEIP(d, "eip1"),
+ Eip2: buildCreateEvpnGatewayEIP(d, "eip2"),
+ }
+ _, err = gateway.Update(client, opts)
+ if err != nil {
+ return diag.Errorf("error updating OpenTelekomCloud EVPN gateway: %s", err)
+ }
+ }
+
+ // update tags
+ if d.HasChange("tags") {
+ if err = updateTags(client, d, "vpn-gateway", d.Id()); err != nil {
+ return diag.Errorf("error updating tags of OpenTelekomCloud EVPN gateway (%s): %s", d.Id(), err)
+ }
+ }
+ clientCtx := common.CtxWithClient(ctx, client, keyClientV5)
+ return resourceEvpnGatewayRead(clientCtx, d, meta)
+}
+
+func updateTags(client *golangsdk.ServiceClient, d *schema.ResourceData, resourceType, id string) error {
+ if d.HasChange("tags") {
+ oldMapRaw, newMapRaw := d.GetChange("tags")
+ oldMap := oldMapRaw.(map[string]interface{})
+ newMap := newMapRaw.(map[string]interface{})
+
+ // remove old tags
+ if len(oldMap) > 0 {
+ tagList := common.ExpandResourceTags(oldMap)
+ err := vpntags.Delete(client, resourceType, id, vpntags.TagsOpts{Tags: tagList})
+ if err != nil {
+ return err
+ }
+ }
+
+ // set new tags
+ if len(newMap) > 0 {
+ tagList := common.ExpandResourceTags(newMap)
+ err := vpntags.Create(client, resourceType, id, vpntags.TagsOpts{Tags: tagList})
+ if err != nil {
+ return err
+ }
+ }
+ }
+
+ return nil
+}
+
+func resourceEvpnGatewayDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ config := meta.(*cfg.Config)
+ client, err := common.ClientFromCtx(ctx, keyClientV5, func() (*golangsdk.ServiceClient, error) {
+ return config.EvpnV5Client(config.GetRegion(d))
+ })
+ if err != nil {
+ return fmterr.Errorf(errCreationV5Client, err)
+ }
+
+ err = gateway.Delete(client, d.Id())
+ if err != nil {
+ return common.CheckDeletedDiag(d, err, "error deleting OpenTelekomCloud EVPN gateway")
+ }
+
+ stateConf := &resource.StateChangeConf{
+ Pending: []string{"DELETING"},
+ Target: []string{"DELETED"},
+ Refresh: waitForGatewayDeletion(client, d.Id()),
+ Timeout: d.Timeout(schema.TimeoutCreate),
+ Delay: 10 * time.Second,
+ }
+ _, err = stateConf.WaitForStateContext(ctx)
+ if err != nil {
+ return diag.Errorf("error waiting for deleting OpenTelekomCloud EVPN gateway (%s) to complete: %s", d.Id(), err)
+ }
+ return nil
+}
+
+func waitForGatewayActive(client *golangsdk.ServiceClient, id string) resource.StateRefreshFunc {
+ return func() (interface{}, string, error) {
+ n, err := gateway.Get(client, id)
+ if err != nil {
+ return nil, "", err
+ }
+ if n.Status == "ACTIVE" {
+ return n, "ACTIVE", nil
+ }
+ return n, "CREATING", nil
+ }
+}
+
+func waitForGatewayDeletion(client *golangsdk.ServiceClient, id string) resource.StateRefreshFunc {
+ return func() (interface{}, string, error) {
+ r, err := gateway.Get(client, id)
+ if err != nil {
+ if _, ok := err.(golangsdk.ErrDefault404); ok {
+ log.Printf("[DEBUG] The OpenTelekomCloud EVPN gateway has been deleted (ID:%s).", id)
+ return r, "DELETED", nil
+ }
+ return nil, "ERROR", err
+ }
+ switch r.Status {
+ case "ACTIVE", "PENDING_DELETE":
+ return r, "DELETING", nil
+ default:
+ err = fmt.Errorf("error deleting OpenTelekomCloud EVPN gateway[%s]. "+
+ "Unexpected status: %v", r.ID, r.Status)
+ return r, "ERROR", err
+ }
+ }
+}
diff --git a/releasenotes/notes/evpn-gateway-f67a7b2d43de7294.yaml b/releasenotes/notes/evpn-gateway-f67a7b2d43de7294.yaml
new file mode 100644
index 000000000..670afb873
--- /dev/null
+++ b/releasenotes/notes/evpn-gateway-f67a7b2d43de7294.yaml
@@ -0,0 +1,4 @@
+---
+features:
+ |
+ **[EVPN]** Add new resource ``resource/opentelekomcloud_enterprise_vpn_gateway_v5`` (`#2671 `_)