Add new resource to support IAM custom project roles. (#709)

* Upgrade iam client to latest version
* Add new resource to support IAM custom roles.
* Add documentation
This commit is contained in:
Vincent Roseberry 2017-11-10 11:01:33 -08:00 committed by GitHub
parent 756dec44d8
commit ab68b06af5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 5496 additions and 1067 deletions

View File

@ -0,0 +1,29 @@
package google
import (
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"testing"
)
func TestAccGoogleProjectIamCustomRole_import(t *testing.T) {
t.Parallel()
roleId := "tfIamRole" + acctest.RandString(10)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckGoogleProjectIamCustomRoleDestroy,
Steps: []resource.TestStep{
{
Config: testAccCheckGoogleProjectIamCustomRole_update(roleId),
},
{
ResourceName: "google_project_iam_custom_role.foo",
ImportState: true,
ImportStateVerify: true,
},
},
})
}

View File

@ -130,6 +130,7 @@ func Provider() terraform.ResourceProvider {
"google_project_iam_binding": resourceGoogleProjectIamBinding(),
"google_project_iam_member": resourceGoogleProjectIamMember(),
"google_project_service": resourceGoogleProjectService(),
"google_project_iam_custom_role": resourceGoogleProjectIamCustomRole(),
"google_project_services": resourceGoogleProjectServices(),
"google_pubsub_topic": resourcePubsubTopic(),
"google_pubsub_subscription": resourcePubsubSubscription(),

View File

@ -0,0 +1,170 @@
package google
import (
"fmt"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
"google.golang.org/api/iam/v1"
)
func resourceGoogleProjectIamCustomRole() *schema.Resource {
return &schema.Resource{
Create: resourceGoogleProjectIamCustomRoleCreate,
Read: resourceGoogleProjectIamCustomRoleRead,
Update: resourceGoogleProjectIamCustomRoleUpdate,
Delete: resourceGoogleProjectIamCustomRoleDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Schema: map[string]*schema.Schema{
"role_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"title": {
Type: schema.TypeString,
Required: true,
},
"permissions": {
Type: schema.TypeSet,
Required: true,
MinItems: 1,
Elem: &schema.Schema{Type: schema.TypeString},
},
"project": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
"stage": {
Type: schema.TypeString,
Optional: true,
Default: "GA",
ValidateFunc: validation.StringInSlice([]string{"ALPHA", "BETA", "GA", "DEPRECATED", "DISABLED", "EAP"}, false),
},
"description": {
Type: schema.TypeString,
Optional: true,
},
"deleted": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
},
}
}
func resourceGoogleProjectIamCustomRoleCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
project, err := getProject(d, config)
if err != nil {
return err
}
if d.Get("deleted").(bool) {
return fmt.Errorf("Cannot create a custom project role with a deleted state. `deleted` field should be false.")
}
role, err := config.clientIAM.Projects.Roles.Create("projects/"+project, &iam.CreateRoleRequest{
RoleId: d.Get("role_id").(string),
Role: &iam.Role{
Title: d.Get("title").(string),
Description: d.Get("description").(string),
Stage: d.Get("stage").(string),
IncludedPermissions: convertStringSet(d.Get("permissions").(*schema.Set)),
},
}).Do()
if err != nil {
return fmt.Errorf("Error creating the custom project role %s: %s", d.Get("title").(string), err)
}
d.SetId(role.Name)
return resourceGoogleProjectIamCustomRoleRead(d, meta)
}
func resourceGoogleProjectIamCustomRoleRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
role, err := config.clientIAM.Projects.Roles.Get(d.Id()).Do()
if err != nil {
return handleNotFoundError(err, d, d.Id())
}
d.Set("role_id", GetResourceNameFromSelfLink(role.Name))
d.Set("title", role.Title)
d.Set("description", role.Description)
d.Set("permissions", role.IncludedPermissions)
d.Set("stage", role.Stage)
d.Set("deleted", role.Deleted)
return nil
}
func resourceGoogleProjectIamCustomRoleUpdate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
d.Partial(true)
if d.HasChange("deleted") {
if d.Get("deleted").(bool) {
if err := resourceGoogleProjectIamCustomRoleDelete(d, meta); err != nil {
return err
}
} else {
if err := resourceGoogleProjectIamCustomRoleUndelete(d, meta); err != nil {
return err
}
}
d.SetPartial("deleted")
}
if d.HasChange("title") || d.HasChange("description") || d.HasChange("stage") || d.HasChange("permissions") {
_, err := config.clientIAM.Projects.Roles.Patch(d.Id(), &iam.Role{
Title: d.Get("title").(string),
Description: d.Get("description").(string),
Stage: d.Get("stage").(string),
IncludedPermissions: convertStringSet(d.Get("permissions").(*schema.Set)),
}).Do()
if err != nil {
return fmt.Errorf("Error updating the custom project role %s: %s", d.Get("title").(string), err)
}
d.SetPartial("title")
d.SetPartial("description")
d.SetPartial("stage")
d.SetPartial("permissions")
}
d.Partial(false)
return nil
}
func resourceGoogleProjectIamCustomRoleDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
_, err := config.clientIAM.Projects.Roles.Delete(d.Id()).Do()
if err != nil {
return fmt.Errorf("Error deleting the custom project role %s: %s", d.Get("title").(string), err)
}
return nil
}
func resourceGoogleProjectIamCustomRoleUndelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
_, err := config.clientIAM.Projects.Roles.Undelete(d.Id(), &iam.UndeleteRoleRequest{}).Do()
if err != nil {
return fmt.Errorf("Error undeleting the custom project role %s: %s", d.Get("title").(string), err)
}
return nil
}

