diff --git a/google/data_source_google_billing_account.go b/google/data_source_google_billing_account.go new file mode 100644 index 00000000..39b291a7 --- /dev/null +++ b/google/data_source_google_billing_account.go @@ -0,0 +1,131 @@ +package google + +import ( + "fmt" + "net/http" + "strings" + + "github.com/hashicorp/terraform/helper/schema" + + "google.golang.org/api/cloudbilling/v1" + "google.golang.org/api/googleapi" +) + +func dataSourceGoogleBillingAccount() *schema.Resource { + return &schema.Resource{ + Read: dataSourceBillingAccountRead, + Schema: map[string]*schema.Schema{ + "billing_account": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"display_name"}, + }, + "display_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ConflictsWith: []string{"billing_account"}, + }, + "open": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + }, + "name": { + Type: schema.TypeString, + Computed: true, + }, + "project_ids": { + Type: schema.TypeSet, + Computed: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + } +} + +func dataSourceBillingAccountRead(d *schema.ResourceData, meta interface{}) error { + config := meta.(*Config) + + open, openOk := d.GetOkExists("open") + + var billingAccount *cloudbilling.BillingAccount + if v, ok := d.GetOk("billing_account"); ok { + resp, err := config.clientBilling.BillingAccounts.Get(canonicalBillingAccountName(v.(string))).Do() + if err != nil { + if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == http.StatusNotFound { + return fmt.Errorf("Billing account not found: %s", v) + } + + return fmt.Errorf("Error reading billing account: %s", err) + } + + if openOk && resp.Open != open.(bool) { + return fmt.Errorf("Billing account not found: %s", v) + } + + billingAccount = resp + } else if v, ok := d.GetOk("display_name"); ok { + token := "" + for paginate := true; paginate; { + resp, err := config.clientBilling.BillingAccounts.List().PageToken(token).Do() + if err != nil { + return fmt.Errorf("Error reading billing accounts: %s", err) + } + + for _, ba := range resp.BillingAccounts { + if ba.DisplayName == v.(string) { + if openOk && ba.Open != open.(bool) { + continue + } + if billingAccount != nil { + return fmt.Errorf("More than one matching billing account found") + } + billingAccount = ba + } + } + + token = resp.NextPageToken + paginate = token != "" + } + + if billingAccount == nil { + return fmt.Errorf("Billing account not found: %s", v) + } + } else { + return fmt.Errorf("one of billing_account or display_name must be set") + } + + resp, err := config.clientBilling.BillingAccounts.Projects.List(billingAccount.Name).Do() + if err != nil { + return fmt.Errorf("Error reading billing account projects: %s", err) + } + projectIds := flattenBillingProjects(resp.ProjectBillingInfo) + + d.SetId(GetResourceNameFromSelfLink(billingAccount.Name)) + d.Set("name", billingAccount.Name) + d.Set("display_name", billingAccount.DisplayName) + d.Set("open", billingAccount.Open) + d.Set("project_ids", projectIds) + + return nil +} + +func canonicalBillingAccountName(ba string) string { + if strings.HasPrefix(ba, "billingAccounts/") { + return ba + } + + return "billingAccounts/" + ba +} + +func flattenBillingProjects(billingProjects []*cloudbilling.ProjectBillingInfo) []string { + projectIds := make([]string, len(billingProjects)) + for i, billingProject := range billingProjects { + projectIds[i] = billingProject.ProjectId + } + + return projectIds +} diff --git a/google/data_source_google_billing_account_test.go b/google/data_source_google_billing_account_test.go new file mode 100644 index 00000000..18352b68 --- /dev/null +++ b/google/data_source_google_billing_account_test.go @@ -0,0 +1,104 @@ +package google + +import ( + "fmt" + "regexp" + "testing" + + "github.com/hashicorp/terraform/helper/acctest" + + "github.com/hashicorp/terraform/helper/resource" +) + +func TestAccDataSourceGoogleBillingAccount_byFullName(t *testing.T) { + billingId := getTestBillingAccountFromEnv(t) + name := "billingAccounts/" + billingId + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckGoogleBillingAccount_byName(name), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.google_billing_account.acct", "id", billingId), + resource.TestCheckResourceAttr("data.google_billing_account.acct", "name", name), + resource.TestCheckResourceAttr("data.google_billing_account.acct", "open", "true"), + ), + }, + }, + }) +} + +func TestAccDataSourceGoogleBillingAccount_byShortName(t *testing.T) { + billingId := getTestBillingAccountFromEnv(t) + name := "billingAccounts/" + billingId + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckGoogleBillingAccount_byName(billingId), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("data.google_billing_account.acct", "id", billingId), + resource.TestCheckResourceAttr("data.google_billing_account.acct", "name", name), + resource.TestCheckResourceAttr("data.google_billing_account.acct", "open", "true"), + ), + }, + }, + }) +} + +func TestAccDataSourceGoogleBillingAccount_byFullNameClosed(t *testing.T) { + billingId := getTestBillingAccountFromEnv(t) + name := "billingAccounts/" + billingId + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckGoogleBillingAccount_byNameClosed(name), + ExpectError: regexp.MustCompile("Billing account not found: " + name), + }, + }, + }) +} + +func TestAccDataSourceGoogleBillingAccount_byDisplayName(t *testing.T) { + name := acctest.RandString(16) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccCheckGoogleBillingAccount_byDisplayName(name), + ExpectError: regexp.MustCompile("Billing account not found: " + name), + }, + }, + }) +} + +func testAccCheckGoogleBillingAccount_byName(name string) string { + return fmt.Sprintf(` +data "google_billing_account" "acct" { + billing_account = "%s" +}`, name) +} + +func testAccCheckGoogleBillingAccount_byNameClosed(name string) string { + return fmt.Sprintf(` +data "google_billing_account" "acct" { + billing_account = "%s" + open = false +}`, name) +} + +func testAccCheckGoogleBillingAccount_byDisplayName(name string) string { + return fmt.Sprintf(` +data "google_billing_account" "acct" { + display_name = "%s" +}`, name) +} diff --git a/google/provider.go b/google/provider.go index d3a47efd..78adb957 100644 --- a/google/provider.go +++ b/google/provider.go @@ -60,6 +60,7 @@ func Provider() terraform.ResourceProvider { }, DataSourcesMap: map[string]*schema.Resource{ + "google_billing_account": dataSourceGoogleBillingAccount(), "google_dns_managed_zone": dataSourceDnsManagedZone(), "google_client_config": dataSourceGoogleClientConfig(), "google_compute_address": dataSourceGoogleComputeAddress(), diff --git a/website/docs/d/google_billing_account.html.markdown b/website/docs/d/google_billing_account.html.markdown new file mode 100644 index 00000000..edb41c5f --- /dev/null +++ b/website/docs/d/google_billing_account.html.markdown @@ -0,0 +1,46 @@ +--- +layout: "google" +page_title: "Google: google_billing_account" +sidebar_current: "docs-google-datasource-billing-account" +description: |- + Get information about a Google Billing Account. +--- + +# google\_billing\_account + +Use this data source to get information about a Google Billing Account. + +```hcl +data "google_billing_account" "acct" { + display_name = "My Billing Account" + open = true +} + +resource "google_project" "my_project" { + name = "My Project" + project_id = "your-project-id" + org_id = "1234567" + + billing_account = "${data.google_billing_account.acct.id}" +} +``` + +## Argument Reference + +The arguments of this data source act as filters for querying the available billing accounts. +The given filters must match exactly one billing account whose data will be exported as attributes. +The following arguments are supported: + +* `billing_account` (Optional) - The name of the billing account in the form `{billing_account_id}` or `billingAccounts/{billing_account_id}`. +* `display_name` (Optional) - The display name of the billing account. +* `open` (Optional) - `true` if the billing account is open, `false` if the billing account is closed. + +~> **NOTE:** One of `billing_account` or `display_name` must be specified. + +## Attributes Reference + +The following additional attributes are exported: + +* `id` - The billing account ID. +* `name` - The resource name of the billing account in the form `billingAccounts/{billing_account_id}`. +* `project_ids` - The IDs of any projects associated with the billing account. diff --git a/website/google.erb b/website/google.erb index 677bfb41..8effbbd4 100644 --- a/website/google.erb +++ b/website/google.erb @@ -13,6 +13,9 @@ > Google Cloud Platform Data Sources