Repository: vmware/terraform-provider-nsxt
Pull request #1554: Implement nsxt_policy_edge_high_availability_profile resource
Author: login: ksamoray
Created: 2025-02-25T15:38:04Z | Merged: 2025-03-13T12:52:47Z



Diff:
diff --git a/nsxt/provider.go b/nsxt/provider.go
index 517639d48..b59c83282 100644
--- a/nsxt/provider.go
+++ b/nsxt/provider.go
@@ -527,19 +527,20 @@ func Provider() *schema.Provider {
 			"nsxt_policy_shared_resource":                              resourceNsxtPolicySharedResource(),
 			"nsxt_policy_gateway_connection":                           resourceNsxtPolicyGatewayConnection(),
 			"nsxt_policy_distributed_vlan_connection":                  resourceNsxtPolicyDistributedVlanConnection(),
-			"nsxt_vpc":                                  resourceNsxtVpc(),
-			"nsxt_vpc_attachment":                       resourceNsxtVpcAttachment(),
-			"nsxt_vpc_nat_rule":                         resourceNsxtPolicyVpcNatRule(),
-			"nsxt_policy_transit_gateway_attachment":    resourceNsxtPolicyTransitGatewayAttachment(),
-			"nsxt_vpc_external_address":                 resourceNsxtVpcExternalAddress(),
-			"nsxt_vpc_ip_address_allocation":            resourceNsxtVpcIpAddressAllocation(),
-			"nsxt_vpc_subnet":                           resourceNsxtVpcSubnet(),
-			"nsxt_policy_transit_gateway_nat_rule":      resourceNsxtPolicyTransitGatewayNatRule(),
-			"nsxt_vpc_static_route":                     resourceNsxtVpcStaticRoutes(),
-			"nsxt_policy_project_ip_address_allocation": resourceNsxtPolicyProjectIpAddressAllocation(),
-			"nsxt_vpc_dhcp_v4_static_binding":           resourceNsxtVpcSubnetDhcpV4StaticBindingConfig(),
-			"nsxt_policy_l7_access_profile":             resourceNsxtPolicyL7AccessProfile(),
-			"nsxt_policy_edge_transport_node":           resourceNsxtPolicyEdgeTransportNode(),
+			"nsxt_vpc":                                   resourceNsxtVpc(),
+			"nsxt_vpc_attachment":                        resourceNsxtVpcAttachment(),
+			"nsxt_vpc_nat_rule":                          resourceNsxtPolicyVpcNatRule(),
+			"nsxt_policy_transit_gateway_attachment":     resourceNsxtPolicyTransitGatewayAttachment(),
+			"nsxt_vpc_external_address":                  resourceNsxtVpcExternalAddress(),
+			"nsxt_vpc_ip_address_allocation":             resourceNsxtVpcIpAddressAllocation(),
+			"nsxt_vpc_subnet":                            resourceNsxtVpcSubnet(),
+			"nsxt_policy_transit_gateway_nat_rule":       resourceNsxtPolicyTransitGatewayNatRule(),
+			"nsxt_vpc_static_route":                      resourceNsxtVpcStaticRoutes(),
+			"nsxt_policy_project_ip_address_allocation":  resourceNsxtPolicyProjectIpAddressAllocation(),
+			"nsxt_vpc_dhcp_v4_static_binding":            resourceNsxtVpcSubnetDhcpV4StaticBindingConfig(),
+			"nsxt_policy_l7_access_profile":              resourceNsxtPolicyL7AccessProfile(),
+			"nsxt_policy_edge_transport_node":            resourceNsxtPolicyEdgeTransportNode(),
+			"nsxt_policy_edge_high_availability_profile": resourceNsxtPolicyEdgeHighAvailabilityProfile(),
 		},
 
 		ConfigureFunc: providerConfigure,