View File

@ -0,0 +1,195 @@
package google
import (
"fmt"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"reflect"
"sort"
"testing"
)
func TestAccGoogleProjectIamCustomRole_basic(t *testing.T) {
t.Parallel()
roleId := "tfIamCustomRole" + acctest.RandString(10)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckGoogleProjectIamCustomRoleDestroy,
Steps: []resource.TestStep{
{
Config: testAccCheckGoogleProjectIamCustomRole_basic(roleId),
Check: testAccCheckGoogleProjectIamCustomRole(
"google_project_iam_custom_role.foo",
"My Custom Role",
"foo",
"GA",
[]string{"iam.roles.list"}),
},
{
Config: testAccCheckGoogleProjectIamCustomRole_update(roleId),
Check: testAccCheckGoogleProjectIamCustomRole(
"google_project_iam_custom_role.foo",
"My Custom Role Updated",
"bar",
"BETA",
[]string{"iam.roles.list", "iam.roles.create", "iam.roles.delete"}),
},
},
})
}
func TestAccGoogleProjectIamCustomRole_undelete(t *testing.T) {
t.Parallel()
roleId := "tfIamCustomRole" + acctest.RandString(10)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckGoogleProjectIamCustomRoleDestroy,
Steps: []resource.TestStep{
{
Config: testAccCheckGoogleProjectIamCustomRole_basic(roleId),
Check: testAccCheckGoogleProjectIamCustomRoleDeletionStatus("google_project_iam_custom_role.foo", false),
},
// Soft-delete
{
Config: testAccCheckGoogleProjectIamCustomRole_deleted(roleId),
Check: testAccCheckGoogleProjectIamCustomRoleDeletionStatus("google_project_iam_custom_role.foo", true),
},
// Undelete
{
Config: testAccCheckGoogleProjectIamCustomRole_basic(roleId),
Check: testAccCheckGoogleProjectIamCustomRoleDeletionStatus("google_project_iam_custom_role.foo", false),
},
},
})
}
func testAccCheckGoogleProjectIamCustomRoleDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
for _, rs := range s.RootModule().Resources {
if rs.Type != "google_project_iam_custom_role" {
continue
}
role, err := config.clientIAM.Projects.Roles.Get(rs.Primary.ID).Do()
if err != nil {
return err
}
if !role.Deleted {
return fmt.Errorf("Iam custom role still exists")
}
}
return nil
}
func testAccCheckGoogleProjectIamCustomRole(n, title, description, stage string, permissions []string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}
if rs.Primary.ID == "" {
return fmt.Errorf("No ID is set")
}
config := testAccProvider.Meta().(*Config)
role, err := config.clientIAM.Projects.Roles.Get(rs.Primary.ID).Do()
if err != nil {
return err
}
if title != role.Title {
return fmt.Errorf("Incorrect title. Expected %q, got %q", title, role.Title)
}
if description != role.Description {
return fmt.Errorf("Incorrect description. Expected %q, got %q", description, role.Description)
}
if stage != role.Stage {
return fmt.Errorf("Incorrect stage. Expected %q, got %q", stage, role.Stage)
}
sort.Strings(permissions)
sort.Strings(role.IncludedPermissions)
if !reflect.DeepEqual(permissions, role.IncludedPermissions) {
return fmt.Errorf("Incorrect permissions. Expected %q, got %q", permissions, role.IncludedPermissions)
}
return nil
}
}
func testAccCheckGoogleProjectIamCustomRoleDeletionStatus(n string, deleted bool) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}
if rs.Primary.ID == "" {
return fmt.Errorf("No ID is set")
}
config := testAccProvider.Meta().(*Config)
role, err := config.clientIAM.Projects.Roles.Get(rs.Primary.ID).Do()
if err != nil {
return err
}
if deleted != role.Deleted {
return fmt.Errorf("Incorrect deletion status. Expected %t, got %t", deleted, role.Deleted)
}
return nil
}
}
func testAccCheckGoogleProjectIamCustomRole_basic(roleId string) string {
return fmt.Sprintf(`
resource "google_project_iam_custom_role" "foo" {
role_id = "%s"
title = "My Custom Role"
description = "foo"
permissions = ["iam.roles.list"]
}
`, roleId)
}
func testAccCheckGoogleProjectIamCustomRole_deleted(roleId string) string {
return fmt.Sprintf(`
resource "google_project_iam_custom_role" "foo" {
role_id = "%s"
title = "My Custom Role"
description = "foo"
permissions = ["iam.roles.list"]
deleted = true
}
`, roleId)
}
func testAccCheckGoogleProjectIamCustomRole_update(roleId string) string {
return fmt.Sprintf(`
resource "google_project_iam_custom_role" "foo" {
role_id = "%s"
title = "My Custom Role Updated"
description = "bar"
permissions = ["iam.roles.list", "iam.roles.create", "iam.roles.delete"]
stage = "BETA"
}
`, roleId)
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

6
vendor/vendor.json vendored
View File

@ -1115,10 +1115,10 @@
"revisionTime": "2017-06-08T21:27:40Z"
},
{
"checksumSHA1": "+u3FeHSXeRJZzw52OZsT3wUPb24=",
"checksumSHA1": "80I5aL8rBaTYQ2SScpc1MYRz5wY=",
"path": "google.golang.org/api/iam/v1",
"revision": "3cc2e591b550923a2c5f0ab5a803feda924d5823",
"revisionTime": "2016-11-27T23:54:21Z"
"revision": "76b09a3314405551b1f19de9944347fcda3f80ae",
"revisionTime": "2017-11-08T00:03:15Z"
},
{
"checksumSHA1": "dENAVft6XToomTHrm5J2zFt4hgU=",

View File

@ -0,0 +1,56 @@
---
layout: "google"
page_title: "Google: google_project_iam_custom_role"
sidebar_current: "docs-google-project-iam-custom-role"
description: |-
Allows management of a customized Cloud IAM project role.
---
# google\_project\_iam\_custom\_role
Allows management of a customized Cloud IAM project role. For more information see
[the official documentation](https://cloud.google.com/iam/docs/understanding-custom-roles)
and
[API](https://cloud.google.com/iam/reference/rest/v1/projects.roles).
## Example Usage
This snippet creates a customized IAM role.
```hcl
resource "google_project_iam_custom_role" "my-custom-role" {
role_id = "myCustomRole"
title = "My Custom Role"
description = "A description"
permissions = ["iam.roles.list", "iam.roles.create", "iam.roles.delete"]
}
```
## Argument Reference
The following arguments are supported:
* `role_id` - (Required) The role id to use for this role.
* `title` - (Required) A human-readable title for the role.
* `permissions` (Required) The names of the permissions this role grants when bound in an IAM policy. At least one permission must be specified.
* `project` - (Optional) The project that the service account will be created in.
Defaults to the provider project configuration.
* `stage` - (Optional) The current launch stage of the role.
Defaults to `GA`.
List of possible stages is [here](https://cloud.google.com/iam/reference/rest/v1/organizations.roles#Role.RoleLaunchStage).
* `description` - (Optional) A human-readable description for the role.
* `deleted` - (Optional) The current deleted state of the role. Defaults to `false`.
## Import
Customized IAM project role can be imported using their URI, e.g.
```
$ terraform import google_project_iam_custom_role.my-custom-role projects/my-project/roles/myCustomRole
```

View File

@ -95,6 +95,9 @@
<li<%= sidebar_current("docs-google-project-iam-policy") %>>
<a href="/docs/providers/google/r/google_project_iam_policy.html">google_project_iam_policy</a>
</li>
<li<%= sidebar_current("docs-google-project-iam-custom-role") %>>
<a href="/docs/providers/google/r/google_project_iam_custom_role.html">google_project_iam_custom_role</a>
</li>
<li<%= sidebar_current("docs-google-project-service") %>>
<a href="/docs/providers/google/r/google_project_service.html">google_project_service</a>
</li>