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()) +}