forked from GoogleCloudPlatform/terraform-google-conversion
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add billing account IAM (GoogleCloudPlatform#92)
- Loading branch information
1 parent
86ba1ad
commit b8895d9
Showing
7 changed files
with
455 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
package google | ||
|
||
import ( | ||
"fmt" | ||
"github.com/hashicorp/errwrap" | ||
"github.com/hashicorp/terraform/helper/schema" | ||
"google.golang.org/api/cloudbilling/v1" | ||
"google.golang.org/api/cloudresourcemanager/v1" | ||
) | ||
|
||
var IamBillingAccountSchema = map[string]*schema.Schema{ | ||
"billing_account_id": { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
}, | ||
} | ||
|
||
type BillingAccountIamUpdater struct { | ||
billingAccountId string | ||
Config *Config | ||
} | ||
|
||
func NewBillingAccountIamUpdater(d *schema.ResourceData, config *Config) (ResourceIamUpdater, error) { | ||
return &BillingAccountIamUpdater{ | ||
billingAccountId: canonicalBillingAccountId(d.Get("billing_account_id").(string)), | ||
Config: config, | ||
}, nil | ||
} | ||
|
||
func BillingAccountIdParseFunc(d *schema.ResourceData, _ *Config) error { | ||
d.Set("billing_account_id", d.Id()) | ||
return nil | ||
} | ||
|
||
func (u *BillingAccountIamUpdater) GetResourceIamPolicy() (*cloudresourcemanager.Policy, error) { | ||
return getBillingAccountIamPolicyByBillingAccountName(u.billingAccountId, u.Config) | ||
} | ||
|
||
func (u *BillingAccountIamUpdater) SetResourceIamPolicy(policy *cloudresourcemanager.Policy) error { | ||
billingPolicy, err := resourceManagerToBillingPolicy(policy) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
_, err = u.Config.clientBilling.BillingAccounts.SetIamPolicy("billingAccounts/"+u.billingAccountId, &cloudbilling.SetIamPolicyRequest{ | ||
Policy: billingPolicy, | ||
}).Do() | ||
|
||
if err != nil { | ||
return errwrap.Wrapf(fmt.Sprintf("Error setting IAM policy for %s: {{err}}", u.DescribeResource()), err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (u *BillingAccountIamUpdater) GetResourceId() string { | ||
return u.billingAccountId | ||
} | ||
|
||
func (u *BillingAccountIamUpdater) GetMutexKey() string { | ||
return fmt.Sprintf("iam-billing-account-%s", u.billingAccountId) | ||
} | ||
|
||
func (u *BillingAccountIamUpdater) DescribeResource() string { | ||
return fmt.Sprintf("billingAccount %q", u.billingAccountId) | ||
} | ||
|
||
func canonicalBillingAccountId(resource string) string { | ||
return resource | ||
} | ||
|
||
func resourceManagerToBillingPolicy(p *cloudresourcemanager.Policy) (*cloudbilling.Policy, error) { | ||
out := &cloudbilling.Policy{} | ||
err := Convert(p, out) | ||
if err != nil { | ||
return nil, errwrap.Wrapf("Cannot convert a v1 policy to a billing policy: {{err}}", err) | ||
} | ||
return out, nil | ||
} | ||
|
||
func billingToResourceManagerPolicy(p *cloudbilling.Policy) (*cloudresourcemanager.Policy, error) { | ||
out := &cloudresourcemanager.Policy{} | ||
err := Convert(p, out) | ||
if err != nil { | ||
return nil, errwrap.Wrapf("Cannot convert a billing policy to a v1 policy: {{err}}", err) | ||
} | ||
return out, nil | ||
} | ||
|
||
// Retrieve the existing IAM Policy for a billing account | ||
func getBillingAccountIamPolicyByBillingAccountName(resource string, config *Config) (*cloudresourcemanager.Policy, error) { | ||
p, err := config.clientBilling.BillingAccounts.GetIamPolicy("billingAccounts/" + resource).Do() | ||
|
||
if err != nil { | ||
return nil, errwrap.Wrapf(fmt.Sprintf("Error retrieving IAM policy for billing account %q: {{err}}", resource), err) | ||
} | ||
|
||
v1Policy, err := billingToResourceManagerPolicy(p) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return v1Policy, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
178 changes: 178 additions & 0 deletions
178
google-beta/resource_google_billing_account_iam_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
package google | ||
|
||
import ( | ||
"fmt" | ||
"reflect" | ||
"sort" | ||
"testing" | ||
|
||
"github.com/hashicorp/terraform/helper/acctest" | ||
"github.com/hashicorp/terraform/helper/resource" | ||
"github.com/hashicorp/terraform/terraform" | ||
) | ||
|
||
func TestAccBillingAccountIam(t *testing.T) { | ||
t.Parallel() | ||
|
||
billing := getTestBillingAccountFromEnv(t) | ||
account := acctest.RandomWithPrefix("tf-test") | ||
role := "roles/billing.viewer" | ||
resource.Test(t, resource.TestCase{ | ||
PreCheck: func() { testAccPreCheck(t) }, | ||
Providers: testAccProviders, | ||
Steps: []resource.TestStep{ | ||
{ | ||
// Test Iam Binding creation | ||
Config: testAccBillingAccountIamBinding_basic(account, billing, role), | ||
Check: testAccCheckGoogleBillingAccountIamBindingExists("foo", role, []string{ | ||
fmt.Sprintf("serviceAccount:%s@%s.iam.gserviceaccount.com", account, getTestProjectFromEnv()), | ||
}), | ||
}, | ||
{ | ||
ResourceName: "google_billing_account_iam_binding.foo", | ||
ImportStateId: fmt.Sprintf("%s roles/billing.viewer", billing), | ||
ImportState: true, | ||
ImportStateVerify: true, | ||
}, | ||
{ | ||
// Test Iam Binding update | ||
Config: testAccBillingAccountIamBinding_update(account, billing, role), | ||
Check: testAccCheckGoogleBillingAccountIamBindingExists("foo", role, []string{ | ||
fmt.Sprintf("serviceAccount:%s@%s.iam.gserviceaccount.com", account, getTestProjectFromEnv()), | ||
fmt.Sprintf("serviceAccount:%s-2@%s.iam.gserviceaccount.com", account, getTestProjectFromEnv()), | ||
}), | ||
}, | ||
{ | ||
ResourceName: "google_billing_account_iam_binding.foo", | ||
ImportStateId: fmt.Sprintf("%s roles/billing.viewer", billing), | ||
ImportState: true, | ||
ImportStateVerify: true, | ||
}, | ||
{ | ||
// Test Iam Member creation (no update for member, no need to test) | ||
Config: testAccBillingAccountIamMember_basic(account, billing, role), | ||
Check: testAccCheckGoogleBillingAccountIamMemberExists("foo", "roles/billing.viewer", | ||
fmt.Sprintf("serviceAccount:%s@%s.iam.gserviceaccount.com", account, getTestProjectFromEnv()), | ||
), | ||
}, | ||
{ | ||
ResourceName: "google_billing_account_iam_member.foo", | ||
ImportStateId: fmt.Sprintf("%s roles/billing.viewer serviceAccount:%s@%s.iam.gserviceaccount.com", billing, account, getTestProjectFromEnv()), | ||
ImportState: true, | ||
ImportStateVerify: true, | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
func testAccCheckGoogleBillingAccountIamBindingExists(bindingResourceName, role string, members []string) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
bindingRs, ok := s.RootModule().Resources["google_billing_account_iam_binding."+bindingResourceName] | ||
if !ok { | ||
return fmt.Errorf("Not found: %s", bindingResourceName) | ||
} | ||
|
||
config := testAccProvider.Meta().(*Config) | ||
p, err := config.clientBilling.BillingAccounts.GetIamPolicy("billingAccounts/" + bindingRs.Primary.Attributes["billing_account_id"]).Do() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for _, binding := range p.Bindings { | ||
if binding.Role == role { | ||
sort.Strings(members) | ||
sort.Strings(binding.Members) | ||
|
||
if reflect.DeepEqual(members, binding.Members) { | ||
return nil | ||
} | ||
|
||
return fmt.Errorf("Binding found but expected members is %v, got %v", members, binding.Members) | ||
} | ||
} | ||
|
||
return fmt.Errorf("No binding for role %q", role) | ||
} | ||
} | ||
|
||
func testAccCheckGoogleBillingAccountIamMemberExists(n, role, member string) resource.TestCheckFunc { | ||
return func(s *terraform.State) error { | ||
rs, ok := s.RootModule().Resources["google_billing_account_iam_member."+n] | ||
if !ok { | ||
return fmt.Errorf("Not found: %s", n) | ||
} | ||
|
||
config := testAccProvider.Meta().(*Config) | ||
p, err := config.clientBilling.BillingAccounts.GetIamPolicy("billingAccounts/" + rs.Primary.Attributes["billing_account_id"]).Do() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
for _, binding := range p.Bindings { | ||
if binding.Role == role { | ||
for _, m := range binding.Members { | ||
if m == member { | ||
return nil | ||
} | ||
} | ||
|
||
return fmt.Errorf("Missing member %q, got %v", member, binding.Members) | ||
} | ||
} | ||
|
||
return fmt.Errorf("No binding for role %q", role) | ||
} | ||
} | ||
|
||
func testAccBillingAccountIamBinding_basic(account, billingAccountId, role string) string { | ||
return fmt.Sprintf(` | ||
resource "google_service_account" "test-account" { | ||
account_id = "%s" | ||
display_name = "Iam Testing Account" | ||
} | ||
resource "google_billing_account_iam_binding" "foo" { | ||
billing_account_id = "%s" | ||
role = "%s" | ||
members = ["serviceAccount:${google_service_account.test-account.email}"] | ||
} | ||
`, account, billingAccountId, role) | ||
} | ||
|
||
func testAccBillingAccountIamBinding_update(account, billingAccountId, role string) string { | ||
return fmt.Sprintf(` | ||
resource "google_service_account" "test-account" { | ||
account_id = "%s" | ||
display_name = "Iam Testing Account" | ||
} | ||
resource "google_service_account" "test-account-2" { | ||
account_id = "%s-2" | ||
display_name = "Iam Testing Account" | ||
} | ||
resource "google_billing_account_iam_binding" "foo" { | ||
billing_account_id = "%s" | ||
role = "%s" | ||
members = [ | ||
"serviceAccount:${google_service_account.test-account.email}", | ||
"serviceAccount:${google_service_account.test-account-2.email}" | ||
] | ||
} | ||
`, account, account, billingAccountId, role) | ||
} | ||
|
||
func testAccBillingAccountIamMember_basic(account, billingAccountId, role string) string { | ||
return fmt.Sprintf(` | ||
resource "google_service_account" "test-account" { | ||
account_id = "%s" | ||
display_name = "Iam Testing Account" | ||
} | ||
resource "google_billing_account_iam_member" "foo" { | ||
billing_account_id = "%s" | ||
role = "%s" | ||
member = "serviceAccount:${google_service_account.test-account.email}" | ||
} | ||
`, account, billingAccountId, role) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
--- | ||
layout: "google" | ||
page_title: "Google: google_billing_account_iam_binding" | ||
sidebar_current: "docs-google-billing-account-iam-binding" | ||
description: |- | ||
Allows management of a single binding with an IAM policy for a Google Cloud Platform Billing Account. | ||
--- | ||
|
||
# google\_billing_account_\_iam\_binding | ||
|
||
Allows creation and management of a single binding within IAM policy for | ||
an existing Google Cloud Platform Billing Account. | ||
|
||
~> **Note:** This resource __must not__ be used in conjunction with | ||
`google_billing_account_iam_member` for the __same role__ or they will fight over | ||
what your policy should be. | ||
|
||
## Example Usage | ||
|
||
```hcl | ||
resource "google_billing_account_iam_binding" "binding" { | ||
billing_account_id = "00AA00-000AAA-00AA0A" | ||
role = "roles/billing.viewer" | ||
members = [ | ||
"user:[email protected]", | ||
] | ||
} | ||
``` | ||
|
||
## Argument Reference | ||
|
||
The following arguments are supported: | ||
|
||
* `billing_account_id` - (Required) The billing account id. | ||
|
||
* `role` - (Required) The role that should be applied. | ||
|
||
* `members` - (Required) A list of users that the role should apply to. | ||
|
||
## Attributes Reference | ||
|
||
In addition to the arguments listed above, the following computed attributes are | ||
exported: | ||
|
||
* `etag` - (Computed) The etag of the billing account's IAM policy. | ||
|
||
## Import | ||
|
||
IAM binding imports use space-delimited identifiers; first the resource in question and then the role. These bindings can be imported using the `billing_account_id` and role, e.g. | ||
|
||
``` | ||
$ terraform import google_billing_account_iam_binding.binding "your-billing-account-id roles/viewer" | ||
``` |
Oops, something went wrong.