Billing account module (#1743)

* initial untested draft

* readme and tests

* folder module tfdoc

* remove redundant billing cost manager role in fast stage 0

* fix FAST test
This commit is contained in:
Ludovico Magnocavallo 2023-10-15 17:02:50 +02:00 committed by GitHub
parent 3949fdc283
commit 252127bde5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 1035 additions and 322 deletions

View File

@ -29,7 +29,7 @@ The current list of modules supports most of the core foundational and networkin
Currently available modules:
- **foundational** - [billing budget](./modules/billing-budget), [Cloud Identity group](./modules/cloud-identity-group/), [folder](./modules/folder), [service accounts](./modules/iam-service-account), [logging bucket](./modules/logging-bucket), [organization](./modules/organization), [project](./modules/project), [projects-data-source](./modules/projects-data-source)
- **foundational** - [billing account](./modules/billing-account), [Cloud Identity group](./modules/cloud-identity-group/), [folder](./modules/folder), [service accounts](./modules/iam-service-account), [logging bucket](./modules/logging-bucket), [organization](./modules/organization), [project](./modules/project), [projects-data-source](./modules/projects-data-source)
- **networking** - [DNS](./modules/dns), [DNS Response Policy](./modules/dns-response-policy/), [Cloud Endpoints](./modules/endpoints), [address reservation](./modules/net-address), [NAT](./modules/net-cloudnat), [VLAN Attachment](./modules/net-vlan-attachment/), [External Application LB](./modules/net-lb-app-ext/), [External Passthrough Network LB](./modules/net-lb-ext), [Firewall policy](./modules/net-firewall-policy), [Internal Application LB](./modules/net-lb-app-int), [Internal Passthrough Network LB](./modules/net-lb-int), [Internal Proxy Network LB](./modules/net-lb-proxy-int), [IPSec over Interconnect](./modules/net-ipsec-over-interconnect), [VPC](./modules/net-vpc), [VPC firewall](./modules/net-vpc-firewall), [VPC peering](./modules/net-vpc-peering), [VPN dynamic](./modules/net-vpn-dynamic), [HA VPN](./modules/net-vpn-ha), [VPN static](./modules/net-vpn-static), [Service Directory](./modules/service-directory), [Secure Web Proxy](./modules/net-swp)
- **compute** - [VM/VM group](./modules/compute-vm), [MIG](./modules/compute-mig), [COS container](./modules/cloud-config-container/cos-generic-metadata/) (coredns, mysql, onprem, squid), [GKE cluster](./modules/gke-cluster-standard), [GKE hub](./modules/gke-hub), [GKE nodepool](./modules/gke-nodepool), [GCVE private cloud](./modules/gcve-private-cloud)
- **data** - <!-- [AlloyDB instance](./modules/alloydb-instance), --> [BigQuery dataset](./modules/bigquery-dataset), [Bigtable instance](./modules/bigtable-instance), [Dataplex](./modules/dataplex), [Dataplex DataScan](./modules/dataplex-datascan/), [Cloud SQL instance](./modules/cloudsql-instance), [Data Catalog Policy Tag](./modules/data-catalog-policy-tag), [Datafusion](./modules/datafusion), [Dataproc](./modules/dataproc), [GCS](./modules/gcs), [Pub/Sub](./modules/pubsub)

View File

@ -74,12 +74,3 @@ resource "google_billing_account_iam_member" "billing_ext_admin" {
role = "roles/billing.admin"
member = each.key
}
resource "google_billing_account_iam_member" "billing_ext_cost_manager" {
for_each = toset(
local.billing_mode == "resource" ? local.billing_ext_admins : []
)
billing_account_id = var.billing_account.id
role = "roles/billing.costsManager"
member = each.key
}

View File

@ -34,8 +34,7 @@ locals {
authoritative = []
additive = (
local.billing_mode != "org" ? [] : [
"roles/billing.admin",
"roles/billing.costsManager"
"roles/billing.admin"
]
)
}
@ -66,8 +65,7 @@ locals {
"roles/orgpolicy.policyAdmin"
],
local.billing_mode != "org" ? [] : [
"roles/billing.admin",
"roles/billing.costsManager"
"roles/billing.admin"
]
)
}
@ -111,8 +109,7 @@ locals {
"roles/orgpolicy.policyAdmin"
],
local.billing_mode != "org" ? [] : [
"roles/billing.admin",
"roles/billing.costsManager"
"roles/billing.admin"
]
)
}
@ -129,8 +126,7 @@ locals {
"roles/orgpolicy.policyAdmin"
],
local.billing_mode != "org" ? [] : [
"roles/billing.admin",
"roles/billing.costsManager"
"roles/billing.admin"
]
)
}
@ -148,8 +144,7 @@ locals {
# TODO: align additive roles with the README
additive = (
local.billing_mode != "org" ? [] : [
"roles/billing.admin",
"roles/billing.costsManager"
"roles/billing.admin"
]
)
}

View File

@ -30,14 +30,14 @@ These modules are used in the examples included in this repository. If you are u
## Foundational modules
- [billing budget](./billing-budget)
- [Billing account](./billing-account)
- [Cloud Identity group](./cloud-identity-group/)
- [folder](./folder)
- [service accounts](./iam-service-account)
- [logging bucket](./logging-bucket)
- [organization](./organization)
- [project](./project)
- [projects-data-source](./projects-data-source)
- [Folder](./folder)
- [Service accounts](./iam-service-account)
- [Logging bucket](./logging-bucket)
- [Organization](./organization)
- [Project](./project)
- [Projects (data source)](./projects-data-source)
## Networking modules

