From fb1a54a7d1e59df3eb134f0200b41f89ef03bdad Mon Sep 17 00:00:00 2001 From: William Guilherme Date: Fri, 3 May 2024 21:20:31 -0700 Subject: [PATCH] fix: Fixed Policy Reorder Resource to validate Deception Rule (#450) * fix: Fixed Policy Reorder Resource to validate Deception Rule * fix: Added new attributes to Service Edge Group resource * fix: Added new service edge group attributes to test --- CHANGELOG.md | 20 +++- GNUmakefile | 6 +- docs/data-sources/zpa_service_edge_group.md | 4 + docs/guides/release-notes.md | 22 ++++- .../zpa_policy_access_rule_reorder.md | 95 ++++++++++++++++++- docs/resources/zpa_service_edge_group.md | 5 + zpa/data_source_zpa_service_edge_group.go | 18 ++++ ...data_source_zpa_service_edge_group_test.go | 3 + ...resource_zpa_policy_access_rule_reorder.go | 89 ++++++++++++----- ...rce_zpa_policy_access_rule_reorder_test.go | 5 - zpa/resource_zpa_service_edge_group.go | 44 +++++++++ zpa/resource_zpa_service_edge_group_test.go | 27 +++--- 12 files changed, 288 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dfcaa2b4..d2d4434d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,32 @@ # Changelog -## 3.2.2 (May, xx 2024) +## 3.2.11 (May, 3 2024) ### Notes -- Release date: **(May, xx 2024)** +- Release date: **(May, 3 2024)** - Supported Terraform version: **v1.x** ### Internal Changes - [PR #449](https://github.com/zscaler/terraform-provider-zpa/pull/449) - Added `CodeCov` Support to GitHub Workflow +### Bug Fixes +- [PR #450](https://github.com/zscaler/terraform-provider-zpa/pull/450) - Implemented additional validation within the resource `zpa_policy_access_rule_reorder` to ensure it accounts for the potential existence of the `Zscaler Deception` rule. [Zscaler API Documentation](https://help.zscaler.com/zpa/configuring-access-policies-using-api#:~:text=Updating%20the%20rule,configured%20using%20Deception.) for further details. + +⚠️ **WARNING:**: This change does not affect existing rule configurations, and is only applicable for tenants with the Zscaler Deception rule configured. If your tenant have this rule configured, please refer to the [provider documentation](https://registry.terraform.io/providers/zscaler/zpa/latest/docs/resources/zpa_policy_access_rule_reorder) for further examples on how you can address potential drift issues due to rule order missmatch. [Issue #445](https://github.com/zscaler/terraform-provider-zpa/issues/445) + +### ENHACEMENTS +- [PR #450](https://github.com/zscaler/terraform-provider-zpa/pull/450) - The resource `zpa_service_edge_group` now supports the following new attributes: + * `grace_distance_enabled`: Allows ZPA Private Service Edge Groups within the specified distance to be prioritized over a closer ZPA Public Service Edge. + * `grace_distance_value`: Indicates the maximum distance in miles or kilometers to ZPA Private Service Edge groups that would override a ZPA Public Service Edge. + * `grace_distance_value_unit`: Indicates the grace distance unit of measure in miles or kilometers. This value is only required if `grace_distance_enabled` is set to true. Support values are: `MILES` and `KMS` + +### Documentation +- [PR #450](https://github.com/zscaler/terraform-provider-zpa/pull/450) - Updated documentation for `zpa_policy_access_rule_reorder` by removing deprecated `policy_set_id` attribute from the resource. Only the `policy_type` is required. +### Documentation +- [PR #450](https://github.com/zscaler/terraform-provider-zpa/pull/450) - Updated documentation for `zpa_service_edge_group` by including detailed description of the new attributes: `grace_distance_enabled`, `grace_distance_value`, `grace_distance_value_unit`. + ## 3.2.1 (April, 8 2024) ### Notes diff --git a/GNUmakefile b/GNUmakefile index e93e1a93..7c9566ae 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -54,14 +54,14 @@ test\:integration\:zpa: build13: GOOS=$(shell go env GOOS) build13: GOARCH=$(shell go env GOARCH) ifeq ($(OS),Windows_NT) # is Windows_NT on XP, 2000, 7, Vista, 10... -build13: DESTINATION=$(APPDATA)/terraform.d/plugins/$(ZPA_PROVIDER_NAMESPACE)/3.2.1/$(GOOS)_$(GOARCH) +build13: DESTINATION=$(APPDATA)/terraform.d/plugins/$(ZPA_PROVIDER_NAMESPACE)/3.2.11/$(GOOS)_$(GOARCH) else -build13: DESTINATION=$(HOME)/.terraform.d/plugins/$(ZPA_PROVIDER_NAMESPACE)/3.2.1/$(GOOS)_$(GOARCH) +build13: DESTINATION=$(HOME)/.terraform.d/plugins/$(ZPA_PROVIDER_NAMESPACE)/3.2.11/$(GOOS)_$(GOARCH) endif build13: fmtcheck @echo "==> Installing plugin to $(DESTINATION)" @mkdir -p $(DESTINATION) - go build -o $(DESTINATION)/terraform-provider-zpa_v3.2.1 + go build -o $(DESTINATION)/terraform-provider-zpa_v3.2.11 vet: @echo "==> Checking source code against go vet and staticcheck" diff --git a/docs/data-sources/zpa_service_edge_group.md b/docs/data-sources/zpa_service_edge_group.md index d1967086..dad885b0 100644 --- a/docs/data-sources/zpa_service_edge_group.md +++ b/docs/data-sources/zpa_service_edge_group.md @@ -54,6 +54,10 @@ In addition to all arguments above, the following attributes are exported: * `creation_time` - (string) * `geo_location_id` - (string) * `is_public` - (string) +* `grace_distance_enabled`: Allows ZPA Private Service Edge Groups within the specified distance to be prioritized over a closer ZPA Public Service Edge. +* `grace_distance_value`: Indicates the maximum distance in miles or kilometers to ZPA Private Service Edge groups that would override a ZPA Public Service Edge. +* `grace_distance_value_unit`: Indicates the grace distance unit of measure in miles or kilometers. This value is only required if `grace_distance_enabled` is set to true. Support values are: `MILES` and `KMS` + * `latitude` - (string) Latitude of the Service Edge Group. Integer or decimal. With values in the range of `-90` to `90` * `longitude` - (string) Longitude of the Service Edge Group.Integer or decimal. With values in the range of `-180` to `180` * `location` - (string) Location of the Service Edge Group. diff --git a/docs/guides/release-notes.md b/docs/guides/release-notes.md index 1721ea4b..c3ef3333 100644 --- a/docs/guides/release-notes.md +++ b/docs/guides/release-notes.md @@ -12,21 +12,37 @@ Track all ZPA Terraform provider's releases. New resources, features, and bug fi --- -``Last updated: v3.2.2`` +``Last updated: v3.2.11`` --- -## 3.2.2 (May, xx 2024) +## 3.2.11 (May, 3 2024) ### Notes -- Release date: **(May, xx 2024)** +- Release date: **(May, 3 2024)** - Supported Terraform version: **v1.x** ### Internal Changes - [PR #449](https://github.com/zscaler/terraform-provider-zpa/pull/449) - Added `CodeCov` Support to GitHub Workflow +### Bug Fixes +- [PR #450](https://github.com/zscaler/terraform-provider-zpa/pull/450) - Implemented additional validation within the resource `zpa_policy_access_rule_reorder` to ensure it accounts for the potential existence of the `Zscaler Deception` rule. [Zscaler API Documentation](https://help.zscaler.com/zpa/configuring-access-policies-using-api#:~:text=Updating%20the%20rule,configured%20using%20Deception.) for further details. + +⚠️ **WARNING:**: This change does not affect existing rule configurations, and is only applicable for tenants with the Zscaler Deception rule configured. If your tenant have this rule configured, please refer to the [provider documentation](https://registry.terraform.io/providers/zscaler/zpa/latest/docs/resources/zpa_policy_access_rule_reorder) for further examples on how you can address potential drift issues due to rule order missmatch. [Issue #445](https://github.com/zscaler/terraform-provider-zpa/issues/445) + +### ENHACEMENTS +- [PR #450](https://github.com/zscaler/terraform-provider-zpa/pull/450) - The resource `zpa_service_edge_group` now supports the following new attributes: + * `grace_distance_enabled`: Allows ZPA Private Service Edge Groups within the specified distance to be prioritized over a closer ZPA Public Service Edge. + * `grace_distance_value`: Indicates the maximum distance in miles or kilometers to ZPA Private Service Edge groups that would override a ZPA Public Service Edge. + * `grace_distance_value_unit`: Indicates the grace distance unit of measure in miles or kilometers. This value is only required if `grace_distance_enabled` is set to true. Support values are: `MILES` and `KMS` + +### Documentation +- [PR #450](https://github.com/zscaler/terraform-provider-zpa/pull/450) - Updated documentation for `zpa_policy_access_rule_reorder` by removing deprecated `policy_set_id` attribute from the resource. Only the `policy_type` is required. +### Documentation +- [PR #450](https://github.com/zscaler/terraform-provider-zpa/pull/450) - Updated documentation for `zpa_service_edge_group` by including detailed description of the new attributes: `grace_distance_enabled`, `grace_distance_value`, `grace_distance_value_unit`. + ## 3.2.1 (April, 8 2024) ### Notes diff --git a/docs/resources/zpa_policy_access_rule_reorder.md b/docs/resources/zpa_policy_access_rule_reorder.md index 876e1510..c8b536ae 100644 --- a/docs/resources/zpa_policy_access_rule_reorder.md +++ b/docs/resources/zpa_policy_access_rule_reorder.md @@ -16,6 +16,8 @@ The **zpa_policy_access_rule_reorder** is a dedicated resource to manage and upd ⚠️ **WARNING:**: The attribute ``rule_order`` is now deprecated in favor of this resource for all ZPA policy types. +⚠️ **WARNING:**: Updating the rule order of an access policy configured using `Zscaler Deception` is not supported. When changing the rule order of a regular access policy and there is an access policy configured using Deception, the rule order of the regular access policy must be greater than the rule order for an access policy configured using Deception. Please refer to the [Zscaler API Documentation](https://help.zscaler.com/zpa/configuring-access-policies-using-api#:~:text=Updating%20the%20rule,configured%20using%20Deception.) for further details. + ## Example Usage 1 ```terraform @@ -24,7 +26,6 @@ resource "zpa_policy_access_rule" "example001" { description = "example001" action = "ALLOW" operator = "AND" - policy_set_id = data.zpa_policy_type.access_policy.id } resource "zpa_policy_access_rule" "example002" { @@ -32,7 +33,6 @@ resource "zpa_policy_access_rule" "example002" { description = "example002" action = "ALLOW" operator = "AND" - policy_set_id = data.zpa_policy_type.access_policy.id } locals { @@ -43,7 +43,6 @@ locals { } resource "zpa_policy_access_rule_reorder" "access_policy_reorder" { - policy_set_id = data.zpa_policy_type.access_policy.id policy_type = "ACCESS_POLICY" dynamic "rules" { @@ -99,6 +98,96 @@ resource "zpa_policy_access_rule_reorder" "access_policy_reorder" { } ``` +## Example Usage 3 - Used when Zscaler Deception Rule Exists + +```terraform +# IF NO ZSCALER DECEPTION RULE EXIST, DECREASE THE INDEX TO +1 TO PREVENT DRIFTS +locals { + policy_config = yamldecode(file("${path.module}/policies.yaml")) + policies = { for policy in local.policy_config.policies : policy.name => merge(policy, { rule_number = index(local.policy_config.policies, policy) + 2 }) } +} + +resource "zpa_policy_access_rule" "rules" { + for_each = local.policies + name = each.value.name + action = each.value.action + description = each.value.description + custom_msg = try(each.value.custom_msg, null) + operator = try(each.value.operator, "AND") +} + +resource "zpa_policy_access_rule_reorder" "access_policy_reorder" { + policy_type = "ACCESS_POLICY" + + dynamic "rules" { + for_each = local.policies + content { + id = zpa_policy_access_rule.rules[rules.key].id + order = rules.value.rule_number + } + } +} +``` + +## Example Usage 4 - Similar to Example 3 - No YAML File + +```terraform +locals { + policies = { for index, policy in var.policy_config.policies : + policy.name => merge(policy, { rule_number = index + 1 }) + } +} + +resource "zpa_policy_access_rule" "rules" { + for_each = { for rule in local.policies : rule.name => rule } + name = each.value.name + action = each.value.action + description = each.value.description + custom_msg = try(each.value.custom_msg, null) + operator = try(each.value.operator, "AND") +} + + +resource "zpa_policy_access_rule_reorder" "access_policy_reorder" { + policy_type = "ACCESS_POLICY" + + dynamic "rules" { + for_each = local.policies # This sets up 'rules' as the variable within the block + content { + id = zpa_policy_access_rule.rules[rules.key].id # Access 'rules.key' for the map key + order = rules.value.rule_number # Use 'rules.value' to get the values from the map + } + } +} + +variable "policy_config" { + description = "Configuration for policy rules" + type = object({ + policies = list(object({ + name = string + description = string + action = string + // Additional attributes can be included here as needed + })) + }) + + default = { + policies = [ + { name = "example001", description = "example001", action = "ALLOW"}, + { name = "example002", description = "example002", action = "DENY" }, + { name = "example003", description = "example003", action = "ALLOW" }, + { name = "example004", description = "example004", action = "DENY" }, + { name = "example005", description = "example005", action = "ALLOW" }, + { name = "example006", description = "example006", action = "DENY" }, + { name = "example007", description = "example007", action = "ALLOW" }, + { name = "example008", description = "example008", action = "DENY" }, + { name = "example009", description = "example009", action = "ALLOW" }, + { name = "example010", description = "example010", action = "DENY" }, + ] + } +} +``` + ## Schema ### Required diff --git a/docs/resources/zpa_service_edge_group.md b/docs/resources/zpa_service_edge_group.md index ef0c5ca7..02a0fbce 100644 --- a/docs/resources/zpa_service_edge_group.md +++ b/docs/resources/zpa_service_edge_group.md @@ -71,6 +71,11 @@ In addition to all arguments above, the following attributes are exported: - `enabled` - (Boolean) Whether this Service Edge Group is enabled or not. Default value: `true` Supported values: `true`, `false` - `description` - (String) Description of the Service Edge Group. - `is_public` - (String) Enable or disable public access for the Service Edge Group. Default value: `false` Supported values: `true`, `false` + +- `grace_distance_enabled`: Allows ZPA Private Service Edge Groups within the specified distance to be prioritized over a closer ZPA Public Service Edge. +- `grace_distance_value`: Indicates the maximum distance in miles or kilometers to ZPA Private Service Edge groups that would override a ZPA Public Service Edge. +- `grace_distance_value_unit`: Indicates the grace distance unit of measure in miles or kilometers. This value is only required if `grace_distance_enabled` is set to true. Support values are: `MILES` and `KMS` + - `override_version_profile` - (Boolean) Whether the default version profile of the App Connector Group is applied or overridden. Default: `false` Supported values: `true`, `false` - `version_profile_id` - (String) ID of the version profile. To learn more, see Version Profile Use Cases. Supported values are: - ``0`` = ``Default`` diff --git a/zpa/data_source_zpa_service_edge_group.go b/zpa/data_source_zpa_service_edge_group.go index ccb3925f..3d7e877f 100644 --- a/zpa/data_source_zpa_service_edge_group.go +++ b/zpa/data_source_zpa_service_edge_group.go @@ -362,6 +362,21 @@ func dataSourceServiceEdgeGroup() *schema.Resource { Computed: true, Description: "ID of the version profile. To learn more", }, + "grace_distance_enabled": { + Type: schema.TypeBool, + Computed: true, + Description: "If enabled, allows ZPA Private Service Edge Groups within the specified distance to be prioritized over a closer ZPA Public Service Edge.", + }, + "grace_distance_value": { + Type: schema.TypeString, + Computed: true, + Description: "Indicates the maximum distance in miles or kilometers to ZPA Private Service Edge groups that would override a ZPA Public Service Edge", + }, + "grace_distance_value_unit": { + Type: schema.TypeString, + Computed: true, + Description: "Indicates the grace distance unit of measure in miles or kilometers. This value is only required if grace_distance_value is set to true", + }, }, } } @@ -409,6 +424,9 @@ func dataSourceServiceEdgeGroupRead(d *schema.ResourceData, m interface{}) error _ = d.Set("version_profile_id", resp.VersionProfileID) _ = d.Set("version_profile_name", resp.VersionProfileName) _ = d.Set("version_profile_visibility_scope", resp.VersionProfileVisibilityScope) + _ = d.Set("grace_distance_enabled", resp.GraceDistanceEnabled) + _ = d.Set("grace_distance_value", resp.GraceDistanceValue) + _ = d.Set("grace_distance_value_unit", resp.GraceDistanceValueUnit) _ = d.Set("trusted_networks", flattenTrustedNetworks(resp)) _ = d.Set("service_edges", flattenServiceEdges(resp.ServiceEdges)) diff --git a/zpa/data_source_zpa_service_edge_group_test.go b/zpa/data_source_zpa_service_edge_group_test.go index ffe76b84..d3c1cde6 100644 --- a/zpa/data_source_zpa_service_edge_group_test.go +++ b/zpa/data_source_zpa_service_edge_group_test.go @@ -30,6 +30,9 @@ func TestAccDataSourceServiceEdgeGroup_Basic(t *testing.T) { resource.TestCheckResourceAttrPair(dataSourceTypeAndName, "longitude", resourceTypeAndName, "longitude"), resource.TestCheckResourceAttrPair(dataSourceTypeAndName, "location", resourceTypeAndName, "location"), resource.TestCheckResourceAttrPair(dataSourceTypeAndName, "version_profile_name", resourceTypeAndName, "version_profile_name"), + resource.TestCheckResourceAttrPair(dataSourceTypeAndName, "grace_distance_enabled", resourceTypeAndName, "grace_distance_enabled"), + resource.TestCheckResourceAttrPair(dataSourceTypeAndName, "grace_distance_value", resourceTypeAndName, "grace_distance_value"), + resource.TestCheckResourceAttrPair(dataSourceTypeAndName, "grace_distance_value_unit", resourceTypeAndName, "grace_distance_value_unit"), ), }, }, diff --git a/zpa/resource_zpa_policy_access_rule_reorder.go b/zpa/resource_zpa_policy_access_rule_reorder.go index 0dbaedc3..31243ab8 100644 --- a/zpa/resource_zpa_policy_access_rule_reorder.go +++ b/zpa/resource_zpa_policy_access_rule_reorder.go @@ -131,53 +131,98 @@ func getRules(d *schema.ResourceData) (*RulesOrders, error) { func resourcePolicyAccessReorderRead(d *schema.ResourceData, m interface{}) error { zClient := m.(*Client) - rulesOrders, err := getRules(d) + policyType := d.Get("policy_type").(string) + + currentRules, _, err := zClient.policysetcontroller.GetAllByType(policyType) if err != nil { + log.Printf("[ERROR] failed to get rules: %v\n", err) + d.SetId("") return err } - rules, _, err := zClient.policysetcontroller.GetAllByType(rulesOrders.PolicyType) + + configuredRules, err := getRules(d) if err != nil { - log.Printf("[ERROR] failed to get rules: %v\n", err) return err } - log.Printf("[INFO] reorder rules on read: %v\n", rulesOrders) - for _, r := range rules { - for id := range rulesOrders.Orders { - if r.ID == id { - rulesOrders.Orders[id], _ = strconv.Atoi(r.RuleOrder) - } + + log.Printf("[INFO] reorder rules on read: %v\n", configuredRules) + + currentOrderMap := make(map[string]int) + for _, rule := range currentRules { + if order, err := strconv.Atoi(rule.RuleOrder); err == nil { + currentOrderMap[rule.ID] = order } } + + for id := range configuredRules.Orders { + if currentOrder, exists := currentOrderMap[id]; exists { + configuredRules.Orders[id] = currentOrder + } + } + rulesMap := []map[string]interface{}{} - for id, order := range rulesOrders.Orders { + for id, order := range configuredRules.Orders { rulesMap = append(rulesMap, map[string]interface{}{ "id": id, "order": strconv.Itoa(order), }) } - _ = d.Set("rules", rulesMap) + + if err := d.Set("rules", rulesMap); err != nil { + return err + } + + d.SetId(fmt.Sprintf("%s-%s", policyType, "reorder")) + return nil } func resourcePolicyAccessReorderUpdate(d *schema.ResourceData, m interface{}) error { - // Convert the interface to a client instance. zClient := m.(*Client) - // Fetch and sort the rule orders from the provided data. - rules, err := getRules(d) + + existingRules, _, err := zClient.policysetcontroller.GetAllByType(d.Get("policy_type").(string)) if err != nil { + log.Printf("[ERROR] Failed to get existing rules: %v\n", err) return err } - // Validate the fetched rule orders. - if err := validateRuleOrders(rules); err != nil { - log.Printf("[ERROR] reordering rules failed: %v\n", err) - return err + + deceptionAtOne := false + deceptionID := "" + for _, rule := range existingRules { + if rule.Name == "Zscaler Deception" && rule.RuleOrder == "1" { + deceptionAtOne = true + deceptionID = rule.ID + break + } } - d.SetId(rules.PolicyType) - _, err = zClient.policysetcontroller.BulkReorder(rules.PolicyType, rules.Orders) + + userDefinedRules, err := getRules(d) if err != nil { - log.Printf("[ERROR] reordering rules failed: %v\n", err) + return err + } + + if err := validateRuleOrders(userDefinedRules); err != nil { + log.Printf("[ERROR] Reordering rules failed: %v\n", err) + return err } - // Read the updated rule set. + + ruleIdToOrder := make(map[string]int) + baseOrder := 1 + if deceptionAtOne { + ruleIdToOrder[deceptionID] = 1 + baseOrder = 2 + } + + for id, order := range userDefinedRules.Orders { + ruleIdToOrder[id] = order + baseOrder - 1 + } + + if _, err := zClient.policysetcontroller.BulkReorder(d.Get("policy_type").(string), ruleIdToOrder); err != nil { + log.Printf("[ERROR] Bulk reordering rules failed: %v", err) + return err + } + + d.SetId(fmt.Sprintf("%s-%s", d.Get("policy_type").(string), "reorder")) return resourcePolicyAccessReorderRead(d, m) } diff --git a/zpa/resource_zpa_policy_access_rule_reorder_test.go b/zpa/resource_zpa_policy_access_rule_reorder_test.go index 736e5220..8c147aa0 100644 --- a/zpa/resource_zpa_policy_access_rule_reorder_test.go +++ b/zpa/resource_zpa_policy_access_rule_reorder_test.go @@ -141,16 +141,12 @@ func testAccCheckPolicyAccessReorderDestroy(s *terraform.State) error { func testAccPolicyAccessRuleReorderConfig(randName string) string { return fmt.Sprintf(` -data "zpa_policy_type" "access_policy" { - policy_type = "ACCESS_POLICY" -} resource "zpa_policy_access_rule" "rule1" { name = "%s-rule1" description = "%s-desc1" action = "ALLOW" operator = "AND" - policy_set_id = data.zpa_policy_type.access_policy.id lifecycle { create_before_destroy = true @@ -162,7 +158,6 @@ resource "zpa_policy_access_rule" "rule2" { description = "%s-desc2" action = "ALLOW" operator = "AND" - policy_set_id = data.zpa_policy_type.access_policy.id depends_on = [zpa_policy_access_rule.rule1] lifecycle { diff --git a/zpa/resource_zpa_service_edge_group.go b/zpa/resource_zpa_service_edge_group.go index 2b483a32..db890ccc 100644 --- a/zpa/resource_zpa_service_edge_group.go +++ b/zpa/resource_zpa_service_edge_group.go @@ -1,6 +1,7 @@ package zpa import ( + "fmt" "log" "strconv" "strings" @@ -176,6 +177,43 @@ func resourceServiceEdgeGroup() *schema.Resource { Optional: true, Computed: true, }, + "grace_distance_enabled": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + Description: "If enabled, allows ZPA Private Service Edge Groups within the specified distance to be prioritized over a closer ZPA Public Service Edge.", + }, + "grace_distance_value": { + Type: schema.TypeString, + Optional: true, + Computed: true, + RequiredWith: []string{"grace_distance_enabled", "grace_distance_value_unit"}, + Description: "Indicates the maximum distance in miles or kilometers to ZPA Private Service Edge groups that would override a ZPA Public Service Edge", + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + // Normalize the old and new values by converting them to float64 and formatting them as strings with one decimal place + oldVal, errOld := strconv.ParseFloat(old, 64) + if errOld != nil { + return false // If the old value can't be parsed as float, don't suppress the diff + } + newVal, errNew := strconv.ParseFloat(new, 64) + if errNew != nil { + return false // If the new value can't be parsed as float, don't suppress the diff + } + // Return true if the normalized old and new values are equal + return fmt.Sprintf("%.1f", oldVal) == fmt.Sprintf("%.1f", newVal) + }, + }, + + "grace_distance_value_unit": { + Type: schema.TypeString, + Optional: true, + Computed: true, + RequiredWith: []string{"grace_distance_enabled", "grace_distance_value"}, + Description: "Indicates the grace distance unit of measure in miles or kilometers. This value is only required if grace_distance_value is set to true", + ValidateFunc: validation.StringInSlice([]string{ + "MILES", "KMS", + }, false), + }, }, } } @@ -231,6 +269,9 @@ func resourceServiceEdgeGroupRead(d *schema.ResourceData, m interface{}) error { _ = d.Set("version_profile_id", resp.VersionProfileID) _ = d.Set("version_profile_name", resp.VersionProfileName) _ = d.Set("microtenant_id", resp.MicroTenantID) + _ = d.Set("grace_distance_enabled", resp.GraceDistanceEnabled) + _ = d.Set("grace_distance_value", resp.GraceDistanceValue) + _ = d.Set("grace_distance_value_unit", resp.GraceDistanceValueUnit) _ = d.Set("version_profile_visibility_scope", resp.VersionProfileVisibilityScope) _ = d.Set("trusted_networks", flattenAppTrustedNetworksSimple(resp.TrustedNetworks)) _ = d.Set("service_edges", flattenServiceEdgeSimple(resp.ServiceEdges)) @@ -294,6 +335,9 @@ func expandServiceEdgeGroup(d *schema.ResourceData) serviceedgegroup.ServiceEdge VersionProfileVisibilityScope: d.Get("version_profile_visibility_scope").(string), OverrideVersionProfile: d.Get("override_version_profile").(bool), MicroTenantID: d.Get("microtenant_id").(string), + GraceDistanceEnabled: d.Get("grace_distance_enabled").(bool), + GraceDistanceValue: d.Get("grace_distance_value").(string), + GraceDistanceValueUnit: d.Get("grace_distance_value_unit").(string), ServiceEdges: expandServiceEdges(d), TrustedNetworks: expandTrustedNetworks(d), } diff --git a/zpa/resource_zpa_service_edge_group_test.go b/zpa/resource_zpa_service_edge_group_test.go index 8f0d57a9..6c00e40e 100644 --- a/zpa/resource_zpa_service_edge_group_test.go +++ b/zpa/resource_zpa_service_edge_group_test.go @@ -114,18 +114,21 @@ func testAccCheckServiceEdgeGroupConfigure(resourceTypeAndName, generatedName, d return fmt.Sprintf(` resource "%s" "%s" { - name = "%s" - description = "%s" - enabled = "%s" - is_public = "%s" - upgrade_day = "SUNDAY" - upgrade_time_in_secs = "66600" - country_code = "US" - city_country = "San Jose, US" - latitude = "37.33874" - longitude = "-121.8852525" - location = "San Jose, CA, USA" - version_profile_id = 0 + name = "%s" + description = "%s" + enabled = "%s" + is_public = "%s" + upgrade_day = "SUNDAY" + upgrade_time_in_secs = "66600" + country_code = "US" + city_country = "San Jose, US" + latitude = "37.33874" + longitude = "-121.8852525" + location = "San Jose, CA, USA" + version_profile_id = 0 + grace_distance_enabled = true + grace_distance_value = "10" + grace_distance_value_unit = "KMS" } data "%s" "%s" {