Skip to content

Commit

Permalink
Add data source for retrieving project iam custom roles (#13303)
Browse files Browse the repository at this point in the history
  • Loading branch information
bestefreund authored Mar 11, 2025
1 parent 0b4149b commit 0fdfe63
Show file tree
Hide file tree
Showing 4 changed files with 258 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ var handwrittenDatasources = map[string]*schema.Resource{
"google_project": resourcemanager.DataSourceGoogleProject(),
"google_projects": resourcemanager.DataSourceGoogleProjects(),
"google_project_ancestry": resourcemanager.DataSourceGoogleProjectAncestry(),
"google_project_iam_custom_roles": resourcemanager.DataSourceGoogleProjectIamCustomRoles(),
"google_project_organization_policy": resourcemanager.DataSourceGoogleProjectOrganizationPolicy(),
"google_project_service": resourcemanager.DataSourceGoogleProjectService(),
"google_pubsub_subscription": pubsub.DataSourceGooglePubsubSubscription(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package resourcemanager

import (
"context"
"fmt"
"path"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-provider-google/google/tpgresource"
transport_tpg "github.com/hashicorp/terraform-provider-google/google/transport"
"google.golang.org/api/iam/v1"
)

func DataSourceGoogleProjectIamCustomRoles() *schema.Resource {
return &schema.Resource{
Read: dataSourceProjectIamCustomRoleRead,
Schema: map[string]*schema.Schema{
"project": {
Type: schema.TypeString,
Optional: true,
},
"show_deleted": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"view": {
Type: schema.TypeString,
Optional: true,
Default: "BASIC",
ValidateFunc: validateView,
},
"roles": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"deleted": {
Type: schema.TypeBool,
Computed: true,
},
"description": {
Type: schema.TypeString,
Computed: true,
},
"id": {
Type: schema.TypeString,
Computed: true,
},
"name": {
Type: schema.TypeString,
Computed: true,
},
"permissions": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
"role_id": {
Type: schema.TypeString,
Computed: true,
},
"stage": {
Type: schema.TypeString,
Computed: true,
},
"title": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
},
}
}

func validateView(val interface{}, key string) ([]string, []error) {
v := val.(string)
var errs []error

if v != "BASIC" && v != "FULL" {
errs = append(errs, fmt.Errorf("%q must be either 'BASIC' or 'FULL', got %q", key, v))
}

return nil, errs
}

func dataSourceProjectIamCustomRoleRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*transport_tpg.Config)
userAgent, err := tpgresource.GenerateUserAgentString(d, config.UserAgent)
if err != nil {
return err
}

project, err := tpgresource.GetProject(d, config)
if err != nil {
return fmt.Errorf("Error fetching project for custom roles: %s", err)
}

roles := make([]map[string]interface{}, 0)

showDeleted := d.Get("show_deleted").(bool)
view := d.Get("view").(string)

request := config.NewIamClient(userAgent).Projects.Roles.List("projects/" + project).ShowDeleted(showDeleted).View(view)

err = request.Pages(context.Background(), func(roleList *iam.ListRolesResponse) error {
for _, role := range roleList.Roles {
var permissions []string

switch view {
case "BASIC":
permissions = []string{}
case "FULL":
permissions = role.IncludedPermissions
default:
return fmt.Errorf("Unsupported view type: %s", view)
}

roles = append(roles, map[string]interface{}{
"deleted": role.Deleted,
"description": role.Description,
"id": role.Name,
"name": role.Name,
"permissions": permissions,
"role_id": path.Base(role.Name),
"stage": role.Stage,
"title": role.Title,
})
}
return nil
})

if err != nil {
return fmt.Errorf("Error retrieving project custom roles: %s", err)
}

if err := d.Set("roles", roles); err != nil {
return fmt.Errorf("Error setting project custom roles: %s", err)
}

d.SetId("projects/" + project)

return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package resourcemanager_test

import (
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-provider-google/google/acctest"
"github.com/hashicorp/terraform-provider-google/google/envvar"
)

func TestAccDataSourceGoogleProjectIamCustomRoles_basic(t *testing.T) {
t.Parallel()

project := envvar.GetTestProjectFromEnv()
roleId := "tfIamCustomRole" + acctest.RandString(t, 10)

acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
Steps: []resource.TestStep{
{
Config: testAccCheckGoogleProjectIamCustomRolesConfig(project, roleId),
Check: resource.ComposeTestCheckFunc(
// We can't guarantee no project won't have our custom role as first element, so we'll check set-ness rather than correctness
resource.TestCheckResourceAttrSet("data.google_project_iam_custom_roles.this", "roles.0.id"),
resource.TestCheckResourceAttrSet("data.google_project_iam_custom_roles.this", "roles.0.name"),
resource.TestCheckResourceAttrSet("data.google_project_iam_custom_roles.this", "roles.0.role_id"),
resource.TestCheckResourceAttrSet("data.google_project_iam_custom_roles.this", "roles.0.stage"),
resource.TestCheckResourceAttrSet("data.google_project_iam_custom_roles.this", "roles.0.title"),
),
},
},
})
}

func testAccCheckGoogleProjectIamCustomRolesConfig(project string, roleId string) string {
return fmt.Sprintf(`
locals {
project = "%s"
role_id = "%s"
}
resource "google_project_iam_custom_role" "this" {
project = local.project
role_id = local.role_id
title = "Terraform Test"
permissions = [
"iam.roles.create",
"iam.roles.delete",
"iam.roles.list",
]
}
data "google_project_iam_custom_roles" "this" {
project = google_project_iam_custom_role.this.project
}
`, project, roleId)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
subcategory: "Cloud Platform"
description: |-
Get information about Google Cloud IAM Custom Roles from a project.
---

# google_project_iam_custom_roles

Get information about Google Cloud IAM Custom Roles from a project.
Note that you must have the `roles/iam.roleViewer`.
See [the official documentation](https://cloud.google.com/iam/docs/creating-custom-roles)
and [API](https://cloud.google.com/iam/docs/reference/rest/v1/projects.roles/list).

```hcl
data "google_project_iam_custom_roles" "example" {
project = "your-project-id"
show_deleted = true
view = "FULL"
}
```

## Argument Reference

The following arguments are supported:

* `project` - (Optional) The project were the custom role has been created in. Defaults to the provider project configuration.

* `show_deleted` - (Optional) Include Roles that have been deleted. Defaults to `false`.

* `view` - (Optional) When `"FULL"` is specified, the `permissions` field is returned, which includes a list of all permissions in the role. The default value is `"BASIC"`, which does not return the `permissions`.

## Attributes Reference

The following attributes are exported:

* `roles` - A list of all retrieved custom roles roles. Structure is [defined below](#nested_roles).

<a name="nested_roles"></a>The `roles` block supports:

* `deleted` - The current deleted state of the role.

* `description` - A human-readable description for the role.

* `id` - an identifier for the resource with the format `projects/{{project}}/roles/{{role_id}}`.

* `name` - The name of the role in the format `projects/{{project}}/roles/{{role_id}}`. Like `id`, this field can be used as a reference in other resources such as IAM role bindings.

* `permissions` - The names of the permissions this role grants when bound in an IAM policy.

* `role_id` - The camel case role id used for this role.

* `stage` - The current launch stage of the role. List of possible stages is [here](https://cloud.google.com/iam/reference/rest/v1/organizations.roles#Role.RoleLaunchStage).

* `title` - A human-readable title for the role.

0 comments on commit 0fdfe63

Please sign in to comment.