Skip to content

Commit

Permalink
Add billing subaccount (#2640) (#2788)
Browse files Browse the repository at this point in the history
Signed-off-by: Modular Magician <magic-modules@google.com>
  • Loading branch information
modular-magician authored Dec 15, 2020
1 parent 953dec2 commit 492a719
Show file tree
Hide file tree
Showing 7 changed files with 355 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .changelog/2640.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-resource
`google_billing_subaccount`
```
1 change: 1 addition & 0 deletions google-beta/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -1118,6 +1118,7 @@ func ResourceMapWithErrors() (map[string]*schema.Resource, error) {
"google_billing_account_iam_binding": ResourceIamBinding(IamBillingAccountSchema, NewBillingAccountIamUpdater, BillingAccountIdParseFunc),
"google_billing_account_iam_member": ResourceIamMember(IamBillingAccountSchema, NewBillingAccountIamUpdater, BillingAccountIdParseFunc),
"google_billing_account_iam_policy": ResourceIamPolicy(IamBillingAccountSchema, NewBillingAccountIamUpdater, BillingAccountIdParseFunc),
"google_billing_subaccount": resourceBillingSubaccount(),
"google_cloudfunctions_function": resourceCloudFunctionsFunction(),
"google_composer_environment": resourceComposerEnvironment(),
"google_compute_attached_disk": resourceComputeAttachedDisk(),
Expand Down
9 changes: 9 additions & 0 deletions google-beta/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ type VcrSource struct {

var sources map[string]VcrSource

var masterBillingAccountEnvVars = []string{
"GOOGLE_MASTER_BILLING_ACCOUNT",
}

func init() {
configs = make(map[string]*Config)
sources = make(map[string]VcrSource)
Expand Down Expand Up @@ -912,6 +916,11 @@ func getTestBillingAccountFromEnv(t *testing.T) string {
return multiEnvSearch(billingAccountEnvVars)
}

func getTestMasterBillingAccountFromEnv(t *testing.T) string {
skipIfEnvNotSet(t, masterBillingAccountEnvVars...)
return multiEnvSearch(masterBillingAccountEnvVars)
}

func getTestServiceAccountFromEnv(t *testing.T) string {
skipIfEnvNotSet(t, serviceAccountEnvVars...)
return multiEnvSearch(serviceAccountEnvVars)
Expand Down
157 changes: 157 additions & 0 deletions google-beta/resource_google_billing_subaccount.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package google

import (
"fmt"
"strings"
"time"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"google.golang.org/api/cloudbilling/v1"
)

func resourceBillingSubaccount() *schema.Resource {
return &schema.Resource{
Create: resourceBillingSubaccountCreate,
Read: resourceBillingSubaccountRead,
Delete: resourceBillingSubaccountDelete,
Update: resourceBillingSubaccountUpdate,

Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"display_name": {
Type: schema.TypeString,
Required: true,
},
"master_billing_account": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
DiffSuppressFunc: compareSelfLinkOrResourceName,
},
"deletion_policy": {
Type: schema.TypeString,
Optional: true,
Default: "",
ValidateFunc: validation.StringInSlice([]string{"RENAME_ON_DESTROY", ""}, false),
},
"billing_account_id": {
Type: schema.TypeString,
Computed: true,
},
"name": {
Type: schema.TypeString,
Computed: true,
},
"open": {
Type: schema.TypeBool,
Computed: true,
},
},
}
}

func resourceBillingSubaccountCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
userAgent, err := generateUserAgentString(d, config.userAgent)
if err != nil {
return err
}

displayName := d.Get("display_name").(string)
masterBillingAccount := d.Get("master_billing_account").(string)

billingAccount := &cloudbilling.BillingAccount{
DisplayName: displayName,
MasterBillingAccount: canonicalBillingAccountName(masterBillingAccount),
}

res, err := config.NewBillingClient(userAgent).BillingAccounts.Create(billingAccount).Do()
if err != nil {
return fmt.Errorf("Error creating billing subaccount '%s' in master account '%s': %s", displayName, masterBillingAccount, err)
}

d.SetId(res.Name)

return resourceBillingSubaccountRead(d, meta)
}

func resourceBillingSubaccountRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
userAgent, err := generateUserAgentString(d, config.userAgent)
if err != nil {
return err
}

id := d.Id()

billingAccount, err := config.NewBillingClient(userAgent).BillingAccounts.Get(d.Id()).Do()
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("Billing Subaccount Not Found : %s", id))
}

if err := d.Set("name", billingAccount.Name); err != nil {
return fmt.Errorf("Error setting name: %s", err)
}
if err := d.Set("display_name", billingAccount.DisplayName); err != nil {
return fmt.Errorf("Error setting display_na,e: %s", err)
}
if err := d.Set("open", billingAccount.Open); err != nil {
return fmt.Errorf("Error setting open: %s", err)
}
if err := d.Set("master_billing_account", billingAccount.MasterBillingAccount); err != nil {
return fmt.Errorf("Error setting master_billing_account: %s", err)
}
if err := d.Set("billing_account_id", strings.TrimPrefix(d.Get("name").(string), "billingAccounts/")); err != nil {
return fmt.Errorf("Error setting billing_account_id: %s", err)
}

return nil
}

func resourceBillingSubaccountUpdate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
userAgent, err := generateUserAgentString(d, config.userAgent)
if err != nil {
return err
}

if ok := d.HasChange("display_name"); ok {
billingAccount := &cloudbilling.BillingAccount{
DisplayName: d.Get("display_name").(string),
}
_, err := config.NewBillingClient(userAgent).BillingAccounts.Patch(d.Id(), billingAccount).UpdateMask("display_name").Do()
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("Error updating billing account : %s", d.Id()))
}
}
return resourceBillingSubaccountRead(d, meta)
}

func resourceBillingSubaccountDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
userAgent, err := generateUserAgentString(d, config.userAgent)
if err != nil {
return err
}

deletionPolicy := d.Get("deletion_policy").(string)

if deletionPolicy == "RENAME_ON_DESTROY" {
t := time.Now()
billingAccount := &cloudbilling.BillingAccount{
DisplayName: "Terraform Destroyed " + t.Format("20060102150405"),
}
_, err := config.NewBillingClient(userAgent).BillingAccounts.Patch(d.Id(), billingAccount).UpdateMask("display_name").Do()
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("Error updating billing account : %s", d.Id()))
}
}

d.SetId("")

return nil
}
131 changes: 131 additions & 0 deletions google-beta/resource_google_billing_subaccount_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package google

import (
"fmt"
"strings"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)

func TestAccBillingSubaccount_renameOnDestroy(t *testing.T) {
t.Parallel()

masterBilling := getTestMasterBillingAccountFromEnv(t)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckGoogleBillingSubaccountRenameOnDestroy,
Steps: []resource.TestStep{
{
// Test Billing Subaccount creation
Config: testAccBillingSubccount_renameOnDestroy(masterBilling),
Check: testAccCheckGoogleBillingSubaccountExists("subaccount_with_rename_on_destroy"),
},
},
})
}

func TestAccBillingSubaccount_basic(t *testing.T) {
t.Parallel()

masterBilling := getTestMasterBillingAccountFromEnv(t)
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
Steps: []resource.TestStep{
{
// Test Billing Subaccount creation
Config: testAccBillingSubccount_basic(masterBilling),
Check: testAccCheckGoogleBillingSubaccountExists("subaccount"),
},
{
ResourceName: "google_billing_subaccount.subaccount",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"deletion_policy"},
},
{
// Test Billing Subaccount update
Config: testAccBillingSubccount_update(masterBilling),
Check: testAccCheckGoogleBillingSubaccountExists("subaccount"),
},
{
ResourceName: "google_billing_subaccount.subaccount",
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"deletion_policy"},
},
},
})
}

func testAccBillingSubccount_basic(masterBillingAccountId string) string {
return fmt.Sprintf(`
resource "google_billing_subaccount" "subaccount" {
display_name = "Test Billing Subaccount"
master_billing_account = "%s"
}
`, masterBillingAccountId)
}

func testAccBillingSubccount_update(masterBillingAccountId string) string {
return fmt.Sprintf(`
resource "google_billing_subaccount" "subaccount" {
display_name = "Rename Test Billing Subaccount"
master_billing_account = "%s"
}
`, masterBillingAccountId)
}

func testAccBillingSubccount_renameOnDestroy(masterBillingAccountId string) string {
return fmt.Sprintf(`
resource "google_billing_subaccount" "subaccount_with_rename_on_destroy" {
display_name = "Test Billing Subaccount (Rename on Destroy)"
master_billing_account = "%s"
deletion_policy = "RENAME_ON_DESTROY"
}
`, masterBillingAccountId)
}

func testAccCheckGoogleBillingSubaccountExists(bindingResourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
subaccount, ok := s.RootModule().Resources["google_billing_subaccount."+bindingResourceName]
if !ok {
return fmt.Errorf("Not found: %s", bindingResourceName)
}

config := testAccProvider.Meta().(*Config)
_, err := config.NewBillingClient(config.userAgent).BillingAccounts.Get(subaccount.Primary.ID).Do()
if err != nil {
return err
}

return nil
}
}

func testAccCheckGoogleBillingSubaccountRenameOnDestroy(s *terraform.State) error {
for name, rs := range s.RootModule().Resources {
if rs.Type != "google_billing_subaccount" {
continue
}
if strings.HasPrefix(name, "data.") {
continue
}

config := testAccProvider.Meta().(*Config)

res, err := config.NewBillingClient(config.userAgent).BillingAccounts.Get(rs.Primary.ID).Do()
if err != nil {
return err
}

if !strings.HasPrefix(res.DisplayName, "Terraform Destroyed") {
return fmt.Errorf("Billing account %s was not renamed on destroy", rs.Primary.ID)
}
}

return nil
}
50 changes: 50 additions & 0 deletions website/docs/r/google_billing_subaccount.html.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
subcategory: "Cloud Platform"
layout: "google"
page_title: "Google: google_billing_subaccount"
sidebar_current: "docs-google-billing-subaccount"
description: |-
Allows management of a Google Cloud Billing Subaccount.
---

# google\_billing\_subaccount

Allows creation and management of a Google Cloud Billing Subaccount.

!> **WARNING:** Deleting this Terraform resource will not delete or close the billing subaccount.

```hcl
resource "google_billing_subaccount" "subaccount" {
display_name = "My Billing Account"
master_billing_account = "012345-567890-ABCDEF"
}
```

## Argument Reference

* `display_name` (Required) - The display name of the billing account.

* `master_billing_account` (Required) - The name of the master billing account that the subaccount
will be created under in the form `{billing_account_id}` or `billingAccounts/{billing_account_id}`.

* `deletion_policy` (Optional) - If set to "RENAME_ON_DESTROY" the billing account display_name
will be changed to "Terraform Destroyed" along with a timestamp. If set to "" this will not occur.
Default is "".

## Attributes Reference

The following additional attributes are exported:

* `open` - `true` if the billing account is open, `false` if the billing account is closed.

* `name` - The resource name of the billing account in the form `billingAccounts/{billing_account_id}`.

* `billing_account_id` - The billing account id.

## Import

Billing Subaccounts can be imported using any of these accepted formats:

```
$ terraform import google_billing_subaccount.default billingAccounts/{billing_account_id}
```
4 changes: 4 additions & 0 deletions website/google.erb
Original file line number Diff line number Diff line change
Expand Up @@ -1036,6 +1036,10 @@
<a href="#">Resources</a>
<ul class="nav nav-auto-expand">

<li>
<a href="/docs/providers/google/r/google_billing_subaccount.html">google_billing_subaccount</a>
</li>

<li>
<a href="/docs/providers/google/r/google_folder.html">google_folder</a>
</li>
Expand Down

0 comments on commit 492a719

Please sign in to comment.