Skip to content

Commit

Permalink
Add inputs for filtering service accounts with data source google_ser…
Browse files Browse the repository at this point in the history
…vice_accounts (GoogleCloudPlatform#12802)
  • Loading branch information
bestefreund authored and anoopkverma-google committed Jan 31, 2025
1 parent 26fe066 commit 95f9803
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,33 @@ package resourcemanager
import (
"context"
"fmt"
"regexp"
"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"
"github.com/hashicorp/terraform-provider-google/google/verify"
"google.golang.org/api/iam/v1"
)

func DataSourceGoogleServiceAccounts() *schema.Resource {
return &schema.Resource{
Read: datasourceGoogleServiceAccountsRead,
Schema: map[string]*schema.Schema{
"prefix": {
Type: schema.TypeString,
Optional: true,
},
"project": {
Type: schema.TypeString,
Optional: true,
},
"regex": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: verify.ValidateRegexCompiles(),
},
"accounts": {
Type: schema.TypeList,
Computed: true,
Expand Down Expand Up @@ -73,14 +84,34 @@ func datasourceGoogleServiceAccountsRead(d *schema.ResourceData, meta interface{
return fmt.Errorf("Error fetching project for service accounts: %s", err)
}

prefix := d.Get("prefix").(string)
regexPattern := d.Get("regex").(string)

var regex *regexp.Regexp
if regexPattern != "" {
regex, err = regexp.Compile(regexPattern)
if err != nil {
return fmt.Errorf("Invalid regex pattern: %s", err)
}
}

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

request := config.NewIamClient(userAgent).Projects.ServiceAccounts.List("projects/" + project)

err = request.Pages(context.Background(), func(accountList *iam.ListServiceAccountsResponse) error {
for _, account := range accountList.Accounts {
accountId := strings.Split(account.Email, "@")[0]

if prefix != "" && !strings.HasPrefix(accountId, prefix) {
continue
}
if regex != nil && !regex.MatchString(account.Email) {
continue
}

accounts = append(accounts, map[string]interface{}{
"account_id": strings.Split(account.Email, "@")[0],
"account_id": accountId,
"disabled": account.Disabled,
"email": account.Email,
"display_name": account.DisplayName,
Expand All @@ -100,10 +131,17 @@ func datasourceGoogleServiceAccountsRead(d *schema.ResourceData, meta interface{
return fmt.Errorf("Error setting service accounts: %s", err)
}

d.SetId(fmt.Sprintf(
"projects/%s",
project,
))
idParts := []string{"projects", project}

if prefix != "" {
idParts = append(idParts, "prefix/"+prefix)
}
if regexPattern != "" {
idParts = append(idParts, "regex/"+regexPattern)
}

// Set the ID dynamically based on the provided attributes
d.SetId(strings.Join(idParts, "/"))

return nil
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,18 @@ func TestAccDataSourceGoogleServiceAccounts_basic(t *testing.T) {
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"),

// Check for prefix on account id
resource.TestCheckResourceAttr("data.google_service_accounts.with_prefix", "accounts.0.account_id", sa_1),

// Check for regex on email
resource.TestCheckResourceAttr("data.google_service_accounts.with_regex", "accounts.0.email", fmt.Sprintf("%s@%s.iam.gserviceaccount.com", sa_1, project)),

// Check if the account_id matches the prefix
resource.TestCheckResourceAttr("data.google_service_accounts.with_prefix_and_regex", "accounts.0.account_id", fmt.Sprintf(sa_1)),

// Check if the email matches the regex
resource.TestCheckResourceAttr("data.google_service_accounts.with_prefix_and_regex", "accounts.0.email", fmt.Sprintf("%s@%s.iam.gserviceaccount.com", sa_1, project)),
),
},
},
Expand Down Expand Up @@ -80,10 +92,26 @@ data "google_service_accounts" "all" {
project = local.project_id
depends_on = [
google_service_account.sa_one,
google_service_account.sa_two,
google_service_account.sa_one,
google_service_account.sa_two,
]
}
data "google_service_accounts" "with_prefix" {
prefix = google_service_account.sa_one.account_id
project = local.project_id
}
data "google_service_accounts" "with_regex" {
project = local.project_id
regex = ".*${google_service_account.sa_one.account_id}.*@.*\\.gserviceaccount\\.com"
}
data "google_service_accounts" "with_prefix_and_regex" {
prefix = google_service_account.sa_one.account_id
project = local.project_id
regex = ".*${google_service_account.sa_one.account_id}.*@.*\\.gserviceaccount\\.com"
}
`,
context["project"].(string),
context["sa_1"].(string),
Expand Down
11 changes: 11 additions & 0 deletions mmv1/third_party/terraform/verify/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -438,3 +438,14 @@ func ValidateRegexp(re string) schema.SchemaValidateFunc {
return
}
}

func ValidateRegexCompiles() schema.SchemaValidateFunc {
return func(v interface{}, k string) (ws []string, errs []error) {
value := v.(string)
if _, err := regexp.Compile(value); err != nil {
errs = append(errs, fmt.Errorf(
"%s (%s) is not a valid regex pattern: %s", k, value, err))
}
return
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,48 @@ and [API](https://cloud.google.com/iam/docs/reference/rest/v1/projects.serviceAc

## Example Usage

Example service accounts.
Get all service accounts from a project

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

Get all service accounts that are prefixed with `"foo"`

```hcl
data "google_service_accounts" "foo" {
prefix = "foo"
}
```

Get all service accounts that contain `"bar"`

```hcl
data "google_service_accounts" "bar" {
regex = ".*bar.*"
}
```

Get all service accounts that are prefixed with `"foo"` and contain `"bar"`

```hcl
data "google_service_accounts" "foo_bar" {
prefix = "foo"
regex = ".*bar.*"
}
```

## Argument Reference

The following arguments are supported:

* `prefix` - (Optional) A prefix for filtering. It's applied with the `account_id`.

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

* `regex` - (Optional) A regular expression for filtering. It's applied with the `email`. Further information about the syntax can be found [here](https://github.com/google/re2/wiki/Syntax).

## Attributes Reference

Expand Down

0 comments on commit 95f9803

Please sign in to comment.