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:
parent
3949fdc283
commit
252127bde5
|
@ -29,7 +29,7 @@ The current list of modules supports most of the core foundational and networkin
|
||||||
|
|
||||||
Currently available modules:
|
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)
|
- **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)
|
- **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)
|
- **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)
|
||||||
|
|
|
@ -74,12 +74,3 @@ resource "google_billing_account_iam_member" "billing_ext_admin" {
|
||||||
role = "roles/billing.admin"
|
role = "roles/billing.admin"
|
||||||
member = each.key
|
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
|
|
||||||
}
|
|
||||||
|
|
|
@ -34,8 +34,7 @@ locals {
|
||||||
authoritative = []
|
authoritative = []
|
||||||
additive = (
|
additive = (
|
||||||
local.billing_mode != "org" ? [] : [
|
local.billing_mode != "org" ? [] : [
|
||||||
"roles/billing.admin",
|
"roles/billing.admin"
|
||||||
"roles/billing.costsManager"
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -66,8 +65,7 @@ locals {
|
||||||
"roles/orgpolicy.policyAdmin"
|
"roles/orgpolicy.policyAdmin"
|
||||||
],
|
],
|
||||||
local.billing_mode != "org" ? [] : [
|
local.billing_mode != "org" ? [] : [
|
||||||
"roles/billing.admin",
|
"roles/billing.admin"
|
||||||
"roles/billing.costsManager"
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -111,8 +109,7 @@ locals {
|
||||||
"roles/orgpolicy.policyAdmin"
|
"roles/orgpolicy.policyAdmin"
|
||||||
],
|
],
|
||||||
local.billing_mode != "org" ? [] : [
|
local.billing_mode != "org" ? [] : [
|
||||||
"roles/billing.admin",
|
"roles/billing.admin"
|
||||||
"roles/billing.costsManager"
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -129,8 +126,7 @@ locals {
|
||||||
"roles/orgpolicy.policyAdmin"
|
"roles/orgpolicy.policyAdmin"
|
||||||
],
|
],
|
||||||
local.billing_mode != "org" ? [] : [
|
local.billing_mode != "org" ? [] : [
|
||||||
"roles/billing.admin",
|
"roles/billing.admin"
|
||||||
"roles/billing.costsManager"
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -148,8 +144,7 @@ locals {
|
||||||
# TODO: align additive roles with the README
|
# TODO: align additive roles with the README
|
||||||
additive = (
|
additive = (
|
||||||
local.billing_mode != "org" ? [] : [
|
local.billing_mode != "org" ? [] : [
|
||||||
"roles/billing.admin",
|
"roles/billing.admin"
|
||||||
"roles/billing.costsManager"
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,14 +30,14 @@ These modules are used in the examples included in this repository. If you are u
|
||||||
|
|
||||||
## Foundational modules
|
## Foundational modules
|
||||||
|
|
||||||
- [billing budget](./billing-budget)
|
- [Billing account](./billing-account)
|
||||||
- [Cloud Identity group](./cloud-identity-group/)
|
- [Cloud Identity group](./cloud-identity-group/)
|
||||||
- [folder](./folder)
|
- [Folder](./folder)
|
||||||
- [service accounts](./iam-service-account)
|
- [Service accounts](./iam-service-account)
|
||||||
- [logging bucket](./logging-bucket)
|
- [Logging bucket](./logging-bucket)
|
||||||
- [organization](./organization)
|
- [Organization](./organization)
|
||||||
- [project](./project)
|
- [Project](./project)
|
||||||
- [projects-data-source](./projects-data-source)
|
- [Projects (data source)](./projects-data-source)
|
||||||
|
|
||||||
## Networking modules
|
## Networking modules
|
||||||
|
|
||||||
|
|
|
@ -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(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)) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||||
|
| [budgets](variables.tf#L47) | Billing budgets. Notification channels are either keys in corresponding variable, or external ids. | <code title="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) })), {}) }))">map(object({…}))</code> | | <code>{}</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(list(string))</code> | | <code>{}</code> |
|
||||||
|
| [iam](variables.tf#L128) | IAM bindings in {ROLE => [MEMBERS]} format. | <code>map(list(string))</code> | | <code>{}</code> |
|
||||||
|
| [iam_bindings](variables.tf#L135) | Authoritative IAM bindings in {KEY => {role = ROLE, members = [], condition = {}}}. Keys are arbitrary. | <code title="map(object({ members = list(string) role = string condition = optional(object({ expression = string title = string description = optional(string) })) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||||
|
| [iam_bindings_additive](variables.tf#L150) | Individual additive IAM bindings. Keys are arbitrary. | <code title="map(object({ member = string role = string condition = optional(object({ expression = string title = string description = optional(string) })) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||||
|
| [logging_sinks](variables.tf#L170) | Logging sinks to create for the organization. | <code title="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) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||||
|
| [projects](variables.tf#L203) | Projects associated with this billing account. | <code>list(string)</code> | | <code>[]</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 -->
|
|
@ -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
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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}')"
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
* Copyright 2022 Google LLC
|
* Copyright 2023 Google LLC
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -14,12 +14,8 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
output "budget" {
|
resource "google_billing_project_info" "default" {
|
||||||
description = "Budget resource."
|
for_each = toset(var.projects)
|
||||||
value = google_billing_budget.budget
|
billing_account = var.id
|
||||||
}
|
project = each.key
|
||||||
|
|
||||||
output "id" {
|
|
||||||
description = "Fully qualified budget id."
|
|
||||||
value = google_billing_budget.budget.id
|
|
||||||
}
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 = []
|
||||||
|
}
|
|
@ -25,5 +25,3 @@ terraform {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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({ current = list(number) forecasted = list(number) })">object({…})</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>"INCLUDE_ALL_CREDITS"</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({ project_id = string emails = list(string) })">object({…})</code> | | <code>null</code> |
|
|
||||||
| [notification_channels](variables.tf#L55) | Monitoring notification channels where to send updates. | <code>list(string)</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(string)</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(string)</code> | | <code>null</code> |
|
|
||||||
|
|
||||||
## Outputs
|
|
||||||
|
|
||||||
| name | description | sensitive |
|
|
||||||
|---|---|:---:|
|
|
||||||
| [budget](outputs.tf#L17) | Budget resource. | |
|
|
||||||
| [id](outputs.tf#L22) | Fully qualified budget id. | |
|
|
||||||
|
|
||||||
<!-- END TFDOC -->
|
|
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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."
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -272,7 +272,7 @@ module "folder" {
|
||||||
|
|
||||||
| name | description | resources |
|
| 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> |
|
| [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> |
|
| [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> |
|
| [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> |
|
| [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(map(list(string)))</code> | | <code>{}</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(map(list(string)))</code> | | <code>{}</code> |
|
||||||
| [logging_exclusions](variables.tf#L104) | Logging exclusions for this folder in the form {NAME -> FILTER}. | <code>map(string)</code> | | <code>{}</code> |
|
| [logging_exclusions](variables.tf#L104) | Logging exclusions for this folder in the form {NAME -> FILTER}. | <code>map(string)</code> | | <code>{}</code> |
|
||||||
| [logging_sinks](variables.tf#L111) | Logging sinks to create for the organization. | <code title="map(object({ bq_partitioned_table = optional(bool) description = optional(string) destination = string disabled = optional(bool, false) exclusions = optional(map(string), {}) filter = string include_children = optional(bool, true) type = string }))">map(object({…}))</code> | | <code>{}</code> |
|
| [logging_sinks](variables.tf#L111) | Logging sinks to create for the folder. | <code title="map(object({ bq_partitioned_table = optional(bool) description = optional(string) destination = string disabled = optional(bool, false) exclusions = optional(map(string), {}) filter = string include_children = optional(bool, true) type = string }))">map(object({…}))</code> | | <code>{}</code> |
|
||||||
| [name](variables.tf#L141) | Folder name. | <code>string</code> | | <code>null</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(object({ inherit_from_parent = optional(bool) # for list policies only. reset = optional(bool) rules = optional(list(object({ allow = optional(object({ all = optional(bool) values = optional(list(string)) })) deny = optional(object({ all = optional(bool) values = optional(list(string)) })) enforce = optional(bool) # for boolean policies only. condition = optional(object({ description = optional(string) expression = optional(string) location = optional(string) title = optional(string) }), {}) })), []) }))">map(object({…}))</code> | | <code>{}</code> |
|
| [org_policies](variables.tf#L147) | Organization policies applied to this folder keyed by policy name. | <code title="map(object({ inherit_from_parent = optional(bool) # for list policies only. reset = optional(bool) rules = optional(list(object({ allow = optional(object({ all = optional(bool) values = optional(list(string)) })) deny = optional(object({ all = optional(bool) values = optional(list(string)) })) enforce = optional(bool) # for boolean policies only. condition = optional(object({ description = optional(string) expression = optional(string) location = optional(string) title = optional(string) }), {}) })), []) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||||
| [org_policies_data_path](variables.tf#L174) | Path containing org policies in YAML format. | <code>string</code> | | <code>null</code> |
|
| [org_policies_data_path](variables.tf#L174) | Path containing org policies in YAML format. | <code>string</code> | | <code>null</code> |
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
# tfdoc:file:description IAM bindings, roles and audit logging resources.
|
# tfdoc:file:description IAM bindings.
|
||||||
|
|
||||||
locals {
|
locals {
|
||||||
_group_iam_roles = distinct(flatten(values(var.group_iam)))
|
_group_iam_roles = distinct(flatten(values(var.group_iam)))
|
||||||
|
|
|
@ -109,7 +109,7 @@ variable "logging_exclusions" {
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "logging_sinks" {
|
variable "logging_sinks" {
|
||||||
description = "Logging sinks to create for the organization."
|
description = "Logging sinks to create for the folder."
|
||||||
type = map(object({
|
type = map(object({
|
||||||
bq_partitioned_table = optional(bool)
|
bq_partitioned_table = optional(bool)
|
||||||
description = optional(string)
|
description = optional(string)
|
||||||
|
|
|
@ -18,7 +18,7 @@ counts:
|
||||||
google_logging_organization_sink: 2
|
google_logging_organization_sink: 2
|
||||||
google_organization_iam_binding: 20
|
google_organization_iam_binding: 20
|
||||||
google_organization_iam_custom_role: 3
|
google_organization_iam_custom_role: 3
|
||||||
google_organization_iam_member: 17
|
google_organization_iam_member: 13
|
||||||
google_project: 3
|
google_project: 3
|
||||||
google_project_iam_binding: 9
|
google_project_iam_binding: 9
|
||||||
google_project_iam_member: 3
|
google_project_iam_member: 3
|
||||||
|
|
|
@ -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: {}
|
|
@ -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: {}
|
|
@ -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: {}
|
|
@ -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: {}
|
||||||
|
|
|
@ -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: {}
|
Loading…
Reference in New Issue