-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Data source for IAM Testable Permissions #3460
Changes from all commits
ba5390f
3e7bbf0
b377239
67cd577
0d9fdde
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
package google | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/helper/schema" | ||
"github.com/hashicorp/terraform-plugin-sdk/helper/validation" | ||
) | ||
|
||
func dataSourceGoogleIamTestablePermissions() *schema.Resource { | ||
return &schema.Resource{ | ||
Read: dataSourceGoogleIamTestablePermissionsRead, | ||
Schema: map[string]*schema.Schema{ | ||
"full_resource_name": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
}, | ||
"stages": { | ||
Type: schema.TypeList, | ||
Optional: true, | ||
Elem: &schema.Schema{ | ||
Type: schema.TypeString, | ||
ValidateFunc: validation.StringInSlice([]string{"ALPHA", "BETA", "GA", "DEPRECATED"}, true), | ||
}, | ||
}, | ||
"custom_support_level": { | ||
Type: schema.TypeString, | ||
Optional: true, | ||
Default: "SUPPORTED", | ||
ValidateFunc: validation.StringInSlice([]string{"NOT_SUPPORTED", "SUPPORTED", "TESTING"}, true), | ||
}, | ||
"permissions": { | ||
Type: schema.TypeList, | ||
Computed: true, | ||
Elem: &schema.Resource{ | ||
Schema: map[string]*schema.Schema{ | ||
"name": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
"title": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
"custom_support_level": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
"stage": { | ||
Type: schema.TypeString, | ||
Computed: true, | ||
}, | ||
"api_disabled": { | ||
Type: schema.TypeBool, | ||
Computed: true, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func dataSourceGoogleIamTestablePermissionsRead(d *schema.ResourceData, meta interface{}) (err error) { | ||
config := meta.(*Config) | ||
body := make(map[string]interface{}) | ||
body["pageSize"] = 500 | ||
permissions := make([]map[string]interface{}, 0) | ||
|
||
custom_support_level := strings.ToUpper(d.Get("custom_support_level").(string)) | ||
stages := []string{} | ||
for _, e := range d.Get("stages").([]interface{}) { | ||
stages = append(stages, strings.ToUpper(e.(string))) | ||
slevenick marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
if len(stages) == 0 { | ||
// Since schema.TypeLists cannot specify defaults, we'll specify it here | ||
stages = append(stages, "GA") | ||
} | ||
for { | ||
url := "https://iam.googleapis.com/v1/permissions:queryTestablePermissions" | ||
body["fullResourceName"] = d.Get("full_resource_name").(string) | ||
res, err := sendRequest(config, "POST", "", url, body) | ||
if err != nil { | ||
return fmt.Errorf("Error retrieving permissions: %s", err) | ||
} | ||
|
||
pagePermissions := flattenTestablePermissionsList(res["permissions"], custom_support_level, stages) | ||
permissions = append(permissions, pagePermissions...) | ||
pToken, ok := res["nextPageToken"] | ||
if ok && pToken != nil && pToken.(string) != "" { | ||
body["pageToken"] = pToken.(string) | ||
} else { | ||
break | ||
} | ||
} | ||
|
||
if err := d.Set("permissions", permissions); err != nil { | ||
return fmt.Errorf("Error retrieving permissions: %s", err) | ||
} | ||
|
||
d.SetId(d.Get("full_resource_name").(string)) | ||
return nil | ||
} | ||
|
||
func flattenTestablePermissionsList(v interface{}, custom_support_level string, stages []string) []map[string]interface{} { | ||
if v == nil { | ||
return make([]map[string]interface{}, 0) | ||
} | ||
|
||
ls := v.([]interface{}) | ||
permissions := make([]map[string]interface{}, 0, len(ls)) | ||
for _, raw := range ls { | ||
p := raw.(map[string]interface{}) | ||
|
||
if _, ok := p["name"]; ok { | ||
var csl bool | ||
if custom_support_level == "SUPPORTED" { | ||
csl = p["customRolesSupportLevel"] == nil || p["customRolesSupportLevel"] == "SUPPORTED" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems to be a API/Docs mismatch where the Docs say they response will be "SUPPORTED" but the actual API response just doesn't include |
||
} else { | ||
csl = p["customRolesSupportLevel"] == custom_support_level | ||
} | ||
if csl && p["stage"] != nil && stringInSlice(stages, p["stage"].(string)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of checking Like: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If stage comes back as nil what do we expect to do? Is that possible? |
||
permissions = append(permissions, map[string]interface{}{ | ||
"name": p["name"], | ||
"title": p["title"], | ||
"stage": p["stage"], | ||
"api_disabled": p["apiDisabled"], | ||
"custom_support_level": p["customRolesSupportLevel"], | ||
}) | ||
} | ||
} | ||
} | ||
|
||
return permissions | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
package google | ||
|
||
import ( | ||
"fmt" | ||
"strconv" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/helper/resource" | ||
"github.com/hashicorp/terraform-plugin-sdk/terraform" | ||
) | ||
|
||
func TestAccDataSourceGoogleIamTestablePermissions_basic(t *testing.T) { | ||
t.Parallel() | ||
|
||
project := getTestProjectFromEnv() | ||
vcrTest(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: fmt.Sprintf(` | ||
data "google_iam_testable_permissions" "perms" { | ||
full_resource_name = "//cloudresourcemanager.googleapis.com/projects/%s" | ||
} | ||
`, project), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckGoogleIamTestablePermissionsMeta( | ||
project, | ||
"data.google_iam_testable_permissions.perms", | ||
[]string{"GA"}, | ||
"", | ||
), | ||
), | ||
}, | ||
{ | ||
Config: fmt.Sprintf(` | ||
data "google_iam_testable_permissions" "perms" { | ||
full_resource_name = "//cloudresourcemanager.googleapis.com/projects/%s" | ||
stages = ["GA"] | ||
} | ||
`, project), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckGoogleIamTestablePermissionsMeta( | ||
project, | ||
"data.google_iam_testable_permissions.perms", | ||
[]string{"GA"}, | ||
"", | ||
), | ||
), | ||
}, | ||
{ | ||
Config: fmt.Sprintf(` | ||
data "google_iam_testable_permissions" "perms" { | ||
full_resource_name = "//cloudresourcemanager.googleapis.com/projects/%s" | ||
custom_support_level = "NOT_SUPPORTED" | ||
stages = ["BETA"] | ||
} | ||
`, project), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckGoogleIamTestablePermissionsMeta( | ||
project, | ||
"data.google_iam_testable_permissions.perms", | ||
[]string{"BETA"}, | ||
"NOT_SUPPORTED", | ||
), | ||
), | ||
}, | ||
{ | ||
Config: fmt.Sprintf(` | ||
data "google_iam_testable_permissions" "perms" { | ||
full_resource_name = "//cloudresourcemanager.googleapis.com/projects/%s" | ||
custom_support_level = "not_supported" | ||
stages = ["beta"] | ||
} | ||
`, project), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckGoogleIamTestablePermissionsMeta( | ||
project, | ||
"data.google_iam_testable_permissions.perms", | ||
[]string{"BETA"}, | ||
"NOT_SUPPORTED", | ||
), | ||
), | ||
}, | ||
{ | ||
Config: fmt.Sprintf(` | ||
data "google_iam_testable_permissions" "perms" { | ||
full_resource_name = "//cloudresourcemanager.googleapis.com/projects/%s" | ||
stages = ["ga", "beta"] | ||
} | ||
`, project), | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckGoogleIamTestablePermissionsMeta( | ||
project, | ||
"data.google_iam_testable_permissions.perms", | ||
[]string{"GA", "BETA"}, | ||
"", | ||
), | ||
), | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccCheckGoogleIamTestablePermissionsMeta(project string, n string, expectedStages []string, expectedSupportLevel string) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
rs, ok := s.RootModule().Resources[n] | ||
if !ok { | ||
return fmt.Errorf("Can't find perms data source: %s", n) | ||
} | ||
expectedId := fmt.Sprintf("//cloudresourcemanager.googleapis.com/projects/%s", project) | ||
if rs.Primary.ID != expectedId { | ||
return fmt.Errorf("perms data source ID not set.") | ||
} | ||
attrs := rs.Primary.Attributes | ||
count, ok := attrs["permissions.#"] | ||
if !ok { | ||
return fmt.Errorf("can't find 'permsissions' attribute") | ||
} | ||
permCount, err := strconv.Atoi(count) | ||
if err != nil { | ||
return err | ||
} | ||
if permCount < 2 { | ||
return fmt.Errorf("count should be greater than 2") | ||
} | ||
foundStageCounter := len(expectedStages) | ||
foundSupport := false | ||
|
||
for i := 0; i < permCount; i++ { | ||
for s := 0; s < len(expectedStages); s++ { | ||
stageKey := "permissions." + strconv.Itoa(i) + ".stage" | ||
supportKey := "permissions." + strconv.Itoa(i) + ".custom_support_level" | ||
if stringInSlice(expectedStages, attrs[stageKey]) { | ||
foundStageCounter -= 1 | ||
} | ||
if attrs[supportKey] == expectedSupportLevel { | ||
foundSupport = true | ||
} | ||
if foundSupport && foundStageCounter == 0 { | ||
return nil | ||
} | ||
} | ||
} | ||
|
||
if foundSupport { // This means we didn't find a stage | ||
return fmt.Errorf("Could not find stages %v in output", expectedStages) | ||
} | ||
if foundStageCounter == 0 { // This meads we didn't fins a custom_support_level | ||
return fmt.Errorf("Could not find custom_support_level %s in output", expectedSupportLevel) | ||
} | ||
return fmt.Errorf("Unable to find customSupportLevel or stages in output") | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
--- | ||
subcategory: "Cloud Platform" | ||
layout: "google" | ||
page_title: "Google: google_projects" | ||
sidebar_current: "docs-google-datasource-iam-testable-permissions" | ||
description: |- | ||
Retrieve a list of testable permissions for a resource. Testable permissions mean the permissions that user can add or remove in a role at a given resource. The resource can be referenced either via the full resource name or via a URI. | ||
--- | ||
|
||
# google\_iam\_testable\_permissions | ||
|
||
Retrieve a list of testable permissions for a resource. Testable permissions mean the permissions that user can add or remove in a role at a given resource. The resource can be referenced either via the full resource name or via a URI. | ||
|
||
## Example Usage - searching for projects about to be deleted in an org | ||
|
||
```hcl | ||
data "google_iam_testable_permissions" "perms" { | ||
full_resource_name = "//cloudresourcemanager.googleapis.com/projects/my-project" | ||
stages = ["GA", "BETA"] | ||
} | ||
``` | ||
|
||
## Argument Reference | ||
|
||
The following arguments are supported: | ||
|
||
* `full_resource_name` - (Required) See [full resource name documentation](https://cloud.google.com/apis/design/resource_names#full_resource_name) for more detail. | ||
* `stages` - (Optional) The acceptable release stages of the permission in the output. Note that `BETA` does not include permissions in `GA`, but you can specify both with `["GA", "BETA"]` for example. Can be a list of `"ALPHA"`, `"BETA"`, `"GA"`, `"DEPRECATED"`. Default is `["GA"]`. | ||
* `custom_support_level` - (Optional) The level of support for custom roles. Can be one of `"NOT_SUPPORTED"`, `"SUPPORTED"`, `"TESTING"`. Default is `"SUPPORTED"` | ||
|
||
## Attributes Reference | ||
|
||
The following attributes are exported: | ||
|
||
* `permissions` - A list of permissions matching the provided input. Structure is defined below. | ||
|
||
The `permissions` block supports: | ||
|
||
* `name` - Name of the permission. | ||
* `title` - Human readable title of the permission. | ||
* `stage` - Release stage of the permission. | ||
* `custom_support_level` - The the support level of this permission for custom roles. | ||
* `api_disabled` - Whether the corresponding API has been enabled for the resource. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as below comment