Deprecate usage of ports (port list) and add support to use updateNetworkAclItems API instead of recreating rules when modified (#242)

* Deprecate usage of ports

* add support to call updateNetworkAclItem when a rule is modified rather than re-creating

* allow modifying rule number

* update doc with examples and deprecation notice

* address comment - modularize method
diff --git a/cloudstack/resource_cloudstack_network_acl_rule.go b/cloudstack/resource_cloudstack_network_acl_rule.go
index 253fd1b..699c7da 100644
--- a/cloudstack/resource_cloudstack_network_acl_rule.go
+++ b/cloudstack/resource_cloudstack_network_acl_rule.go
@@ -23,6 +23,7 @@
 	"context"
 	"fmt"
 	"log"
+	"sort"
 	"strconv"
 	"strings"
 	"sync"
@@ -58,7 +59,7 @@
 			},
 
 			"rule": {
-				Type:     schema.TypeSet,
+				Type:     schema.TypeList,
 				Optional: true,
 				Elem: &schema.Resource{
 					Schema: map[string]*schema.Schema{
@@ -75,10 +76,9 @@
 						},
 
 						"cidr_list": {
-							Type:     schema.TypeSet,
+							Type:     schema.TypeList,
 							Required: true,
 							Elem:     &schema.Schema{Type: schema.TypeString},
-							Set:      schema.HashString,
 						},
 
 						"protocol": {
@@ -99,10 +99,16 @@
 						},
 
 						"ports": {
-							Type:     schema.TypeSet,
+							Type:       schema.TypeSet,
+							Optional:   true,
+							Elem:       &schema.Schema{Type: schema.TypeString},
+							Set:        schema.HashString,
+							Deprecated: "Use 'port' instead. The 'ports' field is deprecated and will be removed in a future version.",
+						},
+
+						"port": {
+							Type:     schema.TypeString,
 							Optional: true,
-							Elem:     &schema.Schema{Type: schema.TypeString},
-							Set:      schema.HashString,
 						},
 
 						"traffic_type": {
@@ -149,12 +155,12 @@
 	}
 
 	// Create all rules that are configured
-	if nrs := d.Get("rule").(*schema.Set); nrs.Len() > 0 {
-		// Create an empty rule set to hold all newly created rules
-		rules := resourceCloudStackNetworkACLRule().Schema["rule"].ZeroValue().(*schema.Set)
+	if nrs := d.Get("rule").([]interface{}); len(nrs) > 0 {
+		// Create an empty rule list to hold all newly created rules
+		rules := make([]interface{}, 0)
 
-		log.Printf("[DEBUG] Processing %d rules", nrs.Len())
-		err := createNetworkACLRules(d, meta, rules, nrs)
+		log.Printf("[DEBUG] Processing %d rules", len(nrs))
+		err := createNetworkACLRules(d, meta, &rules, nrs)
 		if err != nil {
 			log.Printf("[ERROR] Failed to create network ACL rules: %v", err)
 			return err
@@ -178,15 +184,15 @@
 	return resourceCloudStackNetworkACLRuleRead(d, meta)
 }
 
-func createNetworkACLRules(d *schema.ResourceData, meta interface{}, rules *schema.Set, nrs *schema.Set) error {
-	log.Printf("[DEBUG] Creating %d network ACL rules", nrs.Len())
+func createNetworkACLRules(d *schema.ResourceData, meta interface{}, rules *[]interface{}, nrs []interface{}) error {
+	log.Printf("[DEBUG] Creating %d network ACL rules", len(nrs))
 	var errs *multierror.Error
 
 	var wg sync.WaitGroup
-	wg.Add(nrs.Len())
+	wg.Add(len(nrs))
 
 	sem := make(chan struct{}, d.Get("parallelism").(int))
-	for i, rule := range nrs.List() {
+	for i, rule := range nrs {
 		// Put in a tiny sleep here to avoid DoS'ing the API
 		time.Sleep(500 * time.Millisecond)
 
@@ -202,8 +208,8 @@
 				log.Printf("[ERROR] Failed to create rule #%d: %v", index+1, err)
 				errs = multierror.Append(errs, fmt.Errorf("rule #%d: %v", index+1, err))
 			} else if len(rule["uuids"].(map[string]interface{})) > 0 {
-				log.Printf("[DEBUG] Successfully created rule #%d, adding to rules set", index+1)
-				rules.Add(rule)
+				log.Printf("[DEBUG] Successfully created rule #%d, adding to rules list", index+1)
+				*rules = append(*rules, rule)
 			} else {
 				log.Printf("[WARN] Rule #%d created but has no UUIDs", index+1)
 			}
@@ -255,7 +261,7 @@
 
 	// Set the CIDR list
 	var cidrList []string
-	for _, cidr := range rule["cidr_list"].(*schema.Set).List() {
+	for _, cidr := range rule["cidr_list"].([]interface{}) {
 		cidrList = append(cidrList, cidr.(string))
 	}
 	p.SetCidrlist(cidrList)
@@ -299,42 +305,19 @@
 		log.Printf("[DEBUG] Created ALL rule with ID=%s", r.(*cloudstack.CreateNetworkACLResponse).Id)
 	}
 
-	// If protocol is TCP or UDP, create the rule (with or without ports)
+	// If protocol is TCP or UDP, create the rule (with or without port)
 	if rule["protocol"].(string) == "tcp" || rule["protocol"].(string) == "udp" {
-		ps, ok := rule["ports"].(*schema.Set)
-		if !ok || ps == nil {
-			log.Printf("[DEBUG] No ports specified for TCP/UDP rule, creating rule for all ports")
-			ps = &schema.Set{F: schema.HashString}
-		}
+		portStr, hasPort := rule["port"].(string)
 
-		// Create an empty schema.Set to hold all processed ports
-		ports := &schema.Set{F: schema.HashString}
-		log.Printf("[DEBUG] Processing %d ports for TCP/UDP rule", ps.Len())
+		if hasPort && portStr != "" {
+			// Handle single port
+			log.Printf("[DEBUG] Processing single port for TCP/UDP rule: %s", portStr)
 
-		if ps.Len() == 0 {
-			// Create a rule for all ports
-			r, err := Retry(4, retryableACLCreationFunc(cs, p))
-			if err != nil {
-				log.Printf("[ERROR] Failed to create TCP/UDP rule for all ports: %v", err)
-				return err
-			}
-			uuids["all_ports"] = r.(*cloudstack.CreateNetworkACLResponse).Id
-			rule["uuids"] = uuids
-			log.Printf("[DEBUG] Created TCP/UDP rule for all ports with ID=%s", r.(*cloudstack.CreateNetworkACLResponse).Id)
-		} else {
-			// Process specified ports
-			for _, port := range ps.List() {
-				if _, ok := uuids[port.(string)]; ok {
-					ports.Add(port)
-					rule["ports"] = ports
-					log.Printf("[DEBUG] Port %s already has UUID, skipping", port.(string))
-					continue
-				}
-
-				m := splitPorts.FindStringSubmatch(port.(string))
+			if _, ok := uuids[portStr]; !ok {
+				m := splitPorts.FindStringSubmatch(portStr)
 				if m == nil {
-					log.Printf("[ERROR] Invalid port format: %s", port.(string))
-					return fmt.Errorf("%q is not a valid port value. Valid options are '80' or '80-90'", port.(string))
+					log.Printf("[ERROR] Invalid port format: %s", portStr)
+					return fmt.Errorf("%q is not a valid port value. Valid options are '80' or '80-90'", portStr)
 				}
 
 				startPort, err := strconv.Atoi(m[1])
@@ -354,20 +337,31 @@
 
 				p.SetStartport(startPort)
 				p.SetEndport(endPort)
-				log.Printf("[DEBUG] Set ports start=%d, end=%d", startPort, endPort)
+				log.Printf("[DEBUG] Set port start=%d, end=%d", startPort, endPort)
 
 				r, err := Retry(4, retryableACLCreationFunc(cs, p))
 				if err != nil {
-					log.Printf("[ERROR] Failed to create TCP/UDP rule for port %s: %v", port.(string), err)
+					log.Printf("[ERROR] Failed to create TCP/UDP rule for port %s: %v", portStr, err)
 					return err
 				}
 
-				ports.Add(port)
-				rule["ports"] = ports
-				uuids[port.(string)] = r.(*cloudstack.CreateNetworkACLResponse).Id
+				uuids[portStr] = r.(*cloudstack.CreateNetworkACLResponse).Id
 				rule["uuids"] = uuids
-				log.Printf("[DEBUG] Created TCP/UDP rule for port %s with ID=%s", port.(string), r.(*cloudstack.CreateNetworkACLResponse).Id)
+				log.Printf("[DEBUG] Created TCP/UDP rule for port %s with ID=%s", portStr, r.(*cloudstack.CreateNetworkACLResponse).Id)
+			} else {
+				log.Printf("[DEBUG] Port %s already has UUID, skipping", portStr)
 			}
+		} else {
+			// No port specified - create rule for all ports
+			log.Printf("[DEBUG] No port specified for TCP/UDP rule, creating rule for all ports")
+			r, err := Retry(4, retryableACLCreationFunc(cs, p))
+			if err != nil {
+				log.Printf("[ERROR] Failed to create TCP/UDP rule for all ports: %v", err)
+				return err
+			}
+			uuids["all_ports"] = r.(*cloudstack.CreateNetworkACLResponse).Id
+			rule["uuids"] = uuids
+			log.Printf("[DEBUG] Created TCP/UDP rule for all ports with ID=%s", r.(*cloudstack.CreateNetworkACLResponse).Id)
 		}
 	}
 
@@ -375,6 +369,99 @@
 	return nil
 }
 
+func processTCPUDPRule(rule map[string]interface{}, ruleMap map[string]*cloudstack.NetworkACL, uuids map[string]interface{}, rules *[]interface{}) {
+	// Check for deprecated ports field first (for backward compatibility)
+	ps, hasPortsSet := rule["ports"].(*schema.Set)
+	portStr, hasPort := rule["port"].(string)
+
+	if hasPortsSet && ps.Len() > 0 {
+		log.Printf("[DEBUG] Processing %d ports for TCP/UDP rule (deprecated field)", ps.Len())
+
+		var ports []interface{}
+		for _, port := range ps.List() {
+			if processPortForRule(port.(string), rule, ruleMap, uuids) {
+				ports = append(ports, port)
+				log.Printf("[DEBUG] Added port %s to TCP/UDP rule", port.(string))
+			}
+		}
+
+		if len(ports) > 0 {
+			rule["ports"] = schema.NewSet(schema.HashString, ports)
+			*rules = append(*rules, rule)
+			log.Printf("[DEBUG] Added TCP/UDP rule with deprecated ports to state: %+v", rule)
+		}
+
+	} else if hasPort && portStr != "" {
+		log.Printf("[DEBUG] Processing single port for TCP/UDP rule: %s", portStr)
+
+		if processPortForRule(portStr, rule, ruleMap, uuids) {
+			rule["port"] = portStr
+			*rules = append(*rules, rule)
+			log.Printf("[DEBUG] Added TCP/UDP rule with single port to state: %+v", rule)
+		}
+
+	} else {
+		log.Printf("[DEBUG] Processing TCP/UDP rule with no port specified")
+
+		id, ok := uuids["all_ports"]
+		if !ok {
+			log.Printf("[DEBUG] No UUID for all_ports, skipping rule")
+			return
+		}
+
+		r, ok := ruleMap[id.(string)]
+		if !ok {
+			log.Printf("[DEBUG] TCP/UDP rule for all_ports with ID %s not found, removing UUID", id.(string))
+			delete(uuids, "all_ports")
+			return
+		}
+
+		delete(ruleMap, id.(string))
+
+		var cidrs []interface{}
+		for _, cidr := range strings.Split(r.Cidrlist, ",") {
+			cidrs = append(cidrs, cidr)
+		}
+
+		rule["action"] = strings.ToLower(r.Action)
+		rule["protocol"] = r.Protocol
+		rule["traffic_type"] = strings.ToLower(r.Traffictype)
+		rule["cidr_list"] = cidrs
+		*rules = append(*rules, rule)
+		log.Printf("[DEBUG] Added TCP/UDP rule with no port to state: %+v", rule)
+	}
+}
+
+func processPortForRule(portStr string, rule map[string]interface{}, ruleMap map[string]*cloudstack.NetworkACL, uuids map[string]interface{}) bool {
+	id, ok := uuids[portStr]
+	if !ok {
+		log.Printf("[DEBUG] No UUID for port %s, skipping", portStr)
+		return false
+	}
+
+	r, ok := ruleMap[id.(string)]
+	if !ok {
+		log.Printf("[DEBUG] TCP/UDP rule for port %s with ID %s not found, removing UUID", portStr, id.(string))
+		delete(uuids, portStr)
+		return false
+	}
+
+	// Delete the known rule so only unknown rules remain in the ruleMap
+	delete(ruleMap, id.(string))
+
+	var cidrs []interface{}
+	for _, cidr := range strings.Split(r.Cidrlist, ",") {
+		cidrs = append(cidrs, cidr)
+	}
+
+	rule["action"] = strings.ToLower(r.Action)
+	rule["protocol"] = r.Protocol
+	rule["traffic_type"] = strings.ToLower(r.Traffictype)
+	rule["cidr_list"] = cidrs
+
+	return true
+}
+
 func resourceCloudStackNetworkACLRuleRead(d *schema.ResourceData, meta interface{}) error {
 	cs := meta.(*cloudstack.CloudStackClient)
 	log.Printf("[DEBUG] Entering resourceCloudStackNetworkACLRuleRead with acl_id=%s", d.Id())
@@ -428,12 +515,12 @@
 	}
 	log.Printf("[DEBUG] Loaded %d rules into ruleMap", len(ruleMap))
 
-	// Create an empty schema.Set to hold all rules
-	rules := resourceCloudStackNetworkACLRule().Schema["rule"].ZeroValue().(*schema.Set)
+	// Create an empty rule list to hold all rules
+	var rules []interface{}
 
 	// Read all rules that are configured
-	if rs := d.Get("rule").(*schema.Set); rs.Len() > 0 {
-		for _, rule := range rs.List() {
+	if rs := d.Get("rule").([]interface{}); len(rs) > 0 {
+		for _, rule := range rs {
 			rule := rule.(map[string]interface{})
 			uuids := rule["uuids"].(map[string]interface{})
 			log.Printf("[DEBUG] Processing rule with protocol=%s, uuids=%+v", rule["protocol"].(string), uuids)
@@ -456,10 +543,10 @@
 				// Delete the known rule so only unknown rules remain in the ruleMap
 				delete(ruleMap, id.(string))
 
-				// Create a set with all CIDR's
-				cidrs := &schema.Set{F: schema.HashString}
+				// Create a list with all CIDR's
+				var cidrs []interface{}
 				for _, cidr := range strings.Split(r.Cidrlist, ",") {
-					cidrs.Add(cidr)
+					cidrs = append(cidrs, cidr)
 				}
 
 				// Update the values
@@ -469,7 +556,7 @@
 				rule["icmp_code"] = r.Icmpcode
 				rule["traffic_type"] = strings.ToLower(r.Traffictype)
 				rule["cidr_list"] = cidrs
-				rules.Add(rule)
+				rules = append(rules, rule)
 				log.Printf("[DEBUG] Added ICMP rule to state: %+v", rule)
 			}
 
@@ -491,10 +578,10 @@
 				// Delete the known rule so only unknown rules remain in the ruleMap
 				delete(ruleMap, id.(string))
 
-				// Create a set with all CIDR's
-				cidrs := &schema.Set{F: schema.HashString}
+				// Create a list with all CIDR's
+				var cidrs []interface{}
 				for _, cidr := range strings.Split(r.Cidrlist, ",") {
-					cidrs.Add(cidr)
+					cidrs = append(cidrs, cidr)
 				}
 
 				// Update the values
@@ -502,66 +589,13 @@
 				rule["protocol"] = r.Protocol
 				rule["traffic_type"] = strings.ToLower(r.Traffictype)
 				rule["cidr_list"] = cidrs
-				rules.Add(rule)
+				rules = append(rules, rule)
 				log.Printf("[DEBUG] Added ALL rule to state: %+v", rule)
 			}
 
 			if rule["protocol"].(string) == "tcp" || rule["protocol"].(string) == "udp" {
-				ps, ok := rule["ports"].(*schema.Set)
-				if !ok || ps == nil {
-					log.Printf("[DEBUG] No ports specified for TCP/UDP rule, initializing empty set")
-					ps = &schema.Set{F: schema.HashString}
-				}
-
-				// Create an empty schema.Set to hold all ports
-				ports := &schema.Set{F: schema.HashString}
-				log.Printf("[DEBUG] Processing %d ports for TCP/UDP rule", ps.Len())
-
-				// Loop through all ports and retrieve their info
-				for _, port := range ps.List() {
-					id, ok := uuids[port.(string)]
-					if !ok {
-						log.Printf("[DEBUG] No UUID for port %s, skipping", port.(string))
-						continue
-					}
-
-					// Get the rule
-					r, ok := ruleMap[id.(string)]
-					if !ok {
-						log.Printf("[DEBUG] TCP/UDP rule for port %s with ID %s not found, removing UUID", port.(string), id.(string))
-						delete(uuids, port.(string))
-						continue
-					}
-
-					// Delete the known rule so only unknown rules remain in the ruleMap
-					delete(ruleMap, id.(string))
-
-					// Create a set with all CIDR's
-					cidrs := &schema.Set{F: schema.HashString}
-					for _, cidr := range strings.Split(r.Cidrlist, ",") {
-						cidrs.Add(cidr)
-					}
-
-					// Update the values
-					rule["action"] = strings.ToLower(r.Action)
-					rule["protocol"] = r.Protocol
-					rule["traffic_type"] = strings.ToLower(r.Traffictype)
-					rule["cidr_list"] = cidrs
-					ports.Add(port)
-					log.Printf("[DEBUG] Added port %s to TCP/UDP rule", port.(string))
-				}
-
-				// If there is at least one port found, add this rule to the rules set
-				if ports.Len() > 0 {
-					rule["ports"] = ports
-					rules.Add(rule)
-					log.Printf("[DEBUG] Added TCP/UDP rule to state: %+v", rule)
-				} else {
-					// Add the rule even if no ports are specified, as ports are optional
-					rule["ports"] = ports
-					rules.Add(rule)
-					log.Printf("[DEBUG] Added TCP/UDP rule with no ports to state: %+v", rule)
-				}
+				uuids := rule["uuids"].(map[string]interface{})
+				processTCPUDPRule(rule, ruleMap, uuids, &rules)
 			}
 		}
 	}
@@ -570,10 +604,9 @@
 	managed := d.Get("managed").(bool)
 	if managed && len(ruleMap) > 0 {
 		for uuid := range ruleMap {
-			// We need to create and add a dummy value to a schema.Set as the
+			// We need to create and add a dummy value to a list as the
 			// cidr_list is a required field and thus needs a value
-			cidrs := &schema.Set{F: schema.HashString}
-			cidrs.Add(uuid)
+			cidrs := []interface{}{uuid}
 
 			// Make a dummy rule to hold the unknown UUID
 			rule := map[string]interface{}{
@@ -582,14 +615,14 @@
 				"uuids":     map[string]interface{}{uuid: uuid},
 			}
 
-			// Add the dummy rule to the rules set
-			rules.Add(rule)
+			// Add the dummy rule to the rules list
+			rules = append(rules, rule)
 			log.Printf("[DEBUG] Added managed dummy rule for UUID %s", uuid)
 		}
 	}
 
-	if rules.Len() > 0 {
-		log.Printf("[DEBUG] Setting %d rules in state", rules.Len())
+	if len(rules) > 0 {
+		log.Printf("[DEBUG] Setting %d rules in state", len(rules))
 		if err := d.Set("rule", rules); err != nil {
 			log.Printf("[ERROR] Failed to set rule attribute: %v", err)
 			return err
@@ -609,40 +642,16 @@
 		return err
 	}
 
-	// Check if the rule set as a whole has changed
+	// Check if the rule list has changed
 	if d.HasChange("rule") {
 		o, n := d.GetChange("rule")
-		ors := o.(*schema.Set).Difference(n.(*schema.Set))
-		nrs := n.(*schema.Set).Difference(o.(*schema.Set))
+		oldRules := o.([]interface{})
+		newRules := n.([]interface{})
 
-		// We need to start with a rule set containing all the rules we
-		// already have and want to keep. Any rules that are not deleted
-		// correctly and any newly created rules, will be added to this
-		// set to make sure we end up in a consistent state
-		rules := o.(*schema.Set).Intersection(n.(*schema.Set))
-
-		// First loop through all the new rules and create (before destroy) them
-		if nrs.Len() > 0 {
-			err := createNetworkACLRules(d, meta, rules, nrs)
-
-			// We need to update this first to preserve the correct state
-			d.Set("rule", rules)
-
-			if err != nil {
-				return err
-			}
-		}
-
-		// Then loop through all the old rules and delete them
-		if ors.Len() > 0 {
-			err := deleteNetworkACLRules(d, meta, rules, ors)
-
-			// We need to update this first to preserve the correct state
-			d.Set("rule", rules)
-
-			if err != nil {
-				return err
-			}
+		log.Printf("[DEBUG] Rule list changed, performing efficient updates")
+		err := updateNetworkACLRules(d, meta, oldRules, newRules)
+		if err != nil {
+			return err
 		}
 	}
 
@@ -650,61 +659,21 @@
 }
 
 func resourceCloudStackNetworkACLRuleDelete(d *schema.ResourceData, meta interface{}) error {
-	// Create an empty rule set to hold all rules that where
-	// not deleted correctly
-	rules := resourceCloudStackNetworkACLRule().Schema["rule"].ZeroValue().(*schema.Set)
-
 	// Delete all rules
-	if ors := d.Get("rule").(*schema.Set); ors.Len() > 0 {
-		err := deleteNetworkACLRules(d, meta, rules, ors)
-
-		// We need to update this first to preserve the correct state
-		d.Set("rule", rules)
-
-		if err != nil {
-			return err
+	if ors := d.Get("rule").([]interface{}); len(ors) > 0 {
+		for _, rule := range ors {
+			ruleMap := rule.(map[string]interface{})
+			err := deleteNetworkACLRule(d, meta, ruleMap)
+			if err != nil {
+				log.Printf("[ERROR] Failed to delete rule: %v", err)
+				return err
+			}
 		}
 	}
 
 	return nil
 }
 
-func deleteNetworkACLRules(d *schema.ResourceData, meta interface{}, rules *schema.Set, ors *schema.Set) error {
-	var errs *multierror.Error
-
-	var wg sync.WaitGroup
-	wg.Add(ors.Len())
-
-	sem := make(chan struct{}, d.Get("parallelism").(int))
-	for _, rule := range ors.List() {
-		// Put a sleep here to avoid DoS'ing the API
-		time.Sleep(500 * time.Millisecond)
-
-		go func(rule map[string]interface{}) {
-			defer wg.Done()
-			sem <- struct{}{}
-
-			// Delete a single rule
-			err := deleteNetworkACLRule(d, meta, rule)
-
-			// If we have at least one UUID, we need to save the rule
-			if len(rule["uuids"].(map[string]interface{})) > 0 {
-				rules.Add(rule)
-			}
-
-			if err != nil {
-				errs = multierror.Append(errs, err)
-			}
-
-			<-sem
-		}(rule.(map[string]interface{}))
-	}
-
-	wg.Wait()
-
-	return errs.ErrorOrNil()
-}
-
 func deleteNetworkACLRule(d *schema.ResourceData, meta interface{}, rule map[string]interface{}) error {
 	cs := meta.(*cloudstack.CloudStackClient)
 	uuids := rule["uuids"].(map[string]interface{})
@@ -791,19 +760,26 @@
 		// No additional test are needed
 		log.Printf("[DEBUG] Protocol 'all' validated")
 	case "tcp", "udp":
-		if ports, ok := rule["ports"].(*schema.Set); ok {
-			log.Printf("[DEBUG] Found %d ports for TCP/UDP", ports.Len())
-			for _, port := range ports.List() {
-				m := splitPorts.FindStringSubmatch(port.(string))
-				if m == nil {
-					log.Printf("[ERROR] Invalid port format: %s", port.(string))
-					return fmt.Errorf(
-						"%q is not a valid port value. Valid options are '80' or '80-90'", port.(string))
-				}
+		// Check if deprecated ports field is used (not allowed for new configurations)
+		portsSet, hasPortsSet := rule["ports"].(*schema.Set)
+		portStr, hasPort := rule["port"].(string)
+
+		if hasPortsSet && portsSet.Len() > 0 {
+			log.Printf("[ERROR] Deprecated ports field used in new configuration")
+			return fmt.Errorf("The 'ports' field is deprecated. Use 'port' instead for new configurations.")
+		}
+
+		// Validate the new port field if used
+		if hasPort && portStr != "" {
+			log.Printf("[DEBUG] Found port for TCP/UDP: %s", portStr)
+			m := splitPorts.FindStringSubmatch(portStr)
+			if m == nil {
+				log.Printf("[ERROR] Invalid port format: %s", portStr)
+				return fmt.Errorf(
+					"%q is not a valid port value. Valid options are '80' or '80-90'", portStr)
 			}
 		} else {
-			log.Printf("[DEBUG] No ports specified for TCP/UDP, assuming empty set")
-			// Allow empty ports for TCP/UDP (your config has no ports)
+			log.Printf("[DEBUG] No port specified for TCP/UDP, allowing empty port")
 		}
 	default:
 		_, err := strconv.ParseInt(protocol, 0, 0)
@@ -869,3 +845,248 @@
 	log.Printf("[DEBUG] ACL list check result: count=%d", count)
 	return count > 0, nil
 }
+
+func updateNetworkACLRules(d *schema.ResourceData, meta interface{}, oldRules, newRules []interface{}) error {
+	cs := meta.(*cloudstack.CloudStackClient)
+	log.Printf("[DEBUG] Updating ACL rules: %d old rules, %d new rules", len(oldRules), len(newRules))
+
+	oldRuleMap := make(map[string]map[string]interface{})
+	newRuleMap := make(map[string]map[string]interface{})
+
+	for _, rule := range oldRules {
+		ruleMap := rule.(map[string]interface{})
+		key := createRuleKey(ruleMap)
+		oldRuleMap[key] = ruleMap
+		log.Printf("[DEBUG] Old rule key: %s", key)
+	}
+
+	for _, rule := range newRules {
+		ruleMap := rule.(map[string]interface{})
+		key := createRuleKey(ruleMap)
+		newRuleMap[key] = ruleMap
+		log.Printf("[DEBUG] New rule key: %s", key)
+	}
+
+	for key, oldRule := range oldRuleMap {
+		if _, exists := newRuleMap[key]; !exists {
+			log.Printf("[DEBUG] Deleting rule: %s", key)
+			err := deleteNetworkACLRule(d, meta, oldRule)
+			if err != nil {
+				return fmt.Errorf("failed to delete rule %s: %v", key, err)
+			}
+		}
+	}
+
+	var rulesToCreate []interface{}
+	for key, newRule := range newRuleMap {
+		if _, exists := oldRuleMap[key]; !exists {
+			log.Printf("[DEBUG] Creating new rule: %s", key)
+			rulesToCreate = append(rulesToCreate, newRule)
+		}
+	}
+
+	if len(rulesToCreate) > 0 {
+		var createdRules []interface{}
+		err := createNetworkACLRules(d, meta, &createdRules, rulesToCreate)
+		if err != nil {
+			return fmt.Errorf("failed to create new rules: %v", err)
+		}
+	}
+
+	for key, newRule := range newRuleMap {
+		if oldRule, exists := oldRuleMap[key]; exists {
+			if ruleNeedsUpdate(oldRule, newRule) {
+				log.Printf("[DEBUG] Updating rule: %s", key)
+				err := updateNetworkACLRule(cs, oldRule, newRule)
+				if err != nil {
+					return fmt.Errorf("failed to update rule %s: %v", key, err)
+				}
+			}
+		}
+	}
+
+	return nil
+}
+
+func createRuleKey(rule map[string]interface{}) string {
+	protocol := rule["protocol"].(string)
+	trafficType := rule["traffic_type"].(string)
+
+	if protocol == "icmp" {
+		icmpType := rule["icmp_type"].(int)
+		icmpCode := rule["icmp_code"].(int)
+		return fmt.Sprintf("%s-%s-icmp-%d-%d", protocol, trafficType, icmpType, icmpCode)
+	}
+
+	if protocol == "all" {
+		return fmt.Sprintf("%s-%s-all", protocol, trafficType)
+	}
+
+	if protocol == "tcp" || protocol == "udp" {
+		portStr, hasPort := rule["port"].(string)
+		if hasPort && portStr != "" {
+			return fmt.Sprintf("%s-%s-port-%s", protocol, trafficType, portStr)
+		} else {
+			return fmt.Sprintf("%s-%s-noport", protocol, trafficType)
+		}
+	}
+
+	// For numeric protocols
+	return fmt.Sprintf("%s-%s", protocol, trafficType)
+}
+
+func ruleNeedsUpdate(oldRule, newRule map[string]interface{}) bool {
+	if oldRule["action"].(string) != newRule["action"].(string) {
+		log.Printf("[DEBUG] Action changed: %s -> %s", oldRule["action"].(string), newRule["action"].(string))
+		return true
+	}
+
+	if oldRule["protocol"].(string) != newRule["protocol"].(string) {
+		log.Printf("[DEBUG] Protocol changed: %s -> %s", oldRule["protocol"].(string), newRule["protocol"].(string))
+		return true
+	}
+
+	if oldRule["traffic_type"].(string) != newRule["traffic_type"].(string) {
+		log.Printf("[DEBUG] Traffic type changed: %s -> %s", oldRule["traffic_type"].(string), newRule["traffic_type"].(string))
+		return true
+	}
+
+	// Check rule_number
+	oldRuleNum, oldHasRuleNum := oldRule["rule_number"].(int)
+	newRuleNum, newHasRuleNum := newRule["rule_number"].(int)
+	if oldHasRuleNum != newHasRuleNum || (oldHasRuleNum && newHasRuleNum && oldRuleNum != newRuleNum) {
+		log.Printf("[DEBUG] Rule number changed: %d -> %d", oldRuleNum, newRuleNum)
+		return true
+	}
+
+	oldDesc, oldHasDesc := oldRule["description"].(string)
+	newDesc, newHasDesc := newRule["description"].(string)
+	if oldHasDesc != newHasDesc || (oldHasDesc && newHasDesc && oldDesc != newDesc) {
+		log.Printf("[DEBUG] Description changed: %s -> %s", oldDesc, newDesc)
+		return true
+	}
+
+	protocol := newRule["protocol"].(string)
+	switch protocol {
+	case "icmp":
+		if oldRule["icmp_type"].(int) != newRule["icmp_type"].(int) {
+			log.Printf("[DEBUG] ICMP type changed: %d -> %d", oldRule["icmp_type"].(int), newRule["icmp_type"].(int))
+			return true
+		}
+		if oldRule["icmp_code"].(int) != newRule["icmp_code"].(int) {
+			log.Printf("[DEBUG] ICMP code changed: %d -> %d", oldRule["icmp_code"].(int), newRule["icmp_code"].(int))
+			return true
+		}
+	case "tcp", "udp":
+		oldPort, oldHasPort := oldRule["port"].(string)
+		newPort, newHasPort := newRule["port"].(string)
+		if oldHasPort != newHasPort || (oldHasPort && newHasPort && oldPort != newPort) {
+			log.Printf("[DEBUG] Port changed: %s -> %s", oldPort, newPort)
+			return true
+		}
+	}
+
+	oldCidrs := oldRule["cidr_list"].([]interface{})
+	newCidrs := newRule["cidr_list"].([]interface{})
+	if len(oldCidrs) != len(newCidrs) {
+		log.Printf("[DEBUG] CIDR list length changed: %d -> %d", len(oldCidrs), len(newCidrs))
+		return true
+	}
+
+	oldCidrStrs := make([]string, len(oldCidrs))
+	newCidrStrs := make([]string, len(newCidrs))
+	for i, cidr := range oldCidrs {
+		oldCidrStrs[i] = cidr.(string)
+	}
+	for i, cidr := range newCidrs {
+		newCidrStrs[i] = cidr.(string)
+	}
+
+	sort.Strings(oldCidrStrs)
+	sort.Strings(newCidrStrs)
+
+	for i, oldCidr := range oldCidrStrs {
+		if oldCidr != newCidrStrs[i] {
+			log.Printf("[DEBUG] CIDR changed at index %d: %s -> %s", i, oldCidr, newCidrStrs[i])
+			return true
+		}
+	}
+
+	return false
+}
+
+func updateNetworkACLRule(cs *cloudstack.CloudStackClient, oldRule, newRule map[string]interface{}) error {
+	uuids := oldRule["uuids"].(map[string]interface{})
+
+	for key, uuid := range uuids {
+		if key == "%" {
+			continue
+		}
+
+		log.Printf("[DEBUG] Updating ACL rule with UUID: %s", uuid.(string))
+		p := cs.NetworkACL.NewUpdateNetworkACLItemParams(uuid.(string))
+
+		p.SetAction(newRule["action"].(string))
+
+		var cidrList []string
+		for _, cidr := range newRule["cidr_list"].([]interface{}) {
+			cidrList = append(cidrList, cidr.(string))
+		}
+		p.SetCidrlist(cidrList)
+
+		if desc, ok := newRule["description"].(string); ok && desc != "" {
+			p.SetReason(desc)
+		}
+
+		p.SetProtocol(newRule["protocol"].(string))
+
+		p.SetTraffictype(newRule["traffic_type"].(string))
+
+		// Set rule number if provided and non-zero
+		if ruleNum, ok := newRule["rule_number"].(int); ok && ruleNum > 0 {
+			p.SetNumber(ruleNum)
+			log.Printf("[DEBUG] Set rule_number=%d", ruleNum)
+		}
+
+		protocol := newRule["protocol"].(string)
+		switch protocol {
+		case "icmp":
+			if icmpType, ok := newRule["icmp_type"].(int); ok {
+				p.SetIcmptype(icmpType)
+				log.Printf("[DEBUG] Set icmp_type=%d", icmpType)
+			}
+			if icmpCode, ok := newRule["icmp_code"].(int); ok {
+				p.SetIcmpcode(icmpCode)
+				log.Printf("[DEBUG] Set icmp_code=%d", icmpCode)
+			}
+		case "tcp", "udp":
+			if portStr, hasPort := newRule["port"].(string); hasPort && portStr != "" {
+				m := splitPorts.FindStringSubmatch(portStr)
+				if m != nil {
+					startPort, err := strconv.Atoi(m[1])
+					if err == nil {
+						endPort := startPort
+						if m[2] != "" {
+							if ep, err := strconv.Atoi(m[2]); err == nil {
+								endPort = ep
+							}
+						}
+						p.SetStartport(startPort)
+						p.SetEndport(endPort)
+						log.Printf("[DEBUG] Set port start=%d, end=%d", startPort, endPort)
+					}
+				}
+			}
+		}
+
+		_, err := cs.NetworkACL.UpdateNetworkACLItem(p)
+		if err != nil {
+			log.Printf("[ERROR] Failed to update ACL rule %s: %v", uuid.(string), err)
+			return err
+		}
+
+		log.Printf("[DEBUG] Successfully updated ACL rule %s", uuid.(string))
+	}
+
+	return nil
+}
diff --git a/cloudstack/resource_cloudstack_network_acl_rule_test.go b/cloudstack/resource_cloudstack_network_acl_rule_test.go
index a7dad42..e894a8e 100644
--- a/cloudstack/resource_cloudstack_network_acl_rule_test.go
+++ b/cloudstack/resource_cloudstack_network_acl_rule_test.go
@@ -41,43 +41,43 @@
 
 					testAccCheckCloudStackNetworkACLRulesExist("cloudstack_network_acl.foo"),
 					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.#", "3"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.0.action", "allow"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.0.cidr_list.0", "172.16.100.0/24"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.0.protocol", "tcp"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.0.ports.#", "2"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.0.ports.1", "80"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.0.ports.0", "443"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.0.traffic_type", "ingress"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.0.description", "Allow HTTP and HTTPS"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.1.rule_number", "20"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.1.action", "allow"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.1.cidr_list.#", "1"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.1.cidr_list.0", "172.18.100.0/24"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.1.icmp_code", "-1"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.1.icmp_type", "-1"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.1.traffic_type", "ingress"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.1.description", "Allow ICMP traffic"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.2.rule_number", "10"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.2.description", "Allow all traffic"),
+						"cloudstack_network_acl_rule.foo", "rule.#", "4"),
+					// Don't rely on specific rule ordering as TypeSet doesn't guarantee order
+					// Just check that we have the expected rules with their attributes
+					resource.TestCheckTypeSetElemNestedAttrs(
+						"cloudstack_network_acl_rule.foo", "rule.*", map[string]string{
+							"rule_number":  "10",
+							"action":       "allow",
+							"protocol":     "all",
+							"traffic_type": "ingress",
+							"description":  "Allow all traffic",
+						}),
+					resource.TestCheckTypeSetElemNestedAttrs(
+						"cloudstack_network_acl_rule.foo", "rule.*", map[string]string{
+							"rule_number":  "20",
+							"action":       "allow",
+							"protocol":     "icmp",
+							"icmp_type":    "-1",
+							"icmp_code":    "-1",
+							"traffic_type": "ingress",
+							"description":  "Allow ICMP traffic",
+						}),
+					resource.TestCheckTypeSetElemNestedAttrs(
+						"cloudstack_network_acl_rule.foo", "rule.*", map[string]string{
+							"action":       "allow",
+							"protocol":     "tcp",
+							"port":         "80",
+							"traffic_type": "ingress",
+							"description":  "Allow HTTP",
+						}),
+					resource.TestCheckTypeSetElemNestedAttrs(
+						"cloudstack_network_acl_rule.foo", "rule.*", map[string]string{
+							"action":       "allow",
+							"protocol":     "tcp",
+							"port":         "443",
+							"traffic_type": "ingress",
+							"description":  "Allow HTTPS",
+						}),
 				),
 			},
 		},
@@ -95,35 +95,42 @@
 				Check: resource.ComposeTestCheckFunc(
 					testAccCheckCloudStackNetworkACLRulesExist("cloudstack_network_acl.foo"),
 					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.#", "3"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.0.action", "allow"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.0.cidr_list.0", "172.16.100.0/24"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.0.protocol", "tcp"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.0.ports.#", "2"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.0.ports.1", "80"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.0.ports.0", "443"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.0.traffic_type", "ingress"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.1.rule_number", "20"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.1.action", "allow"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.1.cidr_list.#", "1"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.1.cidr_list.0", "172.18.100.0/24"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.1.icmp_code", "-1"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.1.icmp_type", "-1"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.1.traffic_type", "ingress"),
+						"cloudstack_network_acl_rule.foo", "rule.#", "4"),
+					// Don't rely on specific rule ordering as TypeSet doesn't guarantee order
+					resource.TestCheckTypeSetElemNestedAttrs(
+						"cloudstack_network_acl_rule.foo", "rule.*", map[string]string{
+							"rule_number":  "10",
+							"action":       "allow",
+							"protocol":     "all",
+							"traffic_type": "ingress",
+							"description":  "Allow all traffic",
+						}),
+					resource.TestCheckTypeSetElemNestedAttrs(
+						"cloudstack_network_acl_rule.foo", "rule.*", map[string]string{
+							"rule_number":  "20",
+							"action":       "allow",
+							"protocol":     "icmp",
+							"icmp_type":    "-1",
+							"icmp_code":    "-1",
+							"traffic_type": "ingress",
+							"description":  "Allow ICMP traffic",
+						}),
+					resource.TestCheckTypeSetElemNestedAttrs(
+						"cloudstack_network_acl_rule.foo", "rule.*", map[string]string{
+							"action":       "allow",
+							"protocol":     "tcp",
+							"port":         "80",
+							"traffic_type": "ingress",
+							"description":  "Allow HTTP",
+						}),
+					resource.TestCheckTypeSetElemNestedAttrs(
+						"cloudstack_network_acl_rule.foo", "rule.*", map[string]string{
+							"action":       "allow",
+							"protocol":     "tcp",
+							"port":         "443",
+							"traffic_type": "ingress",
+							"description":  "Allow HTTPS",
+						}),
 				),
 			},
 
@@ -132,49 +139,53 @@
 				Check: resource.ComposeTestCheckFunc(
 					testAccCheckCloudStackNetworkACLRulesExist("cloudstack_network_acl.foo"),
 					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.#", "4"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.1.action", "deny"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.1.cidr_list.0", "10.0.0.0/24"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.1.protocol", "tcp"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.1.ports.#", "2"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.1.ports.0", "1000-2000"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.1.ports.1", "80"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.1.traffic_type", "egress"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.2.action", "deny"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.2.cidr_list.#", "2"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.2.cidr_list.1", "172.18.101.0/24"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.2.cidr_list.0", "172.18.100.0/24"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.2.icmp_code", "-1"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.2.icmp_type", "-1"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.2.traffic_type", "ingress"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.0.action", "allow"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.0.cidr_list.0", "172.18.100.0/24"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.0.protocol", "tcp"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.0.ports.#", "2"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.0.ports.1", "80"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.0.ports.0", "443"),
-					resource.TestCheckResourceAttr(
-						"cloudstack_network_acl_rule.foo", "rule.0.traffic_type", "ingress"),
+						"cloudstack_network_acl_rule.foo", "rule.#", "6"),
+					// Check for the expected rules using TypeSet elem matching
+					resource.TestCheckTypeSetElemNestedAttrs(
+						"cloudstack_network_acl_rule.foo", "rule.*", map[string]string{
+							"action":       "deny",
+							"protocol":     "all",
+							"traffic_type": "ingress",
+						}),
+					resource.TestCheckTypeSetElemNestedAttrs(
+						"cloudstack_network_acl_rule.foo", "rule.*", map[string]string{
+							"action":       "deny",
+							"protocol":     "icmp",
+							"icmp_type":    "-1",
+							"icmp_code":    "-1",
+							"traffic_type": "ingress",
+							"description":  "Deny ICMP traffic",
+						}),
+					resource.TestCheckTypeSetElemNestedAttrs(
+						"cloudstack_network_acl_rule.foo", "rule.*", map[string]string{
+							"action":       "allow",
+							"protocol":     "tcp",
+							"port":         "80",
+							"traffic_type": "ingress",
+						}),
+					resource.TestCheckTypeSetElemNestedAttrs(
+						"cloudstack_network_acl_rule.foo", "rule.*", map[string]string{
+							"action":       "allow",
+							"protocol":     "tcp",
+							"port":         "443",
+							"traffic_type": "ingress",
+						}),
+					resource.TestCheckTypeSetElemNestedAttrs(
+						"cloudstack_network_acl_rule.foo", "rule.*", map[string]string{
+							"action":       "deny",
+							"protocol":     "tcp",
+							"port":         "80",
+							"traffic_type": "egress",
+							"description":  "Deny specific TCP ports",
+						}),
+					resource.TestCheckTypeSetElemNestedAttrs(
+						"cloudstack_network_acl_rule.foo", "rule.*", map[string]string{
+							"action":       "deny",
+							"protocol":     "tcp",
+							"port":         "1000-2000",
+							"traffic_type": "egress",
+							"description":  "Deny specific TCP ports",
+						}),
 				),
 			},
 		},
@@ -280,9 +291,17 @@
   rule {
     cidr_list = ["172.16.100.0/24"]
     protocol = "tcp"
-    ports = ["80", "443"]
+    port = "80"
     traffic_type = "ingress"
-	description = "Allow HTTP and HTTPS"
+	description = "Allow HTTP"
+  }
+
+  rule {
+    cidr_list = ["172.16.100.0/24"]
+    protocol = "tcp"
+    port = "443"
+    traffic_type = "ingress"
+	description = "Allow HTTPS"
   }
 }`
 
@@ -324,7 +343,14 @@
 	action = "allow"
     cidr_list = ["172.18.100.0/24"]
     protocol = "tcp"
-    ports = ["80", "443"]
+    port = "80"
+    traffic_type = "ingress"
+  }
+
+  rule {
+    cidr_list = ["172.16.100.0/24"]
+    protocol = "tcp"
+    port = "443"
     traffic_type = "ingress"
   }
 
@@ -332,7 +358,16 @@
 	action = "deny"
     cidr_list = ["10.0.0.0/24"]
     protocol = "tcp"
-    ports = ["80", "1000-2000"]
+    port = "80"
+    traffic_type = "egress"
+	description = "Deny specific TCP ports"
+  }
+
+  rule {
+	action = "deny"
+    cidr_list = ["10.0.0.0/24"]
+    protocol = "tcp"
+    port = "1000-2000"
     traffic_type = "egress"
 	description = "Deny specific TCP ports"
   }
diff --git a/website/docs/r/network_acl_rule.html.markdown b/website/docs/r/network_acl_rule.html.markdown
index dec19ea..55bc6c9 100644
--- a/website/docs/r/network_acl_rule.html.markdown
+++ b/website/docs/r/network_acl_rule.html.markdown
@@ -12,6 +12,8 @@
 
 ## Example Usage
 
+### Basic Example with Port
+
 ```hcl
 resource "cloudstack_network_acl_rule" "default" {
   acl_id = "f3843ce0-334c-4586-bbd3-0c2e2bc946c6"
@@ -20,12 +22,112 @@
     action       = "allow"
     cidr_list    = ["10.0.0.0/8"]
     protocol     = "tcp"
-    ports        = ["80", "1000-2000"]
+    port         = "80"
     traffic_type = "ingress"
   }
 }
 ```
 
+### Example with Port Range
+
+```hcl
+resource "cloudstack_network_acl_rule" "port_range" {
+  acl_id = "f3843ce0-334c-4586-bbd3-0c2e2bc946c6"
+
+  rule {
+    action       = "allow" 
+    cidr_list    = ["192.168.1.0/24"]
+    protocol     = "tcp"
+    port         = "8000-8010"
+    traffic_type = "ingress"
+  }
+}
+```
+
+### Example with No Port (Allow All Ports)
+
+```hcl
+resource "cloudstack_network_acl_rule" "all_ports" {
+  acl_id = "f3843ce0-334c-4586-bbd3-0c2e2bc946c6"
+
+  rule {
+    action       = "allow"
+    cidr_list    = ["10.0.0.0/16"]
+    protocol     = "tcp"
+    traffic_type = "ingress"
+    description  = "Allow all TCP traffic from internal network"
+  }
+}
+```
+
+### Example with ICMP
+
+```hcl
+resource "cloudstack_network_acl_rule" "icmp" {
+  acl_id = "f3843ce0-334c-4586-bbd3-0c2e2bc946c6"
+
+  rule {
+    action       = "allow"
+    cidr_list    = ["0.0.0.0/0"]
+    protocol     = "icmp"
+    icmp_type    = 8
+    icmp_code    = 0
+    traffic_type = "ingress"
+    description  = "Allow ping"
+  }
+}
+```
+
+### Complete Example with Multiple Rules
+
+```hcl
+resource "cloudstack_network_acl_rule" "web_server" {
+  acl_id = "f3843ce0-334c-4586-bbd3-0c2e2bc946c6"
+
+  # HTTP traffic
+  rule {
+    rule_number  = 10
+    action       = "allow"
+    cidr_list    = ["0.0.0.0/0"]
+    protocol     = "tcp"
+    port         = "80"
+    traffic_type = "ingress"
+    description  = "Allow HTTP"
+  }
+
+  # HTTPS traffic
+  rule {
+    rule_number  = 20
+    action       = "allow"
+    cidr_list    = ["0.0.0.0/0"]
+    protocol     = "tcp"
+    port         = "443"
+    traffic_type = "ingress"
+    description  = "Allow HTTPS"
+  }
+
+  # SSH from management network
+  rule {
+    rule_number  = 30
+    action       = "allow"
+    cidr_list    = ["192.168.100.0/24"]
+    protocol     = "tcp"
+    port         = "22"
+    traffic_type = "ingress"
+    description  = "Allow SSH from management"
+  }
+
+  # Allow all outbound traffic
+  rule {
+    rule_number  = 100
+    action       = "allow"
+    cidr_list    = ["0.0.0.0/0"]
+    protocol     = "tcp"
+    traffic_type = "egress"
+    description  = "Allow all outbound TCP"
+  }
+}
+
 ## Argument Reference
 
 The following arguments are supported:
@@ -64,8 +166,15 @@
 * `icmp_code` - (Optional) The ICMP code to allow, or `-1` to allow `any`. This
     can only be specified if the protocol is ICMP. (defaults 0)
 
-* `ports` - (Optional) List of ports and/or port ranges to allow. This can only
-    be specified if the protocol is TCP, UDP, ALL or a valid protocol number.
+* `port` - (Optional) Port or port range to allow. This can only be specified if 
+    the protocol is TCP, UDP, ALL or a valid protocol number. Valid formats are:
+    - Single port: `"80"`
+    - Port range: `"8000-8010"`
+    - If not specified for TCP/UDP, allows all ports for that protocol
+
+* `ports` - (Optional) **DEPRECATED**: Use `port` instead. List of ports and/or 
+    port ranges to allow. This field is deprecated and will be removed in a future 
+    version. For backward compatibility only.
 
 * `traffic_type` - (Optional) The traffic type for the rule. Valid options are:
     `ingress` or `egress` (defaults ingress).