diff --git a/docs/data-sources/networking_secgroup_rules.md b/docs/data-sources/networking_secgroup_rules.md new file mode 100644 index 0000000000..6c46fbb33f --- /dev/null +++ b/docs/data-sources/networking_secgroup_rules.md @@ -0,0 +1,91 @@ +--- +subcategory: "Virtual Private Cloud (VPC)" +--- + +# huaweicloud_networking_secgroup_rules + +Use this data source to get the list of the available HuaweiCloud security group rules. + +## Example Usage + +```hcl +variable "security_group_id" {} + +data "huaweicloud_networking_secgroup_rules" "test" { + security_group_id = var.security_group_id +} +``` + +## Argument Reference + +The following arguments are supported: + +* `region` - (Optional, String) Specifies the region in which to obtain the security group rules. If omitted, the + provider-level region will be used. + +* `security_group_id` - (Required, String) Specifies the security group ID that the rule should belong to. + +* `rule_id` - (Optional, String) Specifies the security group rule ID used for query. + +* `protocol` - (Optional, String) Specifies the security group rule protocol type used for query. + The value can be **tcp**, **udp**, **icmp**, **icmpv6** or IP protocol number, if empty, it indicates support for + all protocols. + +* `description` - (Optional, String) Specifies the security group rule description used for query. + +* `remote_group_id` - (Optional, String) Specifies the remote security group ID used for query. + +* `direction` - (Optional, String) Specifies the direction of the security group rule used for query. + The valid values are as follows: + + **ingress** + + **egress** + +* `action` - (Optional, String) Specifies the effective policy of the security group rule used for query. + The valid values are as follows: + + **allow** + + **deny** + +## Attribute Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The data source ID in UUID format. + +* `rules` - All security group rules that match the filter parameters. + The [rules](#secgroup_rules) structure is documented below. + + +The `rules` block supports: + +* `id` - The ID of the security group rule. + +* `description` - The description of the security group rule. + +* `security_group_id` - The security group ID that the rule should belong to. + +* `direction` - The direction of the security group rule. + +* `protocol` - The security group rule protocol type. + +* `ethertype` - The security group rule IP address protocol type. The value can be **IPv4** or **IPv6**. + +* `ports` - The range of port values for security group rule. Which supports single port (80), continuous port (1-30) + and discontinuous port (22, 3389, 80). + +* `action` - The effective policy of the security group rule. + +* `priority` - The priority of security group rule. The valid value ranges from `1` to `100`, `1` represents the + highest priority. + +* `remote_group_id` - The remote security group ID. + This field is mutually exclusive with `remote_ip_prefix` and `remote_address_group_id`. + +* `remote_ip_prefix` - The remote IP address. The value can be in the CIDR format or IP addresses. + This field is mutually exclusive with `remote_group_id` and `remote_address_group_id`. + +* `remote_address_group_id` - The remote address group ID. + This field is mutually exclusive with `remote_group_id` and `remote_ip_prefix`. + +* `created_at` - The creation time, in UTC format. + +* `updated_at` - The latest update time, in UTC format. diff --git a/huaweicloud/provider.go b/huaweicloud/provider.go index c46c554999..eb73e710cc 100644 --- a/huaweicloud/provider.go +++ b/huaweicloud/provider.go @@ -565,9 +565,10 @@ func Provider() *schema.Provider { "huaweicloud_nat_private_gateways": nat.DataSourcePrivateGateways(), "huaweicloud_nat_private_transit_ips": nat.DataSourcePrivateTransitIps(), - "huaweicloud_networking_port": vpc.DataSourceNetworkingPortV2(), - "huaweicloud_networking_secgroup": vpc.DataSourceNetworkingSecGroup(), - "huaweicloud_networking_secgroups": vpc.DataSourceNetworkingSecGroups(), + "huaweicloud_networking_port": vpc.DataSourceNetworkingPortV2(), + "huaweicloud_networking_secgroup": vpc.DataSourceNetworkingSecGroup(), + "huaweicloud_networking_secgroups": vpc.DataSourceNetworkingSecGroups(), + "huaweicloud_networking_secgroup_rules": vpc.DataSourceNetworkingSecGroupRules(), "huaweicloud_mapreduce_versions": mrs.DataSourceMrsVersions(), diff --git a/huaweicloud/services/acceptance/vpc/data_source_huaweicloud_networking_secgroup_rules_test.go b/huaweicloud/services/acceptance/vpc/data_source_huaweicloud_networking_secgroup_rules_test.go new file mode 100644 index 0000000000..63aecd02db --- /dev/null +++ b/huaweicloud/services/acceptance/vpc/data_source_huaweicloud_networking_secgroup_rules_test.go @@ -0,0 +1,107 @@ +package vpc + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/services/acceptance" +) + +func TestAccNetworkingSecGroupRulesDataSource_basic(t *testing.T) { + var ( + dataSourceName = "data.huaweicloud_networking_secgroup_rules.test" + baseConfig = testAccNetworkingSecGroupRulesDataSource_base() + dc = acceptance.InitDataSourceCheck(dataSourceName) + ) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acceptance.TestAccPreCheck(t) + }, + ProviderFactories: acceptance.TestAccProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccNetworkingSecGroupRulesDataSource_basic(baseConfig), + Check: resource.ComposeTestCheckFunc( + dc.CheckResourceExists(), + resource.TestCheckOutput("is_rule_id_filter_useful", "true"), + resource.TestCheckOutput("is_direction_filter_useful", "true"), + resource.TestCheckOutput("is_action_filter_useful", "true"), + ), + }, + }, + }) +} + +func testAccNetworkingSecGroupRulesDataSource_base() string { + name := acceptance.RandomAccResourceNameWithDash() + + return fmt.Sprintf(` +resource "huaweicloud_networking_secgroup" "test" { + name = "%s-secgroup" + description = "terraform security group rule acceptance test" +} + +resource "huaweicloud_networking_secgroup_rule" "test" { + security_group_id = huaweicloud_networking_secgroup.test.id + description = "description test" + direction = "ingress" + ethertype = "IPv4" + ports = 80 + protocol = "tcp" + action = "allow" + remote_ip_prefix = "0.0.0.0/0" +} +`, name) +} + +func testAccNetworkingSecGroupRulesDataSource_basic(baseConfig string) string { + return fmt.Sprintf(` +%s + +data "huaweicloud_networking_secgroup_rules" "test" { + security_group_id = huaweicloud_networking_secgroup.test.id +} + +locals { + rule_id = data.huaweicloud_networking_secgroup_rules.test.rules[0].id +} +data "huaweicloud_networking_secgroup_rules" "rule_id_filter" { + security_group_id = huaweicloud_networking_secgroup.test.id + rule_id = local.rule_id +} +output "is_rule_id_filter_useful" { + value = length(data.huaweicloud_networking_secgroup_rules.rule_id_filter.rules) > 0 && alltrue( + [for v in data.huaweicloud_networking_secgroup_rules.rule_id_filter.rules[*].id : v == local.rule_id] + ) +} + +locals { + direction = data.huaweicloud_networking_secgroup_rules.test.rules[0].direction +} +data "huaweicloud_networking_secgroup_rules" "direction_filter" { + security_group_id = huaweicloud_networking_secgroup.test.id + direction = local.direction +} +output "is_direction_filter_useful" { + value = length(data.huaweicloud_networking_secgroup_rules.direction_filter.rules) > 0 && alltrue( + [for v in data.huaweicloud_networking_secgroup_rules.direction_filter.rules[*].direction : v == local.direction] + ) +} + +locals { + action = data.huaweicloud_networking_secgroup_rules.test.rules[0].action +} +data "huaweicloud_networking_secgroup_rules" "action_filter" { + security_group_id = huaweicloud_networking_secgroup.test.id + action = local.action +} +output "is_action_filter_useful" { + value = length(data.huaweicloud_networking_secgroup_rules.action_filter.rules) > 0 && alltrue( + [for v in data.huaweicloud_networking_secgroup_rules.action_filter.rules[*].action : v == local.action] + ) +} +`, baseConfig) +} diff --git a/huaweicloud/services/vpc/data_source_huaweicloud_networking_secgroup_rules.go b/huaweicloud/services/vpc/data_source_huaweicloud_networking_secgroup_rules.go new file mode 100644 index 0000000000..a5d720d2a2 --- /dev/null +++ b/huaweicloud/services/vpc/data_source_huaweicloud_networking_secgroup_rules.go @@ -0,0 +1,181 @@ +package vpc + +import ( + "context" + + "github.com/hashicorp/go-multierror" + "github.com/hashicorp/go-uuid" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + v3rules "github.com/chnsz/golangsdk/openstack/networking/v3/security/rules" + + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/common" + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/config" +) + +// API: VPC GET /v3/{project_id}/vpc/security-group-rules +func DataSourceNetworkingSecGroupRules() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceNetworkingSecGroupRulesRead, + + Schema: map[string]*schema.Schema{ + "region": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "security_group_id": { + Type: schema.TypeString, + Required: true, + }, + "rule_id": { + Type: schema.TypeString, + Optional: true, + }, + "protocol": { + Type: schema.TypeString, + Optional: true, + }, + "description": { + Type: schema.TypeString, + Optional: true, + }, + "remote_group_id": { + Type: schema.TypeString, + Optional: true, + }, + "direction": { + Type: schema.TypeString, + Optional: true, + }, + "action": { + Type: schema.TypeString, + Optional: true, + }, + "rules": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "security_group_id": { + Type: schema.TypeString, + Computed: true, + }, + "direction": { + Type: schema.TypeString, + Computed: true, + }, + "protocol": { + Type: schema.TypeString, + Computed: true, + }, + "ethertype": { + Type: schema.TypeString, + Computed: true, + }, + "ports": { + Type: schema.TypeString, + Computed: true, + }, + "action": { + Type: schema.TypeString, + Computed: true, + }, + "priority": { + Type: schema.TypeInt, + Computed: true, + }, + "remote_group_id": { + Type: schema.TypeString, + Computed: true, + }, + "remote_ip_prefix": { + Type: schema.TypeString, + Computed: true, + }, + "remote_address_group_id": { + Type: schema.TypeString, + Computed: true, + }, + "created_at": { + Type: schema.TypeString, + Computed: true, + }, + "updated_at": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +func dataSourceNetworkingSecGroupRulesRead(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var ( + cfg = meta.(*config.Config) + region = cfg.GetRegion(d) + ) + client, err := cfg.NetworkingV3Client(region) + if err != nil { + return diag.Errorf("error creating networking v3 client: %s", err) + } + + listOpts := v3rules.ListOpts{ + SecurityGroupId: d.Get("security_group_id").(string), + ID: d.Get("rule_id").(string), + Protocol: d.Get("protocol").(string), + Description: d.Get("description").(string), + RemoteGroupId: d.Get("remote_group_id").(string), + Direction: d.Get("direction").(string), + Action: d.Get("action").(string), + } + resp, err := v3rules.List(client, listOpts) + if err != nil { + return common.CheckDeletedDiag(d, err, "Security Group rules") + } + rules := make([]map[string]interface{}, len(resp)) + for i, rule := range resp { + ruleInfo := map[string]interface{}{ + "id": rule.ID, + "description": rule.Description, + "security_group_id": rule.SecurityGroupId, + "direction": rule.Direction, + "protocol": rule.Protocol, + "ethertype": rule.Ethertype, + "ports": rule.MultiPort, + "action": rule.Action, + "priority": rule.Priority, + "remote_group_id": rule.RemoteGroupId, + "remote_ip_prefix": rule.RemoteIpPrefix, + "remote_address_group_id": rule.RemoteAddressGroupId, + "created_at": rule.CreateAt, + "updated_at": rule.UpdateAt, + } + rules[i] = ruleInfo + } + + randUUID, err := uuid.GenerateUUID() + if err != nil { + return diag.Errorf("unable to generate ID: %s", err) + } + d.SetId(randUUID) + mErr := multierror.Append(nil, + d.Set("region", region), + d.Set("rules", rules), + ) + if mErr.ErrorOrNil() != nil { + return diag.Errorf("error saving Security Group rules data source fields: %s", mErr) + } + return nil +} diff --git a/vendor/github.com/chnsz/golangsdk/openstack/networking/v3/security/rules/requests.go b/vendor/github.com/chnsz/golangsdk/openstack/networking/v3/security/rules/requests.go index 597ed2fa16..6e1bddac43 100644 --- a/vendor/github.com/chnsz/golangsdk/openstack/networking/v3/security/rules/requests.go +++ b/vendor/github.com/chnsz/golangsdk/openstack/networking/v3/security/rules/requests.go @@ -89,6 +89,8 @@ type ListOpts struct { // Specifies the number of records that will be returned on each page. The value is from 0 to intmax. // limit can be used together with marker. For details, see the parameter description of marker. Limit int `q:"limit"` + // Specifies the security group rule ID. + ID string `q:"id"` // Specifies the security group ID. SecurityGroupId string `q:"security_group_id"` // Security group rule protocol, support multiple filtering