diff --git a/nsxt/resource_nsxt_policy_edge_high_availability_profile.go b/nsxt/resource_nsxt_policy_edge_high_availability_profile.go
new file mode 100644
index 000000000..085be6276
--- /dev/null
+++ b/nsxt/resource_nsxt_policy_edge_high_availability_profile.go
@@ -0,0 +1,302 @@
+/* Copyright © 2024 Broadcom, Inc. All Rights Reserved.
+   SPDX-License-Identifier: MPL-2.0 */
+
+package nsxt
+
+import (
+	"fmt"
+	"log"
+	"reflect"
+
+	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
+	"github.com/vmware/vsphere-automation-sdk-go/runtime/protocol/client"
+	"github.com/vmware/vsphere-automation-sdk-go/services/nsxt/infra"
+	"github.com/vmware/vsphere-automation-sdk-go/services/nsxt/infra/sites/enforcement_points"
+	"github.com/vmware/vsphere-automation-sdk-go/services/nsxt/model"
+
+	"github.com/vmware/terraform-provider-nsxt/nsxt/metadata"
+)
+
+var policyEdgeHighAvailabilityProfileSchema = map[string]*metadata.ExtendedSchema{
+	"nsx_id":       metadata.GetExtendedSchema(getNsxIDSchema()),
+	"path":         metadata.GetExtendedSchema(getPathSchema()),
+	"display_name": metadata.GetExtendedSchema(getDisplayNameSchema()),
+	"description":  metadata.GetExtendedSchema(getDescriptionSchema()),
+	"revision":     metadata.GetExtendedSchema(getRevisionSchema()),
+	"tag":          metadata.GetExtendedSchema(getTagsSchema()),
+	"site_path": {
+		Schema: schema.Schema{
+			Type:         schema.TypeString,
+			Description:  "Path to the site this Host Transport Node belongs to",
+			Optional:     true,
+			ForceNew:     true,
+			Default:      defaultInfraSitePath,
+			ValidateFunc: validatePolicyPath(),
+		},
+		Metadata: metadata.Metadata{
+			SchemaType: "string",
+			Skip:       true,
+		},
+	},
+	"enforcement_point": {
+		Schema: schema.Schema{
+			Type:        schema.TypeString,
+			Description: "ID of the enforcement point this Host Transport Node belongs to",
+			Optional:    true,
+			ForceNew:    true,
+			Default:     "default",
+		},
+		Metadata: metadata.Metadata{
+			SchemaType: "string",
+			Skip:       true,
+		},
+	},
+	"bfd_probe_interval": {
+		Schema: schema.Schema{
+			Type:         schema.TypeInt,
+			Optional:     true,
+			Default:      500,
+			ValidateFunc: validation.IntBetween(50, 60000),
+		},
+		Metadata: metadata.Metadata{
+			SchemaType:   "int",
+			SdkFieldName: "BfdProbeInterval",
+		},
+	},
+	"bfd_allowed_hops": {
+		Schema: schema.Schema{
+			Type:         schema.TypeInt,
+			Optional:     true,
+			Default:      255,
+			ValidateFunc: validation.IntBetween(1, 255),
+		},
+		Metadata: metadata.Metadata{
+			SchemaType:   "int",
+			SdkFieldName: "BfdAllowedHops",
+		},
+	},
+	"bfd_declare_dead_multiple": {
+		Schema: schema.Schema{
+			Type:         schema.TypeInt,
+			Optional:     true,
+			Default:      3,
+			ValidateFunc: validation.IntBetween(2, 16),
+		},
+		Metadata: metadata.Metadata{
+			SchemaType:   "int",
+			SdkFieldName: "BfdDeclareDeadMultiple",
+		},
+	},
+	"standby_relocation_config": {
+		Schema: schema.Schema{
+			Type:     schema.TypeList,
+			MaxItems: 1,
+			Elem: &metadata.ExtendedResource{
+				Schema: map[string]*metadata.ExtendedSchema{
+					"standby_relocation_threshold": {
+						Schema: schema.Schema{
+							Type:     schema.TypeInt,
+							Optional: true,
+							Computed: true,
+						},
+						Metadata: metadata.Metadata{
+							SchemaType:   "int",
+							SdkFieldName: "StandbyRelocationThreshold",
+						},
+					},
+				},
+			},
+			Optional: true,
+			Computed: true,
+		},
+		Metadata: metadata.Metadata{
+			SchemaType:   "struct",
+			SdkFieldName: "StandbyRelocationConfig",
+			ReflectType:  reflect.TypeOf(model.StandbyRelocationConfig{}),
+		},
+	},
+}
+
+func resourceNsxtPolicyEdgeHighAvailabilityProfile() *schema.Resource {
+	return &schema.Resource{
+		Create: resourceNsxtPolicyEdgeHighAvailabilityProfileCreate,
+		Read:   resourceNsxtPolicyEdgeHighAvailabilityProfileRead,
+		Update: resourceNsxtPolicyEdgeHighAvailabilityProfileUpdate,
+		Delete: resourceNsxtPolicyEdgeHighAvailabilityProfileDelete,
+		Importer: &schema.ResourceImporter{
+			State: resourceNsxtPolicyEdgeHighAvailabilityProfileImporter,
+		},
+		Schema: metadata.GetSchemaFromExtendedSchema(policyEdgeHighAvailabilityProfileSchema),
+	}
+}
+
+func resourceNsxtPolicyEdgeHighAvailabilityProfileExists(siteID, epID, id string, connector client.Connector) (bool, error) {
+	// Check site existence first
+	siteClient := infra.NewSitesClient(connector)
+	_, err := siteClient.Get(siteID)
+	if err != nil {
+		msg := fmt.Sprintf("failed to read site %s", siteID)
+		return false, logAPIError(msg, err)
+	}
+
+	client := enforcement_points.NewEdgeClusterHighAvailabilityProfilesClient(connector)
+	_, err = client.Get(siteID, epID, id)
+	if err == nil {
+		return true, nil
+	}
+
+	if isNotFoundError(err) {
+		return false, nil
+	}
+
+	return false, logAPIError("Error retrieving resource", err)
+}
+
+func resourceNsxtPolicyEdgeHighAvailabilityProfileCreate(d *schema.ResourceData, m interface{}) error {
+	id := d.Get("nsx_id").(string)
+	if id == "" {
+		id = newUUID()
+	}
+	sitePath := d.Get("site_path").(string)
+	siteID := getResourceIDFromResourcePath(sitePath, "sites")
+	if siteID == "" {
+		return fmt.Errorf("error obtaining Site ID from site path %s", sitePath)
+	}
+	epID := d.Get("enforcement_point").(string)
+	if epID == "" {
+		epID = getPolicyEnforcementPoint(m)
+	}
+
+	connector := getPolicyConnector(m)
+	exists, err := resourceNsxtPolicyEdgeHighAvailabilityProfileExists(siteID, epID, id, connector)
+	if err != nil {
+		return err
+	}
+	if exists {
+		return fmt.Errorf("resource with ID %s already exists", id)
+	}
+
+	displayName := d.Get("display_name").(string)
+	description := d.Get("description").(string)
+	tags := getPolicyTagsFromSchema(d)
+
+	obj := model.PolicyEdgeHighAvailabilityProfile{
+		DisplayName: &displayName,
+		Description: &description,
+		Tags:        tags,
+	}
+
+	elem := reflect.ValueOf(&obj).Elem()
+	if err := metadata.SchemaToStruct(elem, d, policyEdgeHighAvailabilityProfileSchema, "", nil); err != nil {
+		return err
+	}
+
+	log.Printf("[INFO] Creating PolicyEdgeHighAvailabilityProfile with ID %s", id)
+
+	client := enforcement_points.NewEdgeClusterHighAvailabilityProfilesClient(connector)
+	err = client.Patch(siteID, epID, id, obj)
+	if err != nil {
+		return handleCreateError("PolicyEdgeHighAvailabilityProfile", id, err)
+	}
+	d.SetId(id)
+	d.Set("nsx_id", id)
+
+	return resourceNsxtPolicyEdgeHighAvailabilityProfileRead(d, m)
+}
+
+func resourceNsxtPolicyEdgeHighAvailabilityProfileRead(d *schema.ResourceData, m interface{}) error {
+	id, siteID, epID, err := policyIDSiteEPTuple(d, m)
+	if err != nil {
+		return err
+	}
+
+	connector := getPolicyConnector(m)
+	client := enforcement_points.NewEdgeClusterHighAvailabilityProfilesClient(connector)
+	obj, err := client.Get(siteID, epID, id)
+	if err != nil {
+		return handleReadError(d, "PolicyEdgeHighAvailabilityProfile", id, err)
+	}
+
+	setPolicyTagsInSchema(d, obj.Tags)
+	d.Set("nsx_id", id)
+	d.Set("display_name", obj.DisplayName)
+	d.Set("description", obj.Description)
+	d.Set("revision", obj.Revision)
+	d.Set("path", obj.Path)
+
+	elem := reflect.ValueOf(&obj).Elem()
+	return metadata.StructToSchema(elem, d, policyEdgeHighAvailabilityProfileSchema, "", nil)
+}
+
+func resourceNsxtPolicyEdgeHighAvailabilityProfileUpdate(d *schema.ResourceData, m interface{}) error {
+	id, siteID, epID, err := policyIDSiteEPTuple(d, m)
+	if err != nil {
+		return err
+	}
+
+	description := d.Get("description").(string)
+	displayName := d.Get("display_name").(string)
+	tags := getPolicyTagsFromSchema(d)
+
+	revision := int64(d.Get("revision").(int))
+
+	obj := model.PolicyEdgeHighAvailabilityProfile{
+		DisplayName: &displayName,
+		Description: &description,
+		Tags:        tags,
+		Revision:    &revision,
+	}
+
+	elem := reflect.ValueOf(&obj).Elem()
+	if err := metadata.SchemaToStruct(elem, d, policyEdgeHighAvailabilityProfileSchema, "", nil); err != nil {
+		return err
+	}
+
+	connector := getPolicyConnector(m)
+	client := enforcement_points.NewEdgeClusterHighAvailabilityProfilesClient(connector)
+	_, err = client.Update(siteID, epID, id, obj)
+	if err != nil {
+		return handleUpdateError("PolicyEdgeHighAvailabilityProfile", id, err)
+	}
+
+	return resourceNsxtPolicyEdgeHighAvailabilityProfileRead(d, m)
+}
+
+func resourceNsxtPolicyEdgeHighAvailabilityProfileDelete(d *schema.ResourceData, m interface{}) error {
+	id, siteID, epID, err := policyIDSiteEPTuple(d, m)
+	if err != nil {
+		return err
+	}
+
+	connector := getPolicyConnector(m)
+	client := enforcement_points.NewEdgeClusterHighAvailabilityProfilesClient(connector)
+	err = client.Delete(siteID, epID, id)
+
+	if err != nil {
+		return handleDeleteError("PolicyEdgeHighAvailabilityProfile", id, err)
+	}
+
+	return nil
+}
+
+func resourceNsxtPolicyEdgeHighAvailabilityProfileImporter(d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) {
+	importID := d.Id()
+	rd, err := nsxtPolicyPathResourceImporterHelper(d, m)
+	if err != nil {
+		return rd, err
+	}
+
+	epID, err := getParameterFromPolicyPath("/enforcement-points/", "/edge-cluster-high-availability-profiles/", importID)
+	if err != nil {
+		return nil, err
+	}
+	d.Set("enforcement_point", epID)
+	sitePath, err := getSitePathFromChildResourcePath(importID)
+	if err != nil {
+		return rd, err
+	}
+	d.Set("site_path", sitePath)
+
+	return rd, nil
+}
diff --git a/nsxt/resource_nsxt_policy_edge_high_availability_profile_test.go b/nsxt/resource_nsxt_policy_edge_high_availability_profile_test.go
new file mode 100644
index 000000000..cf97776b5
--- /dev/null
+++ b/nsxt/resource_nsxt_policy_edge_high_availability_profile_test.go
@@ -0,0 +1,222 @@
+/* Copyright © 2024 Broadcom, Inc. All Rights Reserved.
+   SPDX-License-Identifier: MPL-2.0 */
+
+package nsxt
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
+	"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
+)
+
+var accTestPolicyEdgeHighAvailabilityProfileCreateAttributes = map[string]string{
+	"display_name":                 getAccTestResourceName(),
+	"description":                  "terraform created",
+	"bfd_probe_interval":           "20000",
+	"bfd_allowed_hops":             "140",
+	"bfd_declare_dead_multiple":    "7",
+	"standby_relocation_threshold": "2000",
+}
+
+var accTestPolicyEdgeHighAvailabilityProfileUpdateAttributes = map[string]string{
+	"display_name":                 getAccTestResourceName(),
+	"description":                  "terraform updated",
+	"bfd_probe_interval":           "25000",
+	"bfd_allowed_hops":             "122",
+	"bfd_declare_dead_multiple":    "9",
+	"standby_relocation_threshold": "5000",
+}
+
+func TestAccResourceNsxtPolicyEdgeHighAvailabilityProfile_basic(t *testing.T) {
+	testResourceName := "nsxt_policy_edge_high_availability_profile.test"
+
+	resource.ParallelTest(t, resource.TestCase{
+		PreCheck: func() {
+			testAccPreCheck(t)
+			testAccNSXVersion(t, "9.0.0")
+		},
+		Providers: testAccProviders,
+		CheckDestroy: func(state *terraform.State) error {
+			return testAccNsxtPolicyEdgeHighAvailabilityProfileCheckDestroy(state, accTestPolicyEdgeHighAvailabilityProfileUpdateAttributes["display_name"])
+		},
+		Steps: []resource.TestStep{
+			{
+				Config: testAccNsxtPolicyEdgeHighAvailabilityProfileTemplate(true),
+				Check: resource.ComposeTestCheckFunc(
+					testAccNsxtPolicyEdgeHighAvailabilityProfileExists(accTestPolicyEdgeHighAvailabilityProfileCreateAttributes["display_name"], testResourceName),
+					resource.TestCheckResourceAttr(testResourceName, "display_name", accTestPolicyEdgeHighAvailabilityProfileCreateAttributes["display_name"]),
+					resource.TestCheckResourceAttr(testResourceName, "description", accTestPolicyEdgeHighAvailabilityProfileCreateAttributes["description"]),
+					resource.TestCheckResourceAttr(testResourceName, "bfd_probe_interval", accTestPolicyEdgeHighAvailabilityProfileCreateAttributes["bfd_probe_interval"]),
+					resource.TestCheckResourceAttr(testResourceName, "bfd_allowed_hops", accTestPolicyEdgeHighAvailabilityProfileCreateAttributes["bfd_allowed_hops"]),
+					resource.TestCheckResourceAttr(testResourceName, "bfd_declare_dead_multiple", accTestPolicyEdgeHighAvailabilityProfileCreateAttributes["bfd_declare_dead_multiple"]),
+					resource.TestCheckResourceAttr(testResourceName, "standby_relocation_config.#", "1"),
+					resource.TestCheckResourceAttr(testResourceName, "standby_relocation_config.0.standby_relocation_threshold", accTestPolicyEdgeHighAvailabilityProfileCreateAttributes["standby_relocation_threshold"]),
+
+					resource.TestCheckResourceAttrSet(testResourceName, "nsx_id"),
+					resource.TestCheckResourceAttrSet(testResourceName, "path"),
+					resource.TestCheckResourceAttrSet(testResourceName, "revision"),
+					resource.TestCheckResourceAttr(testResourceName, "tag.#", "1"),
+				),
+			},
+			{
+				Config: testAccNsxtPolicyEdgeHighAvailabilityProfileTemplate(false),
+				Check: resource.ComposeTestCheckFunc(
+					testAccNsxtPolicyEdgeHighAvailabilityProfileExists(accTestPolicyEdgeHighAvailabilityProfileUpdateAttributes["display_name"], testResourceName),
+					resource.TestCheckResourceAttr(testResourceName, "display_name", accTestPolicyEdgeHighAvailabilityProfileUpdateAttributes["display_name"]),
+					resource.TestCheckResourceAttr(testResourceName, "description", accTestPolicyEdgeHighAvailabilityProfileUpdateAttributes["description"]),
+					resource.TestCheckResourceAttr(testResourceName, "bfd_probe_interval", accTestPolicyEdgeHighAvailabilityProfileUpdateAttributes["bfd_probe_interval"]),
+					resource.TestCheckResourceAttr(testResourceName, "bfd_allowed_hops", accTestPolicyEdgeHighAvailabilityProfileUpdateAttributes["bfd_allowed_hops"]),
+					resource.TestCheckResourceAttr(testResourceName, "bfd_declare_dead_multiple", accTestPolicyEdgeHighAvailabilityProfileUpdateAttributes["bfd_declare_dead_multiple"]),
+					resource.TestCheckResourceAttr(testResourceName, "standby_relocation_config.#", "1"),
+					resource.TestCheckResourceAttr(testResourceName, "standby_relocation_config.0.standby_relocation_threshold", accTestPolicyEdgeHighAvailabilityProfileUpdateAttributes["standby_relocation_threshold"]),
+
+					resource.TestCheckResourceAttrSet(testResourceName, "nsx_id"),
+					resource.TestCheckResourceAttrSet(testResourceName, "path"),
+					resource.TestCheckResourceAttrSet(testResourceName, "revision"),
+					resource.TestCheckResourceAttr(testResourceName, "tag.#", "1"),
+				),
+			},
+			{
+				Config: testAccNsxtPolicyEdgeHighAvailabilityProfileMinimalistic(),
+				Check: resource.ComposeTestCheckFunc(
+					testAccNsxtPolicyEdgeHighAvailabilityProfileExists(accTestPolicyEdgeHighAvailabilityProfileCreateAttributes["display_name"], testResourceName),
+					resource.TestCheckResourceAttr(testResourceName, "description", ""),
+					resource.TestCheckResourceAttrSet(testResourceName, "nsx_id"),
+					resource.TestCheckResourceAttrSet(testResourceName, "path"),
+					resource.TestCheckResourceAttrSet(testResourceName, "revision"),
+					resource.TestCheckResourceAttr(testResourceName, "tag.#", "0"),
+				),
+			},
+		},
+	})
+}
+
+func TestAccResourceNsxtPolicyEdgeHighAvailabilityProfile_importBasic(t *testing.T) {
+	name := getAccTestResourceName()
+	testResourceName := "nsxt_policy_edge_high_availability_profile.test"
+
+	resource.ParallelTest(t, resource.TestCase{
+		PreCheck: func() {
+			testAccPreCheck(t)
+			testAccNSXVersion(t, "9.0.0")
+		},
+		Providers: testAccProviders,
+		CheckDestroy: func(state *terraform.State) error {
+			return testAccNsxtPolicyEdgeHighAvailabilityProfileCheckDestroy(state, name)
+		},
+		Steps: []resource.TestStep{
+			{
+				Config: testAccNsxtPolicyEdgeHighAvailabilityProfileMinimalistic(),
+			},
+			{
+				ResourceName:      testResourceName,
+				ImportState:       true,
+				ImportStateVerify: true,
+				ImportStateIdFunc: testAccNsxtPolicyEdgeHighAvailabilityProfileImporterGetID,
+			},
+		},
+	})
+}
+
+func testAccNsxtPolicyEdgeHighAvailabilityProfileImporterGetID(s *terraform.State) (string, error) {
+	rs, ok := s.RootModule().Resources["nsxt_policy_edge_high_availability_profile.test"]
+	if !ok {
+		return "", fmt.Errorf("PolicyEdgeHighAvailabilityProfile resource %s not found in resources", "nsxt_policy_edge_high_availability_profile.test")
+	}
+	resourceID := rs.Primary.ID
+	if resourceID == "" {
+		return "", fmt.Errorf("PolicyEdgeHighAvailabilityProfile resource ID not set in resources ")
+	}
+	path := rs.Primary.Attributes["path"]
+	if path == "" {
+		return "", fmt.Errorf("PolicyEdgeHighAvailabilityProfile path not set in resources ")
+	}
+	return path, nil
+}
+
+func testAccNsxtPolicyEdgeHighAvailabilityProfileExists(displayName string, resourceName string) resource.TestCheckFunc {
+	return func(state *terraform.State) error {
+
+		connector := getPolicyConnector(testAccProvider.Meta().(nsxtClients))
+
+		rs, ok := state.RootModule().Resources[resourceName]
+		if !ok {
+			return fmt.Errorf("Policy PolicyEdgeHighAvailabilityProfile resource %s not found in resources", resourceName)
+		}
+
+		resourceID := rs.Primary.ID
+		if resourceID == "" {
+			return fmt.Errorf("Policy PolicyEdgeHighAvailabilityProfile resource ID not set in resources")
+		}
+		epID := rs.Primary.Attributes["enforcement_point"]
+		sitePath := rs.Primary.Attributes["site_path"]
+		siteID := getPolicyIDFromPath(sitePath)
+		exists, err := resourceNsxtPolicyEdgeHighAvailabilityProfileExists(siteID, epID, resourceID, connector)
+		if err != nil {
+			return err
+		}
+		if !exists {
+			return fmt.Errorf("Policy PolicyEdgeHighAvailabilityProfile %s does not exist", resourceID)
+		}
+
+		return nil
+	}
+}
+
+func testAccNsxtPolicyEdgeHighAvailabilityProfileCheckDestroy(state *terraform.State, displayName string) error {
+	connector := getPolicyConnector(testAccProvider.Meta().(nsxtClients))
+	for _, rs := range state.RootModule().Resources {
+
+		if rs.Type != "nsxt_policy_edge_high_availability_profile" {
+			continue
+		}
+
+		resourceID := rs.Primary.Attributes["id"]
+		epID := rs.Primary.Attributes["enforcement_point"]
+		sitePath := rs.Primary.Attributes["site_path"]
+		siteID := getPolicyIDFromPath(sitePath)
+		exists, err := resourceNsxtPolicyEdgeHighAvailabilityProfileExists(siteID, epID, resourceID, connector)
+		if err == nil {
+			return err
+		}
+
+		if exists {
+			return fmt.Errorf("Policy PolicyEdgeHighAvailabilityProfile %s still exists", displayName)
+		}
+	}
+	return nil
+}
+
+func testAccNsxtPolicyEdgeHighAvailabilityProfileTemplate(createFlow bool) string {
+	var attrMap map[string]string
+	if createFlow {
+		attrMap = accTestPolicyEdgeHighAvailabilityProfileCreateAttributes
+	} else {
+		attrMap = accTestPolicyEdgeHighAvailabilityProfileUpdateAttributes
+	}
+	return fmt.Sprintf(`
+resource "nsxt_policy_edge_high_availability_profile" "test" {
+  display_name = "%s"
+  description  = "%s"
+  bfd_probe_interval = %s
+  bfd_allowed_hops = %s
+  bfd_declare_dead_multiple = %s
+  standby_relocation_config {
+    standby_relocation_threshold = %s
+  }
+
+  tag {
+    scope = "scope1"
+    tag   = "tag1"
+  }
+}`, attrMap["display_name"], attrMap["description"], attrMap["bfd_probe_interval"], attrMap["bfd_allowed_hops"], attrMap["bfd_declare_dead_multiple"], attrMap["standby_relocation_threshold"])
+}
+
+func testAccNsxtPolicyEdgeHighAvailabilityProfileMinimalistic() string {
+	return fmt.Sprintf(`
+resource "nsxt_policy_edge_high_availability_profile" "test" {
+  display_name = "%s"
+}`, accTestPolicyEdgeHighAvailabilityProfileUpdateAttributes["display_name"])
+}
diff --git a/website/docs/r/policy_edge_high_availability_profile.html.markdown b/website/docs/r/policy_edge_high_availability_profile.html.markdown
new file mode 100644
index 000000000..c341c6f4a
--- /dev/null
+++ b/website/docs/r/policy_edge_high_availability_profile.html.markdown
@@ -0,0 +1,61 @@
+---
+subcategory: "Beta"
+layout: "nsxt"
+page_title: "NSXT: nsxt_policy_edge_high_availability_profile"
+description: A resource to configure a PolicyEdgeHighAvailabilityProfile.
+---
+
+# nsxt_policy_edge_high_availability_profile
+
+This resource provides a method for the management of a PolicyEdgeHighAvailabilityProfile.
+
+This resource is applicable to NSX Global Manager, NSX Policy Manager and VMC.
+
+## Example Usage
+
+```hcl
+resource "nsxt_policy_edge_high_availability_profile" "test" {
+  display_name              = "test"
+  description               = "Terraform provisioned PolicyEdgeHighAvailabilityProfile"
+  bfd_probe_interval        = 750
+  bfd_allowed_hops          = 140
+  bfd_declare_dead_multiple = 7
+
+}
+```
+
+## Argument Reference
+
+The following arguments are supported:
+
+* `display_name` - (Required) Display name of the resource.
+* `description` - (Optional) Description of the resource.
+* `tag` - (Optional) A list of scope + tag pairs to associate with this resource.
+* `nsx_id` - (Optional) The NSX ID of this resource. If set, this ID will be used to create the resource.
+* `site_path` - (Optional) The path of the site which the Edge Transport Node belongs to. `path` field of the existing `nsxt_policy_site` can be used here. Defaults to default site path.
+* `enforcement_point` - (Optional) The ID of enforcement point under given `site_path` to manage the Edge Transport Node. Defaults to default enforcement point.
+* `bfd_probe_interval` - (Optional) The time interval (in millisec) between probe packets for heartbeat purpose. Default: 500.
+* `bfd_allowed_hops` - (Optional) Value of BFD allowed hops. Default: 255.
+* `bfd_declare_dead_multiple` - (Optional) Number of times a packet is missed before BFD declares the neighbor down. Default: 3.
+* `standby_relocation_config` - (Optional) Stand by relocation flag.
+  * `standby_relocation_threshold` - (Required) Standby service context relocation wait time.
+
+## Attributes Reference
+
+In addition to arguments listed above, the following attributes are exported:
+
+* `id` - ID of the resource.
+* `revision` - Indicates current revision number of the object as seen by NSX-T API server. This attribute can be useful for debugging.
+* `path` - The NSX path of the policy resource.
+
+## Importing
+
+An existing object can be [imported][docs-import] into this resource, via the following command:
+
+[docs-import]: https://www.terraform.io/cli/import
+
+```
+terraform import nsxt_policy_edge_high_availability_profile.test PATH
+```
+
+The above command imports PolicyEdgeHighAvailabilityProfile named `test` with the NSX path `PATH`.


