diff --git a/docs/data-sources/identity_roles.md b/docs/data-sources/identity_roles.md new file mode 100644 index 00000000000..017fd0ec266 --- /dev/null +++ b/docs/data-sources/identity_roles.md @@ -0,0 +1,57 @@ +--- +subcategory: "Identity and Access Management (IAM)" +layout: "huaweicloud" +page_title: "HuaweiCloud: huaweicloud_identity_roles" +description: "" +--- + +# huaweicloud_identity_roles + +Use this data source to get a list of IAM roles with optional filtering by name or display name. + +-> **NOTE:** You *must* have IAM read privileges to use this data source. + +The Roles in Terraform correspond to the IAM roles in HuaweiCloud. You can retrieve all **System-Defined Roles** and their details using this data source. + +## Example Usage + +### Retrieve Roles by Name +```hcl +data "huaweicloud_identity_roles" "roles" { + name = "system_all_64" +} +``` + +### Retrieve Roles by Display Name +```hcl +data "huaweicloud_identity_roles" "roles" { + display_name = "OBS ReadOnlyAccess" +} +``` + +### Retrieve All Roles (No Filtering) +```hcl +data "huaweicloud_identity_roles" "roles" {} +``` + +## Argument Reference + +* `name` - (Optional, String) Specifies the internal name of the role. If provided, only roles matching this name will be returned. + +* `display_name` - (Optional, String) Specifies the display name of the role as shown on the console. If provided, only roles matching this display name will be returned. + +-> **NOTE:** At least one of `name` or `display_name` must be specified if you want to filter roles. If neither is specified, all roles will be returned. + +## Attribute Reference + +In addition to all arguments above, the following attributes are exported: + +* `roles` - A list of roles matching the specified criteria. Each role contains the following attributes: + * `id` - The unique identifier of the role in UUID format. + * `name` - The internal name of the role. + * `display_name` - The display name of the role as shown on the console. + * `description` - The description of the role. + * `catalog` - The service catalog associated with the role. + * `type` - The type or display mode of the role. + * `policy` - The JSON content of the role's policy. +``` \ No newline at end of file diff --git a/huaweicloud/provider.go b/huaweicloud/provider.go index 998c4000747..54991f0ae41 100644 --- a/huaweicloud/provider.go +++ b/huaweicloud/provider.go @@ -948,6 +948,7 @@ func Provider() *schema.Provider { "huaweicloud_identity_permissions": iam.DataSourceIdentityPermissions(), "huaweicloud_identity_role": iam.DataSourceIdentityRole(), + "huaweicloud_identity_roles": iam.DataSourceIdentityRoles(), "huaweicloud_identity_custom_role": iam.DataSourceIdentityCustomRole(), "huaweicloud_identity_group": iam.DataSourceIdentityGroup(), "huaweicloud_identity_projects": iam.DataSourceIdentityProjects(), diff --git a/huaweicloud/services/acceptance/iam/data_source_huaweicloud_identity_roles_test.go b/huaweicloud/services/acceptance/iam/data_source_huaweicloud_identity_roles_test.go new file mode 100644 index 00000000000..1226edfaaa1 --- /dev/null +++ b/huaweicloud/services/acceptance/iam/data_source_huaweicloud_identity_roles_test.go @@ -0,0 +1,81 @@ +package iam + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/services/acceptance" +) + +func TestAccIdentityRolesDataSource_basic(t *testing.T) { + dataSourceName := "data.huaweicloud_identity_roles.roles" + dc := acceptance.InitDataSourceCheck(dataSourceName) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acceptance.TestAccPreCheck(t) + acceptance.TestAccPreCheckAdminOnly(t) + }, + ProviderFactories: acceptance.TestAccProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccIdentityRolesDataSource_by_name, + Check: resource.ComposeTestCheckFunc( + dc.CheckResourceExists(), + resource.TestCheckResourceAttrSet(dataSourceName, "roles.#"), + resource.TestCheckResourceAttr(dataSourceName, "roles.0.name", "system_all_64"), + resource.TestCheckResourceAttr(dataSourceName, "roles.0.display_name", "OBS ReadOnlyAccess"), + ), + }, + { + Config: testAccIdentityRolesDataSource_by_displayname, + Check: resource.ComposeTestCheckFunc( + dc.CheckResourceExists(), + resource.TestCheckResourceAttrSet(dataSourceName, "roles.#"), + resource.TestCheckResourceAttr(dataSourceName, "roles.0.name", "kms_adm"), + resource.TestCheckResourceAttr(dataSourceName, "roles.0.display_name", "KMS Administrator"), + ), + }, + { + Config: testAccIdentityRolesDataSource_empty_filter, + Check: resource.ComposeTestCheckFunc( + dc.CheckResourceExists(), + resource.TestCheckResourceAttr(dataSourceName, "roles.#", "0"), + ), + }, + { + Config: testAccIdentityRolesDataSource_multiple_roles, + Check: resource.ComposeTestCheckFunc( + dc.CheckResourceExists(), + resource.TestCheckResourceAttrSet(dataSourceName, "roles.#"), + resource.TestCheckResourceAttr(dataSourceName, "roles.0.name", "system_all_64"), + resource.TestCheckResourceAttr(dataSourceName, "roles.1.name", "kms_adm"), + ), + }, + }, + }) +} + +const testAccIdentityRolesDataSource_by_name = ` +data "huaweicloud_identity_roles" "roles" { + name = "system_all_64" +} +` + +const testAccIdentityRolesDataSource_by_displayname = ` +data "huaweicloud_identity_roles" "roles" { + display_name = "KMS Administrator" +} +` + +const testAccIdentityRolesDataSource_empty_filter = ` +data "huaweicloud_identity_roles" "roles" { + name = "nonexistent_role" +} +` + +const testAccIdentityRolesDataSource_multiple_roles = ` +data "huaweicloud_identity_roles" "roles" { +} +` diff --git a/huaweicloud/services/iam/data_source_huaweicloud_identity_roles.go b/huaweicloud/services/iam/data_source_huaweicloud_identity_roles.go new file mode 100644 index 00000000000..f3eea9678a1 --- /dev/null +++ b/huaweicloud/services/iam/data_source_huaweicloud_identity_roles.go @@ -0,0 +1,132 @@ +package iam + +import ( + "context" + "encoding/json" + "log" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/chnsz/golangsdk/openstack/identity/v3/roles" + + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/config" +) + +// @API IAM GET /v3/roles +func DataSourceIdentityRoles() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceIdentityRolesRead, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Optional: true, + }, + "display_name": { + Type: schema.TypeString, + Optional: true, + }, + "roles": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "display_name": { + Type: schema.TypeString, + Computed: true, + }, + "description": { + Type: schema.TypeString, + Computed: true, + }, + "catalog": { + Type: schema.TypeString, + Computed: true, + }, + "type": { + Type: schema.TypeString, + Computed: true, + }, + "policy": { + Type: schema.TypeString, + Computed: true, + }, + }, + }, + }, + }, + } +} + +// dataSourceIdentityRolesRead performs the role lookup. +func dataSourceIdentityRolesRead(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + cfg := meta.(*config.Config) + identityClient, err := cfg.IdentityV3Client(cfg.GetRegion(d)) + if err != nil { + return diag.Errorf("error creating IAM client: %s", err) + } + + listOpts := roles.ListOpts{ + Name: d.Get("name").(string), + DisplayName: d.Get("display_name").(string), + } + + log.Printf("[DEBUG] List Options: %#v", listOpts) + allPages, err := roles.ListWithPages(identityClient, listOpts).AllPages() + if err != nil { + return diag.Errorf("unable to query IAM roles: %s", err) + } + + allRoles, err := roles.ExtractOffsetRoles(allPages) + if err != nil { + return diag.Errorf("unable to retrieve IAM roles: %s", err) + } + + if len(allRoles) < 1 { + return diag.Errorf("your query returned no results. " + + "Please change your search criteria and try again.") + } + + roleList := make([]map[string]interface{}, 0, len(allRoles)) + for _, role := range allRoles { + roleMap, err := flattenIdentityRole(&role) + if err != nil { + return diag.FromErr(err) + } + roleList = append(roleList, roleMap) + } + + d.SetId("identity_roles") + if err := d.Set("roles", roleList); err != nil { + return diag.Errorf("error setting roles: %s", err) + } + + return nil +} + +// flattenIdentityRole converts a Role struct into a map for Terraform state. +func flattenIdentityRole(role *roles.Role) (map[string]interface{}, error) { + policy, err := json.Marshal(role.Policy) + if err != nil { + return nil, err + } + + return map[string]interface{}{ + "id": role.ID, + "name": role.Name, + "display_name": role.DisplayName, + "description": role.Description, + "catalog": role.Catalog, + "type": role.Type, + "policy": string(policy), + }, nil +}