diff --git a/docs/data-sources/workspace_policy_groups.md b/docs/data-sources/workspace_policy_groups.md
new file mode 100644
index 00000000000..7735cfee972
--- /dev/null
+++ b/docs/data-sources/workspace_policy_groups.md
@@ -0,0 +1,123 @@
+---
+subcategory: "Workspace"
+layout: "huaweicloud"
+page_title: "HuaweiCloud: huaweicloud_workspace_policy_groups"
+description: |-
+ Use this data source to get the list of Workspace policy groups within HuaweiCloud.
+---
+
+# huaweicloud_workspace_policy_groups
+
+Use this data source to get the list of Workspace policy groups within HuaweiCloud.
+
+## Example Usage
+
+### Basic Usage
+
+```hcl
+data "huaweicloud_workspace_policy_groups" "test" {}
+```
+
+### Filter policy groups by priority
+
+```hcl
+variable "policy_priority" {}
+
+data "huaweicloud_workspace_policy_groups" "test" {
+ priority = var.policy_priority
+}
+```
+
+### Filter policy groups by name
+
+```hcl
+variable "policy_group_name" {}
+
+data "huaweicloud_workspace_policy_groups" "test" {
+ policy_group_name = var.policy_group_name
+}
+```
+
+## Argument Reference
+
+The following arguments are supported:
+
+* `region` - (Optional, String) Specifies the region in which to query the data source.
+ If omitted, the provider-level region will be used.
+
+* `policy_group_id` - (Optional, String) Specifies the ID of the policy group.
+
+* `policy_group_name` - (Optional, String) Specifies the name of the policy group.
+ The name support fuzzy match.
+
+* `priority` - (Optional, Int) Specifies the priority of the policy group.
+ Defaults to **0**.
+
+* `description` - (Optional, String) Specifies the description of the policy group.
+ The description support fuzzy match.
+
+## Attribute Reference
+
+In addition to all arguments above, the following attributes are exported:
+
+* `id` - The data source ID.
+
+* `policy_groups` - The list of policy groups that match the filter parameters.
+ The [policy_groups](#workspace_policy_groups_attr) structure is documented below.
+
+
+The `policy_groups` block supports:
+
+* `policy_group_id` - The ID of the policy group.
+
+* `policy_group_name` - The name of the policy group.
+
+* `priority` - The priority of the policy group.
+
+* `update_time` - The update time of the policy group, in RFC3339 format.
+
+* `description` - The description of the policy group.
+
+* `policies` - The list of policy configurations.
+ The [policies](#workspace_policy_groups_policies) structure is documented below.
+
+* `targets` - The list of target configurations.
+ The [targets](#workspace_policy_groups_targets) structure is documented below.
+
+
+The `policies` block supports:
+
+* `peripherals` - The peripheral device policies, in JSON format.
+
+* `audio` - The audio policies, in JSON format.
+
+* `client` - The client policies, in JSON format.
+
+* `display` - The display policies, in JSON format.
+
+* `file_and_clipboard` - The file and clipboard policies, in JSON format.
+
+* `session` - The session policies, in JSON format.
+
+* `virtual_channel` - The virtual channel policies, in JSON format.
+
+* `watermark` - The watermark policies, in JSON format.
+
+* `keyboard_mouse` - The keyboard and mouse policies, in JSON format.
+
+* `seamless` - The general audio and video bypass policies, in JSON format.
+
+* `personalized_data_mgmt` - The personalized data management policies, in JSON format.
+
+* `custom` - The custom policies, in JSON format.
+
+* `record_audit` - The screen recording audit policies, in JSON format.
+
+
+The `targets` block supports:
+
+* `target_id` - The ID of the target.
+
+* `target_type` - The type of the target.
+
+* `target_name` - The name of the target.
diff --git a/huaweicloud/provider.go b/huaweicloud/provider.go
index 0d6a91bcd1b..f9720348f90 100644
--- a/huaweicloud/provider.go
+++ b/huaweicloud/provider.go
@@ -1507,6 +1507,7 @@ func Provider() *schema.Provider {
"huaweicloud_workspace_desktops": workspace.DataSourceDesktops(),
"huaweicloud_workspace_desktop_tags": workspace.DataSourceDesktopTags(),
"huaweicloud_workspace_flavors": workspace.DataSourceWorkspaceFlavors(),
+ "huaweicloud_workspace_policy_groups": workspace.DataSourcePolicyGroups(),
"huaweicloud_workspace_service": workspace.DataSourceService(),
"huaweicloud_workspace_tags": workspace.DataSourceTags(),
diff --git a/huaweicloud/services/acceptance/workspace/data_source_huaweicloud_workspace_policy_groups_test.go b/huaweicloud/services/acceptance/workspace/data_source_huaweicloud_workspace_policy_groups_test.go
new file mode 100644
index 00000000000..ee5399e69f1
--- /dev/null
+++ b/huaweicloud/services/acceptance/workspace/data_source_huaweicloud_workspace_policy_groups_test.go
@@ -0,0 +1,206 @@
+package workspace
+
+import (
+ "fmt"
+ "regexp"
+ "testing"
+
+ "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
+
+ "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/services/acceptance"
+)
+
+func TestAccDataSourcePolicyGroups_basic(t *testing.T) {
+ var (
+ name = acceptance.RandomAccResourceName()
+
+ dcName = "data.huaweicloud_workspace_policy_groups.all"
+ dc = acceptance.InitDataSourceCheck(dcName)
+
+ filterById = "data.huaweicloud_workspace_policy_groups.filter_by_policy_group_id"
+ dcFilterById = acceptance.InitDataSourceCheck(filterById)
+
+ filterByName = "data.huaweicloud_workspace_policy_groups.filter_by_policy_group_name"
+ dcFilterByName = acceptance.InitDataSourceCheck(filterByName)
+
+ filterByPriority = "data.huaweicloud_workspace_policy_groups.filter_by_priority"
+ dcFilterByPriority = acceptance.InitDataSourceCheck(filterByPriority)
+
+ filterByDescription = "data.huaweicloud_workspace_policy_groups.filter_by_description"
+ dcFilterByDescription = acceptance.InitDataSourceCheck(filterByDescription)
+ )
+
+ resource.Test(t, resource.TestCase{
+ PreCheck: func() {
+ acceptance.TestAccPreCheck(t)
+ },
+ ProviderFactories: acceptance.TestAccProviderFactories,
+ Steps: []resource.TestStep{
+ {
+ Config: testAccDataSourcePolicyGroups_basic(name),
+ Check: resource.ComposeTestCheckFunc(
+ // Query policy groups without any filter parameter
+ dc.CheckResourceExists(),
+ resource.TestMatchResourceAttr(dcName, "policy_groups.#", regexp.MustCompile(`^[1-9]([0-9]*)?$`)),
+ resource.TestCheckResourceAttrSet(dcName, "policy_groups.0.policy_group_id"),
+ resource.TestCheckResourceAttrSet(dcName, "policy_groups.0.policy_group_name"),
+ resource.TestCheckResourceAttrSet(dcName, "policy_groups.0.priority"),
+ resource.TestCheckResourceAttrSet(dcName, "policy_groups.0.update_time"),
+ // Filter by ID
+ dcFilterById.CheckResourceExists(),
+ resource.TestCheckOutput("is_id_filter_useful", "true"),
+ // Filter by name
+ dcFilterByName.CheckResourceExists(),
+ resource.TestCheckOutput("is_name_filter_useful", "true"),
+ // Filter by priority
+ dcFilterByPriority.CheckResourceExists(),
+ resource.TestCheckOutput("is_priority_filter_useful", "true"),
+ // Filter by description
+ dcFilterByDescription.CheckResourceExists(),
+ resource.TestCheckOutput("is_description_filter_useful", "true"),
+ ),
+ },
+ },
+ })
+}
+
+func testAccDataSourcePolicyGroups_basic(name string) string {
+ return fmt.Sprintf(`
+%[1]s
+
+data "huaweicloud_workspace_policy_groups" "all" {
+ depends_on = [
+ huaweicloud_workspace_policy_group.test,
+ huaweicloud_workspace_policy_group.nontest,
+ ]
+}
+
+locals {
+ policy_group_id = try(data.huaweicloud_workspace_policy_groups.all.policy_groups[0].policy_group_id, "NOT_FOUND")
+ policy_group_name = try(data.huaweicloud_workspace_policy_groups.all.policy_groups[0].policy_group_name, "NOT_FOUND")
+ priority = try(data.huaweicloud_workspace_policy_groups.all.policy_groups[0].priority, -1)
+ update_time = try(data.huaweicloud_workspace_policy_groups.all.policy_groups[0].update_time, "1900-01-01T01:01:01Z")
+ description = try(data.huaweicloud_workspace_policy_groups.all.policy_groups[0].description, "NOT_FOUND")
+}
+
+# Filter by policy group id
+data "huaweicloud_workspace_policy_groups" "filter_by_policy_group_id" {
+ policy_group_id = local.policy_group_id
+
+ depends_on = [
+ huaweicloud_workspace_policy_group.test,
+ huaweicloud_workspace_policy_group.nontest,
+ ]
+}
+
+locals {
+ id_filter_result = [
+ for v in data.huaweicloud_workspace_policy_groups.filter_by_policy_group_id.policy_groups[*].policy_group_id :
+ v == local.policy_group_id
+ ]
+}
+
+output "is_id_filter_useful" {
+ value = length(local.id_filter_result) < 2 && alltrue(local.id_filter_result)
+}
+
+# Filter by policy group name
+data "huaweicloud_workspace_policy_groups" "filter_by_policy_group_name" {
+ policy_group_name = local.policy_group_name
+
+ depends_on = [
+ huaweicloud_workspace_policy_group.test,
+ huaweicloud_workspace_policy_group.nontest,
+ ]
+}
+
+output "is_name_filter_useful" {
+ value = length(data.huaweicloud_workspace_policy_groups.filter_by_policy_group_name.policy_groups) > 0
+}
+
+# Filter by priority
+data "huaweicloud_workspace_policy_groups" "filter_by_priority" {
+ priority = local.priority
+
+ depends_on = [
+ huaweicloud_workspace_policy_group.test,
+ huaweicloud_workspace_policy_group.nontest,
+ ]
+}
+
+locals {
+ priority_filter_result = [
+ for v in data.huaweicloud_workspace_policy_groups.filter_by_priority.policy_groups[*].priority :
+ v == local.priority
+ ]
+}
+
+output "is_priority_filter_useful" {
+ value = length(local.priority_filter_result) > 0 && alltrue(local.priority_filter_result)
+}
+
+# Filter by description
+data "huaweicloud_workspace_policy_groups" "filter_by_description" {
+ description = local.description
+
+ depends_on = [
+ huaweicloud_workspace_policy_group.test,
+ huaweicloud_workspace_policy_group.nontest,
+ ]
+}
+
+locals {
+ description_filter_result = [
+ for v in data.huaweicloud_workspace_policy_groups.filter_by_description.policy_groups[*].description :
+ strcontains(v, local.description)
+ ]
+}
+
+output "is_description_filter_useful" {
+ value = length(local.description_filter_result) > 0 && alltrue(local.description_filter_result)
+}
+`, testAccDataSourcePolicyGroups_base(name))
+}
+
+func testAccDataSourcePolicyGroups_base(name string) string {
+ return fmt.Sprintf(`
+resource "huaweicloud_workspace_user" "test" {
+ name = "%[1]s"
+ email = "www.%[1]s@example.com"
+}
+
+// The priority will automatically increment with the creation of the resource, no need to specify it manually.
+resource "huaweicloud_workspace_policy_group" "test" {
+ name = "%[1]s"
+ description = "Created by terraform script"
+
+ targets {
+ type = "USER"
+ id = huaweicloud_workspace_user.test.id
+ name = huaweicloud_workspace_user.test.name
+ }
+
+ policy {
+ access_control {
+ ip_access_control = "112.20.53.2|255.255.240.0;112.20.53.3|255.255.240.0"
+ }
+ }
+}
+
+resource "huaweicloud_workspace_policy_group" "nontest" {
+ name = "non_%[1]s"
+
+ targets {
+ type = "USER"
+ id = huaweicloud_workspace_user.test.id
+ name = huaweicloud_workspace_user.test.name
+ }
+
+ policy {
+ access_control {
+ ip_access_control = "112.20.53.2|255.255.240.0;112.20.53.3|255.255.240.0"
+ }
+ }
+}
+`, name)
+}
diff --git a/huaweicloud/services/workspace/data_source_huaweicloud_workspace_policy_groups.go b/huaweicloud/services/workspace/data_source_huaweicloud_workspace_policy_groups.go
new file mode 100644
index 00000000000..b8e91ebe41d
--- /dev/null
+++ b/huaweicloud/services/workspace/data_source_huaweicloud_workspace_policy_groups.go
@@ -0,0 +1,348 @@
+package workspace
+
+import (
+ "context"
+ "fmt"
+ "strconv"
+ "strings"
+
+ "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"
+
+ "github.com/chnsz/golangsdk"
+
+ "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/config"
+ "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/utils"
+)
+
+// @API Workspace GET /v2/{project_id}/policy-groups/detail
+func DataSourcePolicyGroups() *schema.Resource {
+ return &schema.Resource{
+ ReadContext: dataSourcePolicyGroupsRead,
+
+ Schema: map[string]*schema.Schema{
+ "region": {
+ Type: schema.TypeString,
+ Optional: true,
+ Computed: true,
+ Description: `The region in which to query the data source.`,
+ },
+ "policy_group_id": {
+ Type: schema.TypeString,
+ Optional: true,
+ Description: `The ID of the policy group.`,
+ },
+ "policy_group_name": {
+ Type: schema.TypeString,
+ Optional: true,
+ Description: `The name of the policy group.`,
+ },
+ "priority": {
+ Type: schema.TypeInt,
+ Optional: true,
+ Description: `The priority of the policy group.`,
+ },
+ "description": {
+ Type: schema.TypeString,
+ Optional: true,
+ Description: `The description of the policy group.`,
+ },
+ "policy_groups": {
+ Type: schema.TypeList,
+ Computed: true,
+ Elem: policyGroupSchema(),
+ Description: `The list of policy groups that match the filter parameters.`,
+ },
+ },
+ }
+}
+
+func policyGroupSchema() *schema.Resource {
+ return &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "policy_group_id": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: `The ID of the policy group.`,
+ },
+ "policy_group_name": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: `The name of the policy group.`,
+ },
+ "priority": {
+ Type: schema.TypeInt,
+ Computed: true,
+ Description: `The priority of the policy group.`,
+ },
+ "update_time": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: `The update time of the policy group, in RFC3339 format.`,
+ },
+ "description": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: `The description of the policy group.`,
+ },
+ "policies": {
+ Type: schema.TypeList,
+ Computed: true,
+ Elem: policySchema(),
+ Description: `The list of policy configurations.`,
+ },
+ "targets": {
+ Type: schema.TypeList,
+ Computed: true,
+ Elem: targetSchema(),
+ Description: `The list of target configurations.`,
+ },
+ },
+ }
+}
+
+func policySchema() *schema.Resource {
+ return &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "peripherals": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: `The peripheral device policies, in JSON format.`,
+ },
+ "audio": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: `The audio policies, in JSON format.`,
+ },
+ "client": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: `The client policies, in JSON format.`,
+ },
+ "display": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: `The display policies, in JSON format.`,
+ },
+ "file_and_clipboard": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: `The file and clipboard policies, in JSON format.`,
+ },
+ "session": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: `The session policies, in JSON format.`,
+ },
+ "virtual_channel": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: `The virtual channel policies, in JSON format.`,
+ },
+ "watermark": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: `The watermark policies, in JSON format.`,
+ },
+ "keyboard_mouse": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: `The keyboard and mouse policies, in JSON format.`,
+ },
+ "seamless": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: `The general audio and video bypass policies, in JSON format.`,
+ },
+ "personalized_data_mgmt": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: `The personalized data management policies, in JSON format.`,
+ },
+ "custom": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: `The custom policies, in JSON format.`,
+ },
+ "record_audit": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: `The screen recording audit policies, in JSON format.`,
+ },
+ },
+ }
+}
+
+func targetSchema() *schema.Resource {
+ return &schema.Resource{
+ Schema: map[string]*schema.Schema{
+ "target_id": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: `The ID of the target.`,
+ },
+ "target_type": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: `The type of the target.`,
+ },
+ "target_name": {
+ Type: schema.TypeString,
+ Computed: true,
+ Description: `The name of the target.`,
+ },
+ },
+ }
+}
+
+func flattenPolicyGroups(policyGroups []interface{}) []interface{} {
+ if len(policyGroups) < 1 {
+ return nil
+ }
+
+ result := make([]interface{}, 0, len(policyGroups))
+ for _, item := range policyGroups {
+ result = append(result, map[string]interface{}{
+ "policy_group_id": utils.PathSearch("policy_group_id", item, nil),
+ "policy_group_name": utils.PathSearch("policy_group_name", item, nil),
+ "priority": utils.PathSearch("priority", item, nil),
+ "update_time": utils.PathSearch("update_time", item, nil),
+ "description": utils.PathSearch("description", item, nil),
+ "policies": flattenPolicyGroupsPolicies(utils.PathSearch("policies", item, nil)),
+ "targets": flattenPolicyGroupsTargets(utils.PathSearch("targets", item, make([]interface{}, 0)).([]interface{})),
+ })
+ }
+ return result
+}
+
+func flattenPolicyGroupsPolicies(policies interface{}) []interface{} {
+ if policies == nil {
+ return nil
+ }
+
+ return []interface{}{
+ map[string]interface{}{
+ "peripherals": utils.JsonToString(utils.PathSearch("peripherals", policies, nil)),
+ "audio": utils.JsonToString(utils.PathSearch("audio", policies, nil)),
+ "client": utils.JsonToString(utils.PathSearch("client", policies, nil)),
+ "display": utils.JsonToString(utils.PathSearch("display", policies, nil)),
+ "file_and_clipboard": utils.JsonToString(utils.PathSearch("file_and_clipboard", policies, nil)),
+ "session": utils.JsonToString(utils.PathSearch("session", policies, nil)),
+ "virtual_channel": utils.JsonToString(utils.PathSearch("virtual_channel", policies, nil)),
+ "watermark": utils.JsonToString(utils.PathSearch("watermark", policies, nil)),
+ "keyboard_mouse": utils.JsonToString(utils.PathSearch("keyboard_mouse", policies, nil)),
+ "seamless": utils.JsonToString(utils.PathSearch("seamless", policies, nil)),
+ "personalized_data_mgmt": utils.JsonToString(utils.PathSearch("personalized_data_mgmt", policies, nil)),
+ "custom": utils.JsonToString(utils.PathSearch("custom", policies, nil)),
+ "record_audit": utils.JsonToString(utils.PathSearch("record_audit", policies, nil)),
+ },
+ }
+}
+
+func flattenPolicyGroupsTargets(targets []interface{}) []interface{} {
+ if len(targets) < 1 {
+ return nil
+ }
+
+ result := make([]interface{}, 0, len(targets))
+ for _, item := range targets {
+ result = append(result, map[string]interface{}{
+ "target_id": utils.PathSearch("target_id", item, nil),
+ "target_type": utils.PathSearch("target_type", item, nil),
+ "target_name": utils.PathSearch("target_name", item, nil),
+ })
+ }
+ return result
+}
+
+func buildListPolicyGroupsParams(d *schema.ResourceData) string {
+ res := ""
+ if v, ok := d.GetOk("policy_group_id"); ok {
+ res = fmt.Sprintf("%s&policy_group_id=%v", res, v)
+ }
+
+ if v, ok := d.GetOk("policy_group_name"); ok {
+ res = fmt.Sprintf("%s&policy_group_name=%v", res, v)
+ }
+
+ if v, ok := d.GetOk("priority"); ok {
+ res = fmt.Sprintf("%s&priority=%v", res, v)
+ }
+
+ if v, ok := d.GetOk("description"); ok {
+ res = fmt.Sprintf("%s&description=%v", res, v)
+ }
+ return res
+}
+
+func listPolicyGroups(client *golangsdk.ServiceClient, d *schema.ResourceData) ([]interface{}, error) {
+ var (
+ httpUrl = "v2/{project_id}/policy-groups/detail?limit={limit}"
+ offset = 0
+ limit = 100
+ result = make([]interface{}, 0)
+ )
+
+ listPathWithLimit := client.Endpoint + httpUrl
+ listPathWithLimit = strings.ReplaceAll(listPathWithLimit, "{project_id}", client.ProjectID)
+ listPathWithLimit = strings.ReplaceAll(listPathWithLimit, "{limit}", strconv.Itoa(limit))
+ listPathWithLimit += buildListPolicyGroupsParams(d)
+
+ opt := golangsdk.RequestOpts{
+ KeepResponseBody: true,
+ MoreHeaders: map[string]string{
+ "Content-Type": "application/json",
+ },
+ }
+
+ for {
+ listPathWithOffset := fmt.Sprintf("%s&offset=%d", listPathWithLimit, offset)
+ requestResp, err := client.Request("GET", listPathWithOffset, &opt)
+ if err != nil {
+ return nil, err
+ }
+
+ respBody, err := utils.FlattenResponse(requestResp)
+ if err != nil {
+ return nil, err
+ }
+
+ policyGroups := utils.PathSearch("policy_groups", respBody, make([]interface{}, 0)).([]interface{})
+ result = append(result, policyGroups...)
+ if len(policyGroups) < limit {
+ break
+ }
+ offset += len(policyGroups)
+ }
+ return result, nil
+}
+
+func dataSourcePolicyGroupsRead(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
+ cfg := meta.(*config.Config)
+ region := cfg.GetRegion(d)
+ client, err := cfg.NewServiceClient("workspace", region)
+ if err != nil {
+ return diag.Errorf("error creating Workspace client: %s", err)
+ }
+
+ policyGroups, err := listPolicyGroups(client, d)
+ if err != nil {
+ return diag.Errorf("error querying Workspace policy groups: %s", err)
+ }
+
+ uuid, err := uuid.GenerateUUID()
+ if err != nil {
+ return diag.Errorf("unable to generate ID: %s", err)
+ }
+ d.SetId(uuid)
+
+ mErr := multierror.Append(
+ nil,
+ d.Set("region", region),
+ d.Set("policy_groups", flattenPolicyGroups(policyGroups)),
+ )
+
+ return diag.FromErr(mErr.ErrorOrNil())
+}