Reviews:
user: login: annakhm
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T01:21:55Z
user: login: annakhm
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-11T01:26:50Z
user: login: ksamoray
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T10:56:06Z
user: login: ksamoray
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T11:11:02Z
user: login: annakhm
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T21:53:29Z
user: login: annakhm
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T22:20:41Z
user: login: annakhm
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T22:21:22Z

Review comments:
diff_hunk: @@ -0,0 +1,61 @@
+---
+subcategory: "FIXME"
path: website/docs/r/policy_edge_high_availability_profile.html.markdown
user: login: annakhm
body: `Beta` or `Fabric`
created_at: 2025-03-11T01:21:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,298 @@
+/* Copyright © 2024 Broadcom, Inc. All Rights Reserved.
+   SPDX-License-Identifier: MPL-2.0 */
+
+package nsxt
+
+import (
+	"fmt"
+	"log"
+	"reflect"
+
+	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+	"github.com/vmware/vsphere-automation-sdk-go/runtime/protocol/client"
+	"github.com/vmware/vsphere-automation-sdk-go/services/nsxt/infra"
+	"github.com/vmware/vsphere-automation-sdk-go/services/nsxt/infra/sites/enforcement_points"
+	"github.com/vmware/vsphere-automation-sdk-go/services/nsxt/model"
+
+	"github.com/vmware/terraform-provider-nsxt/nsxt/metadata"
+)
+
+var policyEdgeHighAvailabilityProfileSchema = map[string]*metadata.ExtendedSchema{
+	"nsx_id":       metadata.GetExtendedSchema(getNsxIDSchema()),
+	"path":         metadata.GetExtendedSchema(getPathSchema()),
+	"display_name": metadata.GetExtendedSchema(getDisplayNameSchema()),
+	"description":  metadata.GetExtendedSchema(getDescriptionSchema()),
+	"revision":     metadata.GetExtendedSchema(getRevisionSchema()),
+	"tag":          metadata.GetExtendedSchema(getTagsSchema()),
+	"site_path": {
+		Schema: schema.Schema{
+			Type:         schema.TypeString,
+			Description:  "Path to the site this Host Transport Node belongs to",
+			Optional:     true,
+			ForceNew:     true,
+			Default:      defaultInfraSitePath,
+			ValidateFunc: validatePolicyPath(),
+		},
+		Metadata: metadata.Metadata{
+			SchemaType: "string",
+			Skip:       true,
+		},
+	},
+	"enforcement_point": {
+		Schema: schema.Schema{
+			Type:        schema.TypeString,
+			Description: "ID of the enforcement point this Host Transport Node belongs to",
+			Optional:    true,
+			ForceNew:    true,
+			Default:     "default",
+		},
+		Metadata: metadata.Metadata{
+			SchemaType: "string",
+			Skip:       true,
+		},
+	},
+	"bfd_probe_interval": {
+		Schema: schema.Schema{
+			Type:     schema.TypeInt,
+			Optional: true,
+			Default:  500,
+		},
path: nsxt/resource_nsxt_policy_edge_high_availability_profile.go
user: login: annakhm
body: MP resource has more refined validations - f.e, for this attribute we have `validation.IntBetween(50, 60000)`. Policy spec also specifies those - I think we should be consistent with MP resource here
created_at: 2025-03-11T01:26:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,61 @@
+---
+subcategory: "FIXME"
path: website/docs/r/policy_edge_high_availability_profile.html.markdown
user: login: ksamoray
body: I think that all those policy edge should be beta, right?
created_at: 2025-03-12T10:56:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,298 @@
+/* Copyright © 2024 Broadcom, Inc. All Rights Reserved.
+   SPDX-License-Identifier: MPL-2.0 */
+
+package nsxt
+
+import (
+	"fmt"
+	"log"
+	"reflect"
+
+	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+	"github.com/vmware/vsphere-automation-sdk-go/runtime/protocol/client"
+	"github.com/vmware/vsphere-automation-sdk-go/services/nsxt/infra"
+	"github.com/vmware/vsphere-automation-sdk-go/services/nsxt/infra/sites/enforcement_points"
+	"github.com/vmware/vsphere-automation-sdk-go/services/nsxt/model"
+
+	"github.com/vmware/terraform-provider-nsxt/nsxt/metadata"
+)
+
+var policyEdgeHighAvailabilityProfileSchema = map[string]*metadata.ExtendedSchema{
+	"nsx_id":       metadata.GetExtendedSchema(getNsxIDSchema()),
+	"path":         metadata.GetExtendedSchema(getPathSchema()),
+	"display_name": metadata.GetExtendedSchema(getDisplayNameSchema()),
+	"description":  metadata.GetExtendedSchema(getDescriptionSchema()),
+	"revision":     metadata.GetExtendedSchema(getRevisionSchema()),
+	"tag":          metadata.GetExtendedSchema(getTagsSchema()),
+	"site_path": {
+		Schema: schema.Schema{
+			Type:         schema.TypeString,
+			Description:  "Path to the site this Host Transport Node belongs to",
+			Optional:     true,
+			ForceNew:     true,
+			Default:      defaultInfraSitePath,
+			ValidateFunc: validatePolicyPath(),
+		},
+		Metadata: metadata.Metadata{
+			SchemaType: "string",
+			Skip:       true,
+		},
+	},
+	"enforcement_point": {
+		Schema: schema.Schema{
+			Type:        schema.TypeString,
+			Description: "ID of the enforcement point this Host Transport Node belongs to",
+			Optional:    true,
+			ForceNew:    true,
+			Default:     "default",
+		},
+		Metadata: metadata.Metadata{
+			SchemaType: "string",
+			Skip:       true,
+		},
+	},
+	"bfd_probe_interval": {
+		Schema: schema.Schema{
+			Type:     schema.TypeInt,
+			Optional: true,
+			Default:  500,
+		},
path: nsxt/resource_nsxt_policy_edge_high_availability_profile.go
user: login: ksamoray
body: I'm a bit confused as so far we were reluctant to validate such things in the provider, and left those to NSX.
As this resource is auto-generated, maybe we should add these validations in the generator? Those ranges are available in the APi spec...
created_at: 2025-03-12T11:11:02Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,61 @@
+---
+subcategory: "FIXME"
path: website/docs/r/policy_edge_high_availability_profile.html.markdown
user: login: annakhm
body: If we expect those to be fully tested by release, then we can go with `Fabric` right away 
created_at: 2025-03-12T21:53:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,298 @@
+/* Copyright © 2024 Broadcom, Inc. All Rights Reserved.
+   SPDX-License-Identifier: MPL-2.0 */
+
+package nsxt
+
+import (
+	"fmt"
+	"log"
+	"reflect"
+
+	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
+	"github.com/vmware/vsphere-automation-sdk-go/runtime/protocol/client"
+	"github.com/vmware/vsphere-automation-sdk-go/services/nsxt/infra"
+	"github.com/vmware/vsphere-automation-sdk-go/services/nsxt/infra/sites/enforcement_points"
+	"github.com/vmware/vsphere-automation-sdk-go/services/nsxt/model"
+
+	"github.com/vmware/terraform-provider-nsxt/nsxt/metadata"
+)
+
+var policyEdgeHighAvailabilityProfileSchema = map[string]*metadata.ExtendedSchema{
+	"nsx_id":       metadata.GetExtendedSchema(getNsxIDSchema()),
+	"path":         metadata.GetExtendedSchema(getPathSchema()),
+	"display_name": metadata.GetExtendedSchema(getDisplayNameSchema()),
+	"description":  metadata.GetExtendedSchema(getDescriptionSchema()),
+	"revision":     metadata.GetExtendedSchema(getRevisionSchema()),
+	"tag":          metadata.GetExtendedSchema(getTagsSchema()),
+	"site_path": {
+		Schema: schema.Schema{
+			Type:         schema.TypeString,
+			Description:  "Path to the site this Host Transport Node belongs to",
+			Optional:     true,
+			ForceNew:     true,
+			Default:      defaultInfraSitePath,
+			ValidateFunc: validatePolicyPath(),
+		},
+		Metadata: metadata.Metadata{
+			SchemaType: "string",
+			Skip:       true,
+		},
+	},
+	"enforcement_point": {
+		Schema: schema.Schema{
+			Type:        schema.TypeString,
+			Description: "ID of the enforcement point this Host Transport Node belongs to",
+			Optional:    true,
+			ForceNew:    true,
+			Default:     "default",
+		},
+		Metadata: metadata.Metadata{
+			SchemaType: "string",
+			Skip:       true,
+		},
+	},
+	"bfd_probe_interval": {
+		Schema: schema.Schema{
+			Type:     schema.TypeInt,
+			Optional: true,
+			Default:  500,
+		},
path: nsxt/resource_nsxt_policy_edge_high_availability_profile.go
user: login: annakhm
body: I was just pointing out inconsistency with MP resource, not, not insisting on the limits
created_at: 2025-03-12T22:20:41Z
author_association: CONTRIBUTOR

PR comments:
user: login: ksamoray
created_at: 2025-02-26T08:51:14Z
body: /test-all
author_association: CONTRIBUTOR
user: login: ksamoray
created_at: 2025-02-26T08:52:30Z
body: /test-all
author_association: CONTRIBUTOR
user: login: ksamoray
created_at: 2025-02-26T10:02:57Z
body: /test-all
author_association: CONTRIBUTOR
user: login: ksamoray
created_at: 2025-02-26T13:15:18Z
body: /test-all
author_association: CONTRIBUTOR
user: login: ksamoray
created_at: 2025-02-26T13:19:51Z
body: /test-all
author_association: CONTRIBUTOR
user: login: ksamoray
created_at: 2025-02-26T14:42:51Z
body: /test-all
author_association: CONTRIBUTOR
user: login: ksamoray
created_at: 2025-03-04T13:27:04Z
body: /test-all
author_association: CONTRIBUTOR
user: login: ksamoray
created_at: 2025-03-12T11:20:58Z
body: /test-all
author_association: CONTRIBUTOR
user: login: ksamoray
created_at: 2025-03-13T09:18:58Z
body: /test-all
author_association: CONTRIBUTOR