View File

@ -0,0 +1,237 @@
# Billing Account Module
This module allows managing resources and policies related to a billing account:
- IAM bindings
- log sinks
- billing budgets and their notifications
Managing billing-related resources via application default credentials [requires a billing project to be set](https://cloud.google.com/docs/authentication/troubleshoot-adc#user-creds-client-based). To configure one via Terraform you can use a snippet similar to this one:
```hcl
provider "google" {
billing_project = "my-project"
user_project_override = true
}
# tftest skip
```
<!-- BEGIN TOC -->
- [Examples](#examples)
- [IAM bindings](#iam-bindings)
- [Log sinks](#log-sinks)
- [Billing budgets](#billing-budgets)
- [PubSub update rules](#pubsub-update-rules)
- [Monitoring channels](#monitoring-channels)
- [Variables](#variables)
- [Outputs](#outputs)
<!-- END TOC -->
## Examples
### IAM bindings
Billing account IAM bindings implement [the same interface](../__docs/20230816-iam-refactor.md) used for all other modules.
```hcl
module "billing-account" {
source = "./fabric/modules/billing-account"
id = "012345-ABCDEF-012345"
group_iam = {
"billing-admins@example.org" = ["roles/billing.admin"]
}
iam = {
"roles/billing.admin" = [
"serviceAccount:foo@myprj.iam.gserviceaccount.com"
]
}
iam_bindings = {
conditional-admin = {
members = [
"serviceAccount:pf-dev@myprj.iam.gserviceaccount.com"
]
role = "roles/billing.admin"
condition = {
title = "pf-dev-conditional-billing-admin"
expression = (
"resource.matchTag('123456/environment', 'development')"
)
}
}
}
iam_bindings_additive = {
sa-net-iac-user = {
member = "serviceAccount:net-iac-0@myprj.iam.gserviceaccount.com"
role = "roles/billing.user"
}
}
}
# tftest modules=1 resources=3 inventory=iam.yaml
```
### Log sinks
Billing account log sinks use the same format used for log sinks in the resource manager modules (organization, folder, project).
```hcl
module "log-bucket-all" {
source = "./fabric/modules/logging-bucket"
parent_type = "project"
parent = "myprj"
id = "billing-account-all"
}
module "billing-account" {
source = "./fabric/modules/billing-account"
id = "012345-ABCDEF-012345"
logging_sinks = {
all = {
destination = module.log-bucket-all.id
type = "logging"
}
}
}
# tftest modules=2 resources=3 inventory=logging.yaml
```
### Billing budgets
Billing budgets expose all the attributes of the underlying resource, and allow using external notification channels, or creating them via this same module.
```hcl
module "billing-account" {
source = "./fabric/modules/billing-account"
id = "012345-ABCDEF-012345"
budgets = {
folder-net-month-current-100 = {
display_name = "100 dollars in current spend"
amount = {
units = 100
}
filter = {
period = {
calendar = "MONTH"
}
resource_ancestors = ["folders/1234567890"]
}
threshold_rules = [
{ percent = 0.5 },
{ percent = 0.75 }
]
}
}
}
# tftest modules=1 resources=1 inventory=budget-simple.yaml
```
#### PubSub update rules
Update rules can notify pubsub topics.
```hcl
module "pubsub-billing-topic" {
source = "./fabric/modules/pubsub"
project_id = "my-prj"
name = "budget-default"
}
module "billing-account" {
source = "./fabric/modules/billing-account"
id = "012345-ABCDEF-012345"
budgets = {
folder-net-month-current-100 = {
display_name = "100 dollars in current spend"
amount = {
units = 100
}
filter = {
period = {
calendar = "MONTH"
}
resource_ancestors = ["folders/1234567890"]
}
threshold_rules = [
{ percent = 0.5 },
{ percent = 0.75 }
]
update_rules = {
default = {
pubsub_topic = module.pubsub-billing-topic.id
}
}
}
}
}
# tftest modules=2 resources=2 inventory=budget-pubsub.yaml
```
#### Monitoring channels
Monitoring channels can be referenced in update rules either by passing in an existing channel id, or by using a reference to a key in the `budget_notification_channels` variable, that allows managing ad hoc monitoring channels.
<!-- markdownlint-disable MD034 -->
```hcl
module "billing-account" {
source = "./fabric/modules/billing-account"
id = "012345-ABCDEF-012345"
budget_notification_channels = {
billing-default = {
project_id = "tf-playground-simple"
type = "email"
labels = {
email_address = "gcp-billing-admins@example.com"
}
}
}
budgets = {
folder-net-month-current-100 = {
display_name = "100 dollars in current spend"
amount = {
units = 100
}
filter = {
period = {
calendar = "MONTH"
}
resource_ancestors = ["folders/1234567890"]
}
threshold_rules = [
{ percent = 0.5 },
{ percent = 0.75 }
]
update_rules = {
default = {
disable_default_iam_recipients = true
monitoring_notification_channels = ["billing-default"]
}
}
}
}
}
# tftest modules=1 resources=2 inventory=budget-monitoring-channel.yaml
```
<!-- markdownlint-enable -->
<!-- BEGIN TFDOC -->
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [id](variables.tf#L165) | Billing account id. | <code>string</code> | ✓ | |
| [budget_notification_channels](variables.tf#L17) | Notification channels used by budget alerts. | <code title="map&#40;object&#40;&#123;&#10; project_id &#61; string&#10; type &#61; string&#10; description &#61; optional&#40;string&#41;&#10; display_name &#61; optional&#40;string&#41;&#10; enabled &#61; optional&#40;bool, true&#41;&#10; force_delete &#61; optional&#40;bool&#41;&#10; labels &#61; optional&#40;map&#40;string&#41;&#41;&#10; sensitive_labels &#61; optional&#40;list&#40;object&#40;&#123;&#10; auth_token &#61; optional&#40;string&#41;&#10; password &#61; optional&#40;string&#41;&#10; service_key &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#41;&#10; user_labels &#61; optional&#40;map&#40;string&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [budgets](variables.tf#L47) | Billing budgets. Notification channels are either keys in corresponding variable, or external ids. | <code title="map&#40;object&#40;&#123;&#10; amount &#61; object&#40;&#123;&#10; currency_code &#61; optional&#40;string&#41;&#10; nanos &#61; optional&#40;number&#41;&#10; units &#61; optional&#40;number&#41;&#10; use_last_period &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#10; display_name &#61; optional&#40;string&#41;&#10; filter &#61; optional&#40;object&#40;&#123;&#10; credit_types_treatment &#61; optional&#40;object&#40;&#123;&#10; exclude_all &#61; optional&#40;bool&#41;&#10; include_specified &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; label &#61; optional&#40;object&#40;&#123;&#10; key &#61; string&#10; value &#61; string&#10; &#125;&#41;&#41;&#10; period &#61; optional&#40;object&#40;&#123;&#10; calendar &#61; optional&#40;string&#41;&#10; custom &#61; optional&#40;object&#40;&#123;&#10; start_date &#61; object&#40;&#123;&#10; day &#61; number&#10; month &#61; number&#10; year &#61; number&#10; &#125;&#41;&#10; end_date &#61; optional&#40;object&#40;&#123;&#10; day &#61; number&#10; month &#61; number&#10; year &#61; number&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;&#10; projects &#61; optional&#40;list&#40;string&#41;&#41;&#10; resource_ancestors &#61; optional&#40;list&#40;string&#41;&#41;&#10; services &#61; optional&#40;list&#40;string&#41;&#41;&#10; subaccounts &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; threshold_rules &#61; optional&#40;list&#40;object&#40;&#123;&#10; percent &#61; number&#10; forecasted_spend &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10; update_rules &#61; optional&#40;map&#40;object&#40;&#123;&#10; disable_default_iam_recipients &#61; optional&#40;bool&#41;&#10; monitoring_notification_channels &#61; optional&#40;list&#40;string&#41;&#41;&#10; pubsub_topic &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [group_iam](variables.tf#L121) | Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam](variables.tf#L128) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings](variables.tf#L135) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | <code title="map&#40;object&#40;&#123;&#10; members &#61; list&#40;string&#41;&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [iam_bindings_additive](variables.tf#L150) | Individual additive IAM bindings. Keys are arbitrary. | <code title="map&#40;object&#40;&#123;&#10; member &#61; string&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_sinks](variables.tf#L170) | Logging sinks to create for the organization. | <code title="map&#40;object&#40;&#123;&#10; destination &#61; string&#10; type &#61; string&#10; bq_partitioned_table &#61; optional&#40;bool&#41;&#10; description &#61; optional&#40;string&#41;&#10; disabled &#61; optional&#40;bool, false&#41;&#10; exclusions &#61; optional&#40;map&#40;object&#40;&#123;&#10; filter &#61; string&#10; description &#61; optional&#40;string&#41;&#10; disabled &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10; filter &#61; optional&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [projects](variables.tf#L203) | Projects associated with this billing account. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
## Outputs
| name | description | sensitive |
|---|---|:---:|
| [billing_budget_ids](outputs.tf#L17) | Billing budget ids. | |
| [monitoring_notification_channel_ids](outputs.tf#L25) | Monitoring notification channel ids. | |
<!-- END TFDOC -->

View File

@ -0,0 +1,126 @@
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
resource "google_monitoring_notification_channel" "default" {
for_each = var.budget_notification_channels
description = each.value.description
display_name = coalesce(
each.value.display_name, "Budget email notification ${each.key}."
)
project = each.value.project_id
enabled = each.value.enabled
force_delete = each.value.force_delete
type = each.value.type
labels = each.value.labels
user_labels = each.value.user_labels
dynamic "sensitive_labels" {
for_each = toset(coalesce(each.value.sensitive_labels, []))
content {
auth_token = sensitive_labels.value.auth_token
password = sensitive_labels.value.password
service_key = sensitive_labels.value.service_key
}
}
}
resource "google_billing_budget" "default" {
for_each = var.budgets
billing_account = var.id
display_name = each.value.display_name
dynamic "amount" {
for_each = each.value.amount.use_last_period == true ? [""] : []
content {
last_period_amount = true
}
}
dynamic "amount" {
for_each = each.value.amount.use_last_period != true ? [""] : []
content {
specified_amount {
currency_code = each.value.amount.currency_code
nanos = each.value.amount.nanos
units = each.value.amount.units
}
}
}
budget_filter {
calendar_period = try(each.value.filter.period.calendar, null)
credit_types_treatment = (
try(each.value.filter.credit_types_treatment.exclude_all, null) == true
? "EXCLUDE_ALL_CREDITS"
: (
try(each.value.filter.credit_types_treatment.include_specified, null) != null
? "INCLUDE_SPECIFIED_CREDITS"
: "INCLUDE_ALL_CREDITS"
)
)
labels = each.value.filter.label == null ? null : {
(each.value.filter.label.key) = each.value.filter.label.value
}
projects = each.value.filter.projects
resource_ancestors = each.value.filter.resource_ancestors
services = each.value.filter.services
subaccounts = each.value.filter.subaccounts
dynamic "custom_period" {
for_each = try(each.value.filter.period.custom, null) != null ? [""] : []
content {
start_date {
day = each.value.filter.period.custom.start_date.day
month = each.value.filter.period.custom.start_date.month
year = each.value.filter.period.custom.start_date.year
}
dynamic "end_date" {
for_each = try(each.value.filter.period.custom.end_date, null) != null ? [""] : []
content {
day = each.value.filter.period.custom.end_date.day
month = each.value.filter.period.custom.end_date.month
year = each.value.filter.period.custom.end_date.year
}
}
}
}
}
dynamic "threshold_rules" {
for_each = toset(each.value.threshold_rules)
iterator = rule
content {
threshold_percent = rule.value.percent
spend_basis = (
rule.value.forecasted_spend == true
? "FORECASTED_SPEND"
: "CURRENT_SPEND"
)
}
}
dynamic "all_updates_rule" {
for_each = each.value.update_rules
iterator = rule
content {
pubsub_topic = rule.value.pubsub_topic
schema_version = "1.0"
disable_default_iam_recipients = rule.value.disable_default_iam_recipients
monitoring_notification_channels = (
rule.value.monitoring_notification_channels == null
? null
: [
for v in rule.value.monitoring_notification_channels : try(
google_monitoring_notification_channel.default[v].id, v
)
]
)
}
}
}

View File

@ -0,0 +1,70 @@
/**
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
# tfdoc:file:description IAM bindings.
locals {
_group_iam_roles = distinct(flatten(values(var.group_iam)))
_group_iam = {
for r in local._group_iam_roles : r => [
for k, v in var.group_iam : "group:${k}" if try(index(v, r), null) != null
]
}
iam = {
for role in distinct(concat(keys(var.iam), keys(local._group_iam))) :
role => concat(
try(var.iam[role], []),
try(local._group_iam[role], [])
)
}
}
resource "google_billing_account_iam_binding" "authoritative" {
for_each = local.iam
billing_account_id = var.id
role = each.key
members = each.value
}
resource "google_billing_account_iam_binding" "bindings" {
for_each = var.iam_bindings
billing_account_id = var.id
role = each.value.role
members = each.value.members
dynamic "condition" {
for_each = each.value.condition == null ? [] : [""]
content {
expression = each.value.condition.expression
title = each.value.condition.title
description = each.value.condition.description
}
}
}
resource "google_billing_account_iam_member" "bindings" {
for_each = var.iam_bindings_additive
billing_account_id = var.id
role = each.value.role
member = each.value.member
dynamic "condition" {
for_each = each.value.condition == null ? [] : [""]
content {
expression = each.value.condition.expression
title = each.value.condition.title
description = each.value.condition.description
}
}
}

View File

@ -0,0 +1,92 @@
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
# tfdoc:file:description Log sinks and supporting resources.
locals {
sink_bindings = {
for type in ["bigquery", "pubsub", "logging", "storage"] :
type => {
for name, sink in var.logging_sinks :
name => sink
if sink.type == type
}
}
}
resource "google_logging_billing_account_sink" "sink" {
for_each = var.logging_sinks
name = each.key
description = coalesce(each.value.description, "${each.key} (Terraform-managed).")
billing_account = var.id
destination = "${each.value.type}.googleapis.com/${each.value.destination}"
filter = each.value.filter
disabled = each.value.disabled
dynamic "bigquery_options" {
for_each = each.value.type == "biquery" && each.value.bq_partitioned_table != false ? [""] : []
content {
use_partitioned_tables = each.value.bq_partitioned_table
}
}
dynamic "exclusions" {
for_each = each.value.exclusions
iterator = exclusion
content {
name = exclusion.key
filter = exclusion.value.filter
description = exclusion.value.description
disabled = exclusion.value.disabled
}
}
}
resource "google_storage_bucket_iam_member" "gcs-sinks-binding" {
for_each = local.sink_bindings["storage"]
bucket = each.value.destination
role = "roles/storage.objectCreator"
member = google_logging_billing_account_sink.sink[each.key].writer_identity
}
resource "google_bigquery_dataset_iam_member" "bq-sinks-binding" {
for_each = local.sink_bindings["bigquery"]
project = split("/", each.value.destination)[1]
dataset_id = split("/", each.value.destination)[3]
role = "roles/bigquery.dataEditor"
member = google_logging_billing_account_sink.sink[each.key].writer_identity
}
resource "google_pubsub_topic_iam_member" "pubsub-sinks-binding" {
for_each = local.sink_bindings["pubsub"]
project = split("/", each.value.destination)[1]
topic = split("/", each.value.destination)[3]
role = "roles/pubsub.publisher"
member = google_logging_billing_account_sink.sink[each.key].writer_identity
}
resource "google_project_iam_member" "bucket-sinks-binding" {
for_each = local.sink_bindings["logging"]
project = split("/", each.value.destination)[1]
role = "roles/logging.bucketWriter"
member = google_logging_billing_account_sink.sink[each.key].writer_identity
condition {
title = "${each.key} bucket writer"
description = "Grants bucketWriter to ${google_logging_billing_account_sink.sink[each.key].writer_identity} used by log sink ${each.key} on billing account ${var.id}"
expression = "resource.name.endsWith('${each.value.destination}')"
}
}

View File

@ -1,5 +1,5 @@
/**
* Copyright 2022 Google LLC
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -14,12 +14,8 @@
* limitations under the License.
*/
output "budget" {
description = "Budget resource."
value = google_billing_budget.budget
}
output "id" {
description = "Fully qualified budget id."
value = google_billing_budget.budget.id
resource "google_billing_project_info" "default" {
for_each = toset(var.projects)
billing_account = var.id
project = each.key
}

View File

@ -0,0 +1,31 @@
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
output "billing_budget_ids" {
description = "Billing budget ids."
value = {
for k, v in google_billing_budget.default :
k => v.id
}
}
output "monitoring_notification_channel_ids" {
description = "Monitoring notification channel ids."
value = {
for k, v in google_monitoring_notification_channel.default :
k => v.id
}
}

View File

@ -0,0 +1,208 @@
/**
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
variable "budget_notification_channels" {
description = "Notification channels used by budget alerts."
type = map(object({
project_id = string
type = string
description = optional(string)
display_name = optional(string)
enabled = optional(bool, true)
force_delete = optional(bool)
labels = optional(map(string))
sensitive_labels = optional(list(object({
auth_token = optional(string)
password = optional(string)
service_key = optional(string)
})))
user_labels = optional(map(string))
}))
nullable = false
default = {}
validation {
condition = alltrue([
for k, v in var.budget_notification_channels : contains([
"campfire", "email", "google_chat", "hipchat", "pagerduty",
"pubsub", "slack", "sms", "webhook_basicauth", "webhook_tokenauth"
], v.type)
])
error_message = "Invalid notification channel type."
}
}
variable "budgets" {
description = "Billing budgets. Notification channels are either keys in corresponding variable, or external ids."
type = map(object({
amount = object({
currency_code = optional(string)
nanos = optional(number)
units = optional(number)
use_last_period = optional(bool)
})
display_name = optional(string)
filter = optional(object({
credit_types_treatment = optional(object({
exclude_all = optional(bool)
include_specified = optional(list(string))
}))
label = optional(object({
key = string
value = string
}))
period = optional(object({
calendar = optional(string)
custom = optional(object({
start_date = object({
day = number
month = number
year = number
})
end_date = optional(object({
day = number
month = number
year = number
}))
}))
}))
projects = optional(list(string))
resource_ancestors = optional(list(string))
services = optional(list(string))
subaccounts = optional(list(string))
}))
threshold_rules = optional(list(object({
percent = number
forecasted_spend = optional(bool)
})), [])
update_rules = optional(map(object({
disable_default_iam_recipients = optional(bool)
monitoring_notification_channels = optional(list(string))
pubsub_topic = optional(string)
})), {})
}))
nullable = false
default = {}
validation {
condition = alltrue([
for k, v in var.budgets : v.amount != null && (
try(v.amount.use_last_period, null) == true ||
try(v.amount.units, null) != null
)
])
error_message = "Each budget needs to have amount units specified, or use last period."
}
validation {
condition = alltrue(flatten([
for k, v in var.budgets : [
for kk, vv in v.update_rules : [
vv.monitoring_notification_channels != null
||
vv.pubsub_topic != null
]
]
]))
error_message = "Budget notification rules need either a pubsub topic or monitoring channels defined."
}
}
variable "group_iam" {
description = "Authoritative IAM binding for organization groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable."
type = map(list(string))
default = {}
nullable = false
}
variable "iam" {
description = "IAM bindings in {ROLE => [MEMBERS]} format."
type = map(list(string))
default = {}
nullable = false
}
variable "iam_bindings" {
description = "Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary."
type = map(object({
members = list(string)
role = string
condition = optional(object({
expression = string
title = string
description = optional(string)
}))
}))
nullable = false
default = {}
}
variable "iam_bindings_additive" {
description = "Individual additive IAM bindings. Keys are arbitrary."
type = map(object({
member = string
role = string
condition = optional(object({
expression = string
title = string
description = optional(string)
}))
}))
nullable = false
default = {}
}
variable "id" {
description = "Billing account id."
type = string
}
variable "logging_sinks" {
description = "Logging sinks to create for the organization."
type = map(object({
destination = string
type = string
bq_partitioned_table = optional(bool)
description = optional(string)
disabled = optional(bool, false)
exclusions = optional(map(object({
filter = string
description = optional(string)
disabled = optional(bool)
})), {})
filter = optional(string)
}))
default = {}
nullable = false
validation {
condition = alltrue([
for k, v in var.logging_sinks :
contains(["bigquery", "logging", "pubsub", "storage"], v.type)
])
error_message = "Type must be one of 'bigquery', 'logging', 'pubsub', 'storage'."
}
validation {
condition = alltrue([
for k, v in var.logging_sinks :
v.bq_partitioned_table != true || v.type == "bigquery"
])
error_message = "Can only set bq_partitioned_table when type is `bigquery`."
}
}
variable "projects" {
description = "Projects associated with this billing account."
type = list(string)
nullable = false
default = []
}

View File

@ -25,5 +25,3 @@ terraform {
}
}
}

View File

@ -1,89 +0,0 @@
# Google Cloud Billing Budget Module
This module allows creating a Cloud Billing budget for a set of services and projects.
To create billing budgets you need one of the following IAM roles on the target billing account:
* Billing Account Administrator
* Billing Account Costs Manager
## Examples
### Simple email notification
Send a notification to an email when a set of projects reach $100 of spend.
```hcl
module "budget" {
source = "./fabric/modules/billing-budget"
billing_account = var.billing_account_id
name = "$100 budget"
amount = 100
thresholds = {
current = [0.5, 0.75, 1.0]
forecasted = [1.0]
}
projects = [
"projects/123456789000",
"projects/123456789111"
]
email_recipients = {
project_id = "my-project"
emails = ["user@example.com"]
}
}
# tftest modules=1 resources=2 inventory=email.yaml
```
### Pubsub notification
Send a notification to a PubSub topic the total spend of a billing account reaches the previous month's spend.
```hcl
module "budget" {
source = "./fabric/modules/billing-budget"
billing_account = var.billing_account_id
name = "previous period budget"
amount = 0
thresholds = {
current = [1.0]
forecasted = []
}
pubsub_topic = module.pubsub.id
}
module "pubsub" {
source = "./fabric/modules/pubsub"
project_id = var.project_id
name = "budget-topic"
}
# tftest modules=2 resources=2 inventory=pubsub.yaml
```
<!-- BEGIN TFDOC -->
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [billing_account](variables.tf#L23) | Billing account id. | <code>string</code> | ✓ | |
| [name](variables.tf#L50) | Budget name. | <code>string</code> | ✓ | |
| [thresholds](variables.tf#L85) | Thresholds percentages at which alerts are sent. Must be a value between 0 and 1. | <code title="object&#40;&#123;&#10; current &#61; list&#40;number&#41;&#10; forecasted &#61; list&#40;number&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | |
| [amount](variables.tf#L17) | Amount in the billing account's currency for the budget. Use 0 to set budget to 100% of last period's spend. | <code>number</code> | | <code>0</code> |
| [credit_treatment](variables.tf#L28) | How credits should be treated when determining spend for threshold calculations. Only INCLUDE_ALL_CREDITS or EXCLUDE_ALL_CREDITS are supported. | <code>string</code> | | <code>&#34;INCLUDE_ALL_CREDITS&#34;</code> |
| [email_recipients](variables.tf#L41) | Emails where budget notifications will be sent. Setting this will create a notification channel for each email in the specified project. | <code title="object&#40;&#123;&#10; project_id &#61; string&#10; emails &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [notification_channels](variables.tf#L55) | Monitoring notification channels where to send updates. | <code>list&#40;string&#41;</code> | | <code>null</code> |
| [notify_default_recipients](variables.tf#L61) | Notify Billing Account Administrators and Billing Account Users IAM roles for the target account. | <code>bool</code> | | <code>false</code> |
| [projects](variables.tf#L67) | List of projects of the form projects/{project_number}, specifying that usage from only this set of projects should be included in the budget. Set to null to include all projects linked to the billing account. | <code>list&#40;string&#41;</code> | | <code>null</code> |
| [pubsub_topic](variables.tf#L73) | The ID of the Cloud Pub/Sub topic where budget related messages will be published. | <code>string</code> | | <code>null</code> |
| [services](variables.tf#L79) | List of services of the form services/{service_id}, specifying that usage from only this set of services should be included in the budget. Set to null to include usage for all services. | <code>list&#40;string&#41;</code> | | <code>null</code> |
## Outputs
| name | description | sensitive |
|---|---|:---:|
| [budget](outputs.tf#L17) | Budget resource. | |
| [id](outputs.tf#L22) | Fully qualified budget id. | |
<!-- END TFDOC -->

View File

@ -1,95 +0,0 @@
/**
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
locals {
spend_basis = {
current = "CURRENT_SPEND"
forecasted = "FORECASTED_SPEND"
}
threshold_pairs = flatten([
for type, values in var.thresholds : [
for value in values : {
spend_basis = local.spend_basis[type]
threshold_percent = value
}
]
])
notification_channels = concat(
[for channel in google_monitoring_notification_channel.email_channels : channel.id],
coalesce(var.notification_channels, [])
)
}
resource "google_monitoring_notification_channel" "email_channels" {
for_each = toset(try(var.email_recipients.emails, []))
display_name = "${var.name} budget email notification (${each.value})"
type = "email"
project = var.email_recipients.project_id
labels = {
email_address = each.value
}
user_labels = {}
}
resource "google_billing_budget" "budget" {
billing_account = var.billing_account
display_name = var.name
budget_filter {
projects = var.projects
credit_types_treatment = var.credit_treatment
services = var.services
}
dynamic "amount" {
for_each = var.amount == 0 ? [1] : []
content {
last_period_amount = true
}
}
dynamic "amount" {
for_each = var.amount != 0 ? [1] : []
content {
dynamic "specified_amount" {
for_each = var.amount != 0 ? [1] : []
content {
units = var.amount
}
}
}
}
dynamic "threshold_rules" {
for_each = local.threshold_pairs
iterator = threshold
content {
threshold_percent = threshold.value.threshold_percent
spend_basis = threshold.value.spend_basis
}
}
all_updates_rule {
monitoring_notification_channels = local.notification_channels
pubsub_topic = var.pubsub_topic
# disable_default_iam_recipients can only be set if
# monitoring_notification_channels is nonempty
disable_default_iam_recipients = try(length(var.notification_channels), 0) > 0 && !var.notify_default_recipients
schema_version = "1.0"
}
}

View File

@ -1,95 +0,0 @@
/**
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
variable "amount" {
description = "Amount in the billing account's currency for the budget. Use 0 to set budget to 100% of last period's spend."
type = number
default = 0
}
variable "billing_account" {
description = "Billing account id."
type = string
}
variable "credit_treatment" {
description = "How credits should be treated when determining spend for threshold calculations. Only INCLUDE_ALL_CREDITS or EXCLUDE_ALL_CREDITS are supported."
type = string
default = "INCLUDE_ALL_CREDITS"
validation {
condition = (
var.credit_treatment == "INCLUDE_ALL_CREDITS" ||
var.credit_treatment == "EXCLUDE_ALL_CREDITS"
)
error_message = "Argument credit_treatment must be INCLUDE_ALL_CREDITS or EXCLUDE_ALL_CREDITS."
}
}
variable "email_recipients" {
description = "Emails where budget notifications will be sent. Setting this will create a notification channel for each email in the specified project."
type = object({
project_id = string
emails = list(string)
})
default = null
}
variable "name" {
description = "Budget name."
type = string
}
variable "notification_channels" {
description = "Monitoring notification channels where to send updates."
type = list(string)
default = null
}
variable "notify_default_recipients" {
description = "Notify Billing Account Administrators and Billing Account Users IAM roles for the target account."
type = bool
default = false
}
variable "projects" {
description = "List of projects of the form projects/{project_number}, specifying that usage from only this set of projects should be included in the budget. Set to null to include all projects linked to the billing account."
type = list(string)
default = null
}
variable "pubsub_topic" {
description = "The ID of the Cloud Pub/Sub topic where budget related messages will be published."
type = string
default = null
}
variable "services" {
description = "List of services of the form services/{service_id}, specifying that usage from only this set of services should be included in the budget. Set to null to include usage for all services."
type = list(string)
default = null
}
variable "thresholds" {
description = "Thresholds percentages at which alerts are sent. Must be a value between 0 and 1."
type = object({
current = list(number)
forecasted = list(number)
})
validation {
condition = length(var.thresholds.current) > 0 || length(var.thresholds.forecasted) > 0
error_message = "Must specify at least one budget threshold."
}
}

View File

@ -272,7 +272,7 @@ module "folder" {
| name | description | resources |
|---|---|---|
| [iam.tf](./iam.tf) | IAM bindings, roles and audit logging resources. | <code>google_folder_iam_binding</code> · <code>google_folder_iam_member</code> |
| [iam.tf](./iam.tf) | IAM bindings. | <code>google_folder_iam_binding</code> · <code>google_folder_iam_member</code> |
| [logging.tf](./logging.tf) | Log sinks and supporting resources. | <code>google_bigquery_dataset_iam_member</code> · <code>google_folder_iam_audit_config</code> · <code>google_logging_folder_exclusion</code> · <code>google_logging_folder_sink</code> · <code>google_project_iam_member</code> · <code>google_pubsub_topic_iam_member</code> · <code>google_storage_bucket_iam_member</code> |
| [main.tf](./main.tf) | Module-level locals and resources. | <code>google_compute_firewall_policy_association</code> · <code>google_essential_contacts_contact</code> · <code>google_folder</code> |
| [organization-policies.tf](./organization-policies.tf) | Folder-level organization policies. | <code>google_org_policy_policy</code> |
@ -295,7 +295,7 @@ module "folder" {
| [id](variables.tf#L83) | Folder ID in case you use folder_create=false. | <code>string</code> | | <code>null</code> |
| [logging_data_access](variables.tf#L89) | Control activation of data access logs. Format is service => { log type => [exempted members]}. The special 'allServices' key denotes configuration for all services. | <code>map&#40;map&#40;list&#40;string&#41;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_exclusions](variables.tf#L104) | Logging exclusions for this folder in the form {NAME -> FILTER}. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_sinks](variables.tf#L111) | Logging sinks to create for the organization. | <code title="map&#40;object&#40;&#123;&#10; bq_partitioned_table &#61; optional&#40;bool&#41;&#10; description &#61; optional&#40;string&#41;&#10; destination &#61; string&#10; disabled &#61; optional&#40;bool, false&#41;&#10; exclusions &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; filter &#61; string&#10; include_children &#61; optional&#40;bool, true&#41;&#10; type &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [logging_sinks](variables.tf#L111) | Logging sinks to create for the folder. | <code title="map&#40;object&#40;&#123;&#10; bq_partitioned_table &#61; optional&#40;bool&#41;&#10; description &#61; optional&#40;string&#41;&#10; destination &#61; string&#10; disabled &#61; optional&#40;bool, false&#41;&#10; exclusions &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; filter &#61; string&#10; include_children &#61; optional&#40;bool, true&#41;&#10; type &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [name](variables.tf#L141) | Folder name. | <code>string</code> | | <code>null</code> |
| [org_policies](variables.tf#L147) | Organization policies applied to this folder keyed by policy name. | <code title="map&#40;object&#40;&#123;&#10; inherit_from_parent &#61; optional&#40;bool&#41; &#35; for list policies only.&#10; reset &#61; optional&#40;bool&#41;&#10; rules &#61; optional&#40;list&#40;object&#40;&#123;&#10; allow &#61; optional&#40;object&#40;&#123;&#10; all &#61; optional&#40;bool&#41;&#10; values &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; deny &#61; optional&#40;object&#40;&#123;&#10; all &#61; optional&#40;bool&#41;&#10; values &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; enforce &#61; optional&#40;bool&#41; &#35; for boolean policies only.&#10; condition &#61; optional&#40;object&#40;&#123;&#10; description &#61; optional&#40;string&#41;&#10; expression &#61; optional&#40;string&#41;&#10; location &#61; optional&#40;string&#41;&#10; title &#61; optional&#40;string&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [org_policies_data_path](variables.tf#L174) | Path containing org policies in YAML format. | <code>string</code> | | <code>null</code> |

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
# tfdoc:file:description IAM bindings, roles and audit logging resources.
# tfdoc:file:description IAM bindings.
locals {
_group_iam_roles = distinct(flatten(values(var.group_iam)))

View File

@ -109,7 +109,7 @@ variable "logging_exclusions" {
}
variable "logging_sinks" {
description = "Logging sinks to create for the organization."
description = "Logging sinks to create for the folder."
type = map(object({
bq_partitioned_table = optional(bool)
description = optional(string)

View File

@ -18,7 +18,7 @@ counts:
google_logging_organization_sink: 2
google_organization_iam_binding: 20
google_organization_iam_custom_role: 3
google_organization_iam_member: 17
google_organization_iam_member: 13
google_project: 3
google_project_iam_binding: 9
google_project_iam_member: 3

View File

@ -0,0 +1,60 @@
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
values:
module.billing-account.google_billing_budget.default["folder-net-month-current-100"]:
all_updates_rule:
- disable_default_iam_recipients: true
pubsub_topic: null
schema_version: '1.0'
amount:
- last_period_amount: null
specified_amount:
- nanos: null
units: '100'
billing_account: 012345-ABCDEF-012345
budget_filter:
- calendar_period: null
credit_types_treatment: INCLUDE_ALL_CREDITS
custom_period: []
projects: null
resource_ancestors:
- folders/1234567890
display_name: 100 dollars in current spend
threshold_rules:
- spend_basis: CURRENT_SPEND
threshold_percent: 0.5
- spend_basis: CURRENT_SPEND
threshold_percent: 0.75
timeouts: null
module.billing-account.google_monitoring_notification_channel.default["billing-default"]:
description: null
display_name: Budget email notification billing-default.
enabled: true
force_delete: false
labels:
email_address: gcp-billing-admins@example.com
project: tf-playground-simple
sensitive_labels: []
timeouts: null
type: email
user_labels: null
counts:
google_billing_budget: 1
google_monitoring_notification_channel: 1
modules: 1
resources: 2
outputs: {}

View File

@ -0,0 +1,56 @@
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
values:
module.billing-account.google_billing_budget.default["folder-net-month-current-100"]:
all_updates_rule:
- disable_default_iam_recipients: false
monitoring_notification_channels: null
pubsub_topic: projects/my-prj/topics/budget-default
schema_version: '1.0'
amount:
- last_period_amount: null
specified_amount:
- nanos: null
units: '100'
billing_account: 012345-ABCDEF-012345
budget_filter:
- calendar_period: null
credit_types_treatment: INCLUDE_ALL_CREDITS
custom_period: []
projects: null
resource_ancestors:
- folders/1234567890
display_name: 100 dollars in current spend
threshold_rules:
- spend_basis: CURRENT_SPEND
threshold_percent: 0.5
- spend_basis: CURRENT_SPEND
threshold_percent: 0.75
timeouts: null
module.pubsub-billing-topic.google_pubsub_topic.default:
kms_key_name: null
labels: null
message_retention_duration: null
name: budget-default
project: my-prj
timeouts: null
counts:
google_billing_budget: 1
google_pubsub_topic: 1
modules: 2
resources: 2
outputs: {}

View File

@ -0,0 +1,44 @@
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
values:
module.billing-account.google_billing_budget.default["folder-net-month-current-100"]:
all_updates_rule: []
amount:
- last_period_amount: null
specified_amount:
- nanos: null
units: '100'
billing_account: 012345-ABCDEF-012345
budget_filter:
- calendar_period: null
credit_types_treatment: INCLUDE_ALL_CREDITS
custom_period: []
projects: null
resource_ancestors:
- folders/1234567890
display_name: 100 dollars in current spend
threshold_rules:
- spend_basis: CURRENT_SPEND
threshold_percent: 0.5
- spend_basis: CURRENT_SPEND
threshold_percent: 0.75
timeouts: null
counts:
google_billing_budget: 1
modules: 1
resources: 1
outputs: {}

View File

@ -0,0 +1,45 @@
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
values:
module.billing-account.google_billing_account_iam_binding.authoritative["roles/billing.admin"]:
billing_account_id: 012345-ABCDEF-012345
condition: []
members:
- group:billing-admins@example.org
- serviceAccount:foo@myprj.iam.gserviceaccount.com
role: roles/billing.admin
module.billing-account.google_billing_account_iam_binding.bindings["conditional-admin"]:
billing_account_id: 012345-ABCDEF-012345
condition:
- description: null
expression: resource.matchTag('123456/environment', 'development')
title: pf-dev-conditional-billing-admin
members:
- serviceAccount:pf-dev@myprj.iam.gserviceaccount.com
role: roles/billing.admin
module.billing-account.google_billing_account_iam_member.bindings["sa-net-iac-user"]:
billing_account_id: 012345-ABCDEF-012345
condition: []
member: serviceAccount:net-iac-0@myprj.iam.gserviceaccount.com
role: roles/billing.user
counts:
google_billing_account_iam_binding: 2
google_billing_account_iam_member: 1
modules: 1
resources: 3
outputs: {}

View File

@ -0,0 +1,43 @@
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
values:
module.billing-account.google_logging_billing_account_sink.sink["all"]:
billing_account: 012345-ABCDEF-012345
description: all (Terraform-managed).
disabled: false
exclusions: []
filter: null
name: all
module.billing-account.google_project_iam_member.bucket-sinks-binding["all"]:
condition:
- title: all bucket writer
role: roles/logging.bucketWriter
module.log-bucket-all.google_logging_project_bucket_config.bucket[0]:
bucket_id: billing-account-all
cmek_settings: []
enable_analytics: false
location: global
locked: null
project: myprj
retention_days: 30
counts:
google_logging_billing_account_sink: 1
google_logging_project_bucket_config: 1
google_project_iam_member: 1
modules: 2
resources: 3
outputs: {}