Skip to content

Commit

Permalink
Add data source for retrieving all service accounts from a project (h…
Browse files Browse the repository at this point in the history
…ashicorp#12130) (hashicorp#20062)

[upstream:a3c4547f504d67cfed7eae99f8c711493c2ba51a]

Signed-off-by: Modular Magician <[email protected]>
  • Loading branch information
modular-magician authored Oct 28, 2024
1 parent 6c1b6b3 commit d1c7c06
Show file tree
Hide file tree
Showing 5 changed files with 256 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .changelog/12130.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-datasource
`google_service_accounts`
```
1 change: 1 addition & 0 deletions google/provider/provider_mmv1_resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ var handwrittenDatasources = map[string]*schema.Resource{
"google_service_account_id_token": resourcemanager.DataSourceGoogleServiceAccountIdToken(),
"google_service_account_jwt": resourcemanager.DataSourceGoogleServiceAccountJwt(),
"google_service_account_key": resourcemanager.DataSourceGoogleServiceAccountKey(),
"google_service_accounts": resourcemanager.DataSourceGoogleServiceAccounts(),
"google_site_verification_token": siteverification.DataSourceSiteVerificationToken(),
"google_sourcerepo_repository": sourcerepo.DataSourceGoogleSourceRepoRepository(),
"google_spanner_instance": spanner.DataSourceSpannerInstance(),
Expand Down
105 changes: 105 additions & 0 deletions google/services/resourcemanager/data_source_google_service_accounts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package resourcemanager

import (
"fmt"
"strings"

"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"
)

func DataSourceGoogleServiceAccounts() *schema.Resource {
return &schema.Resource{
Read: datasourceGoogleServiceAccountsRead,
Schema: map[string]*schema.Schema{
"project": {
Type: schema.TypeString,
Optional: true,
},
"accounts": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"account_id": {
Type: schema.TypeString,
Required: true,
},
"disabled": {
Type: schema.TypeBool,
Computed: true,
},
"display_name": {
Type: schema.TypeString,
Computed: true,
},
"email": {
Type: schema.TypeString,
Computed: true,
},
"member": {
Type: schema.TypeString,
Computed: true,
},
"name": {
Type: schema.TypeString,
Computed: true,
},
"unique_id": {
Type: schema.TypeString,
Computed: true,
},
},
},
},
},
}
}

func datasourceGoogleServiceAccountsRead(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 service accounts: %s", err)
}

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

accountList, err := config.NewIamClient(userAgent).Projects.ServiceAccounts.List("projects/" + project).Do()
if err != nil {
return transport_tpg.HandleNotFoundError(err, d, fmt.Sprintf("Service accounts: %s", project))
}

for _, account := range accountList.Accounts {
accounts = append(accounts, map[string]interface{}{
"account_id": strings.Split(account.Email, "@")[0],
"disabled": account.Disabled,
"email": account.Email,
"display_name": account.DisplayName,
"member": "serviceAccount:" + account.Email,
"name": account.Name,
"unique_id": account.UniqueId,
})
}

if err := d.Set("accounts", accounts); err != nil {
return fmt.Errorf("Error retrieving service accounts: %s", err)
}

d.SetId(fmt.Sprintf(
"projects/%s",
project,
))

return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
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 TestAccDataSourceGoogleServiceAccounts_basic(t *testing.T) {
t.Parallel()

// Common resource configuration
static_prefix := "tf-test"
random_suffix := acctest.RandString(t, 10)
project := envvar.GetTestProjectFromEnv()

// Configuration of network resources
sa_1 := static_prefix + "-sa-1-" + random_suffix
sa_2 := static_prefix + "-sa-2-" + random_suffix

// Configuration map used in test deployment
context := map[string]interface{}{
"project": project,
"sa_1": sa_1,
"sa_2": sa_2,
}

acctest.VcrTest(t, resource.TestCase{
PreCheck: func() { acctest.AccTestPreCheck(t) },
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories(t),
Steps: []resource.TestStep{
{
Config: testAccCheckGoogleServiceAccountsConfig(context),
Check: resource.ComposeTestCheckFunc(
// We can't guarantee that no more service accounts are in the project, so we'll check set-ness rather than correctness
resource.TestCheckResourceAttrSet("data.google_service_accounts.all", "accounts.0.account_id"),
resource.TestCheckResourceAttrSet("data.google_service_accounts.all", "accounts.0.disabled"),
resource.TestCheckResourceAttrSet("data.google_service_accounts.all", "accounts.0.display_name"),
resource.TestCheckResourceAttrSet("data.google_service_accounts.all", "accounts.0.email"),
resource.TestCheckResourceAttrSet("data.google_service_accounts.all", "accounts.0.member"),
resource.TestCheckResourceAttrSet("data.google_service_accounts.all", "accounts.0.name"),
resource.TestCheckResourceAttrSet("data.google_service_accounts.all", "accounts.0.unique_id"),
resource.TestCheckResourceAttrSet("data.google_service_accounts.all", "accounts.1.account_id"),
resource.TestCheckResourceAttrSet("data.google_service_accounts.all", "accounts.1.disabled"),
resource.TestCheckResourceAttrSet("data.google_service_accounts.all", "accounts.1.display_name"),
resource.TestCheckResourceAttrSet("data.google_service_accounts.all", "accounts.1.email"),
resource.TestCheckResourceAttrSet("data.google_service_accounts.all", "accounts.1.member"),
resource.TestCheckResourceAttrSet("data.google_service_accounts.all", "accounts.1.name"),
resource.TestCheckResourceAttrSet("data.google_service_accounts.all", "accounts.1.unique_id"),
),
},
},
})
}

func testAccCheckGoogleServiceAccountsConfig(context map[string]interface{}) string {
return fmt.Sprintf(`
locals {
project_id = "%s"
sa_one = "%s"
sa_two = "%s"
}
resource "google_service_account" "sa_one" {
account_id = local.sa_one
description = local.sa_one
display_name = local.sa_one
}
resource "google_service_account" "sa_two" {
account_id = local.sa_two
description = local.sa_two
display_name = local.sa_two
}
data "google_service_accounts" "all" {
project = local.project_id
depends_on = [
google_service_account.sa_one,
google_service_account.sa_two,
]
}
`,
context["project"].(string),
context["sa_1"].(string),
context["sa_2"].(string),
)
}
53 changes: 53 additions & 0 deletions website/docs/d/service_accounts.html.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
subcategory: "Cloud Platform"
description: |-
Get the service accounts from a project.
---


# google_service_accounts

Gets a list of all service accounts from a project.
See [the official documentation](https://cloud.google.com/iam/docs/service-account-overview)
and [API](https://cloud.google.com/iam/docs/reference/rest/v1/projects.serviceAccounts).

## Example Usage

Example service accounts.

```hcl
data "google_service_accounts" "example" {
project = "example-project"
}
```

## Argument Reference

The following arguments are supported:

* `project` - (Optional) The ID of the project. If it is not provided, the provider project is used.


## Attributes Reference

The following attributes are exported:

* `accounts` - A list of all retrieved service accounts. Structure is [defined below](#nested_accounts).

<a name="nested_accounts"></a>The `accounts` block supports:

* `account_id` - The Google service account ID (the part before the `@` sign in the `email`)

* `disabled` - Whether a service account is disabled or not.

* `display_name` - The display name for the service account.

* `email` - The e-mail address of the service account. This value
should be referenced from any `google_iam_policy` data sources
that would grant the service account privileges.

* `member` - The Identity of the service account in the form `serviceAccount:{email}`. This value is often used to refer to the service account in order to grant IAM permissions.

* `name` - The fully-qualified name of the service account.

* `unique_id` - The unique id of the service account.

0 comments on commit d1c7c06

Please sign in to comment.