diff --git a/.gitignore b/.gitignore index b6b0f366..2c88f78d 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,5 @@ website/vendor !command/test-fixtures/**/.terraform/ .terraform.lock.hcl provider.tf +.worktrees/ +.env diff --git a/cloudstack/resource_cloudstack_egress_firewall.go b/cloudstack/resource_cloudstack_egress_firewall.go index e2a83e4c..beec0bd0 100644 --- a/cloudstack/resource_cloudstack_egress_firewall.go +++ b/cloudstack/resource_cloudstack_egress_firewall.go @@ -70,6 +70,13 @@ func resourceCloudStackEgressFirewall() *schema.Resource { Set: schema.HashString, }, + "dest_cidr_list": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Set: schema.HashString, + }, + "protocol": { Type: schema.TypeString, Required: true, @@ -194,6 +201,15 @@ func createEgressFirewallRule(d *schema.ResourceData, meta interface{}, rule map p.SetCidrlist(cidrList) } + // Set the destination CIDR list + if rs, ok := rule["dest_cidr_list"].(*schema.Set); ok && rs.Len() > 0 { + var destCidrList []string + for _, cidr := range rule["dest_cidr_list"].(*schema.Set).List() { + destCidrList = append(destCidrList, cidr.(string)) + } + p.SetDestcidrlist(destCidrList) + } + // If the protocol is ICMP set the needed ICMP parameters if rule["protocol"].(string) == "icmp" { p.SetIcmptype(rule["icmp_type"].(int)) @@ -264,6 +280,19 @@ func createEgressFirewallRule(d *schema.ResourceData, meta interface{}, rule map return nil } +// cidrSetFromList builds a schema.Set of CIDRs from a comma-separated list, +// returning an empty set when the list is empty. +func cidrSetFromList(list string) *schema.Set { + set := &schema.Set{F: schema.HashString} + if list == "" { + return set + } + for _, cidr := range strings.Split(list, ",") { + set.Add(cidr) + } + return set +} + func resourceCloudStackEgressFirewallRead(d *schema.ResourceData, meta interface{}) error { cs := meta.(*cloudstack.CloudStackClient) @@ -297,7 +326,10 @@ func resourceCloudStackEgressFirewallRead(d *schema.ResourceData, meta interface rule := rule.(map[string]interface{}) uuids := rule["uuids"].(map[string]interface{}) - if rule["protocol"].(string) == "icmp" { + protocol := strings.ToLower(rule["protocol"].(string)) + + switch protocol { + case "icmp": id, ok := uuids["icmp"] if !ok { continue @@ -313,64 +345,14 @@ func resourceCloudStackEgressFirewallRead(d *schema.ResourceData, meta interface // 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["protocol"] = r.Protocol rule["icmp_type"] = r.Icmptype rule["icmp_code"] = r.Icmpcode - rule["cidr_list"] = cidrs + rule["cidr_list"] = cidrSetFromList(r.Cidrlist) + rule["dest_cidr_list"] = cidrSetFromList(r.Destcidrlist) rules.Add(rule) - } - - // If protocol is not ICMP, loop through all ports - if rule["protocol"].(string) != "icmp" && strings.ToLower(rule["protocol"].(string)) != "all" { - if ps := rule["ports"].(*schema.Set); ps.Len() > 0 { - - // Create an empty schema.Set to hold all ports - ports := &schema.Set{F: schema.HashString} - - // Loop through all ports and retrieve their info - for _, port := range ps.List() { - id, ok := uuids[port.(string)] - if !ok { - continue - } - - // Get the rule - r, ok := ruleMap[id.(string)] - if !ok { - 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["protocol"] = r.Protocol - rule["cidr_list"] = cidrs - ports.Add(port) - } - - // 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) - } - } - } - if strings.ToLower(rule["protocol"].(string)) == "all" { + case "all": id, ok := uuids["all"] if !ok { continue @@ -386,18 +368,44 @@ func resourceCloudStackEgressFirewallRead(d *schema.ResourceData, meta interface // Delete the known rule so only unknown rules remain in the ruleMap delete(ruleMap, id.(string)) - // Create a set with all CIDR's - if _, ok := rule["cidr_list"]; ok { - cidrs := &schema.Set{F: schema.HashString} - for _, cidr := range strings.Split(r.Cidrlist, ",") { - cidrs.Add(cidr) - } - rule["cidr_list"] = cidrs - } - // Update the values rule["protocol"] = r.Protocol + rule["cidr_list"] = cidrSetFromList(r.Cidrlist) + rule["dest_cidr_list"] = cidrSetFromList(r.Destcidrlist) rules.Add(rule) + default: + // Create an empty schema.Set to hold all ports + ports := &schema.Set{F: schema.HashString} + + // Loop through all ports and retrieve their info + for _, port := range rule["ports"].(*schema.Set).List() { + id, ok := uuids[port.(string)] + if !ok { + continue + } + + // Get the rule + r, ok := ruleMap[id.(string)] + if !ok { + delete(uuids, port.(string)) + continue + } + + // Delete the known rule so only unknown rules remain in the ruleMap + delete(ruleMap, id.(string)) + + // Update the values + rule["protocol"] = r.Protocol + rule["cidr_list"] = cidrSetFromList(r.Cidrlist) + rule["dest_cidr_list"] = cidrSetFromList(r.Destcidrlist) + ports.Add(port) + } + + // 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) + } } } } diff --git a/website/docs/r/egress_firewall.html.markdown b/website/docs/r/egress_firewall.html.markdown index 10badd17..dde74062 100644 --- a/website/docs/r/egress_firewall.html.markdown +++ b/website/docs/r/egress_firewall.html.markdown @@ -17,9 +17,10 @@ resource "cloudstack_egress_firewall" "default" { network_id = "6eb22f91-7454-4107-89f4-36afcdf33021" rule { - cidr_list = ["10.0.0.0/8"] - protocol = "tcp" - ports = ["80", "1000-2000"] + cidr_list = ["10.0.0.0/8"] + dest_cidr_list = ["192.168.0.0/16"] + protocol = "tcp" + ports = ["80", "1000-2000"] } } ``` @@ -43,10 +44,12 @@ The following arguments are supported: The `rule` block supports: -* `cidr_list` - (Required) A CIDR list to allow access to the given ports. +* `cidr_list` - (Optional) A CIDR list to allow access to the given ports. + +* `dest_cidr_list` - (Optional) A CIDR list to restrict the destination of egress traffic. * `protocol` - (Required) The name of the protocol to allow. Valid options are: - `tcp`, `udp` and `icmp`. + `tcp`, `udp`, `icmp` and `all`. * `icmp_type` - (Optional) The ICMP type to allow. This can only be specified if the protocol is ICMP.